]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge branch 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 13 Jun 2009 01:18:05 +0000 (18:18 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 13 Jun 2009 01:18:05 +0000 (18:18 -0700)
* 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6: (30 commits)
  [S390] wire up sys_perf_counter_open
  [S390] wire up sys_rt_tgsigqueueinfo
  [S390] ftrace: add system call tracer support
  [S390] ftrace: add function graph tracer support
  [S390] ftrace: add function trace mcount test support
  [S390] ftrace: add dynamic ftrace support
  [S390] kprobes: use probe_kernel_write
  [S390] maccess: arch specific probe_kernel_write() implementation
  [S390] maccess: add weak attribute to probe_kernel_write
  [S390] profile_tick called twice
  [S390] dasd: forward internal errors to dasd_sleep_on caller
  [S390] dasd: sync after async probe
  [S390] dasd: check_characteristics cleanup
  [S390] dasd: no High Performance FICON in 31-bit mode
  [S390] dcssblk: revert devt conversion
  [S390] qdio: fix access beyond ARRAY_SIZE of irq_ptr->{in,out}put_qs
  [S390] vmalloc: add vmalloc kernel parameter support
  [S390] uaccess: use might_fault() instead of might_sleep()
  [S390] 3270: lock dependency fixes
  [S390] 3270: do not register with tty_register_device
  ...

956 files changed:
Documentation/ide/ide.txt
Documentation/kernel-parameters.txt
Documentation/lguest/Makefile
Documentation/lguest/lguest.c
Documentation/lguest/lguest.txt
Documentation/power/devices.txt
Documentation/sound/alsa/ALSA-Configuration.txt
Documentation/sound/alsa/HD-Audio-Models.txt
Documentation/sound/alsa/Procfile.txt
Documentation/sound/alsa/README.maya44 [new file with mode: 0644]
Documentation/sound/alsa/soc/dapm.txt
MAINTAINERS
arch/alpha/include/asm/atomic.h
arch/alpha/include/asm/bitsperlong.h [new file with mode: 0644]
arch/alpha/include/asm/page.h
arch/alpha/include/asm/signal.h
arch/alpha/include/asm/suspend.h [deleted file]
arch/alpha/include/asm/types.h
arch/alpha/mm/extable.c
arch/arm/include/asm/atomic.h
arch/arm/include/asm/bitsperlong.h [new file with mode: 0644]
arch/arm/include/asm/mman.h
arch/arm/include/asm/page.h
arch/arm/include/asm/signal.h
arch/arm/include/asm/suspend.h [deleted file]
arch/avr32/include/asm/atomic.h
arch/avr32/include/asm/bitsperlong.h [new file with mode: 0644]
arch/avr32/include/asm/mman.h
arch/avr32/include/asm/signal.h
arch/avr32/include/asm/termios.h
arch/avr32/kernel/module.c
arch/blackfin/Kconfig
arch/blackfin/Kconfig.debug
arch/blackfin/configs/BF518F-EZBRD_defconfig
arch/blackfin/configs/BF526-EZBRD_defconfig
arch/blackfin/configs/BF527-EZKIT_defconfig
arch/blackfin/configs/BF533-EZKIT_defconfig
arch/blackfin/configs/BF533-STAMP_defconfig
arch/blackfin/configs/BF537-STAMP_defconfig
arch/blackfin/configs/BF538-EZKIT_defconfig
arch/blackfin/configs/BF548-EZKIT_defconfig
arch/blackfin/configs/BF561-EZKIT_defconfig
arch/blackfin/configs/BlackStamp_defconfig
arch/blackfin/configs/CM-BF527_defconfig
arch/blackfin/configs/CM-BF533_defconfig
arch/blackfin/configs/CM-BF537E_defconfig
arch/blackfin/configs/CM-BF537U_defconfig
arch/blackfin/configs/CM-BF548_defconfig
arch/blackfin/configs/CM-BF561_defconfig
arch/blackfin/configs/H8606_defconfig
arch/blackfin/configs/IP0X_defconfig
arch/blackfin/configs/PNAV-10_defconfig
arch/blackfin/configs/SRV1_defconfig
arch/blackfin/configs/TCM-BF537_defconfig
arch/blackfin/include/asm/atomic.h
arch/blackfin/include/asm/bitsperlong.h [new file with mode: 0644]
arch/blackfin/include/asm/cacheflush.h
arch/blackfin/include/asm/cplb.h
arch/blackfin/include/asm/dma.h
arch/blackfin/include/asm/elf.h
arch/blackfin/include/asm/entry.h
arch/blackfin/include/asm/gptimers.h
arch/blackfin/include/asm/io.h
arch/blackfin/include/asm/ipipe.h
arch/blackfin/include/asm/page.h
arch/blackfin/include/asm/pda.h
arch/blackfin/include/asm/processor.h
arch/blackfin/include/asm/signal.h
arch/blackfin/include/asm/time.h
arch/blackfin/include/asm/uaccess.h
arch/blackfin/kernel/bfin_dma_5xx.c
arch/blackfin/kernel/bfin_gpio.c
arch/blackfin/kernel/bfin_ksyms.c
arch/blackfin/kernel/cplb-mpu/cacheinit.c
arch/blackfin/kernel/cplb-mpu/cplbinit.c
arch/blackfin/kernel/cplb-nompu/cacheinit.c
arch/blackfin/kernel/early_printk.c
arch/blackfin/kernel/gptimers.c
arch/blackfin/kernel/ipipe.c
arch/blackfin/kernel/irqchip.c
arch/blackfin/kernel/kgdb.c
arch/blackfin/kernel/module.c
arch/blackfin/kernel/process.c
arch/blackfin/kernel/setup.c
arch/blackfin/kernel/sys_bfin.c
arch/blackfin/kernel/time-ts.c
arch/blackfin/kernel/time.c
arch/blackfin/kernel/traps.c
arch/blackfin/kernel/vmlinux.lds.S
arch/blackfin/mach-bf518/Kconfig
arch/blackfin/mach-bf518/boards/ezbrd.c
arch/blackfin/mach-bf518/include/mach/anomaly.h
arch/blackfin/mach-bf518/include/mach/portmux.h
arch/blackfin/mach-bf527/Kconfig
arch/blackfin/mach-bf527/boards/cm_bf527.c
arch/blackfin/mach-bf527/boards/ezbrd.c
arch/blackfin/mach-bf527/boards/ezkit.c
arch/blackfin/mach-bf527/include/mach/anomaly.h
arch/blackfin/mach-bf533/Kconfig
arch/blackfin/mach-bf533/boards/H8606.c
arch/blackfin/mach-bf533/boards/cm_bf533.c
arch/blackfin/mach-bf533/boards/ezkit.c
arch/blackfin/mach-bf533/boards/stamp.c
arch/blackfin/mach-bf533/include/mach/anomaly.h
arch/blackfin/mach-bf537/Kconfig
arch/blackfin/mach-bf537/boards/cm_bf537.c
arch/blackfin/mach-bf537/boards/pnav10.c
arch/blackfin/mach-bf537/boards/stamp.c
arch/blackfin/mach-bf537/boards/tcm_bf537.c
arch/blackfin/mach-bf537/include/mach/anomaly.h
arch/blackfin/mach-bf538/Kconfig
arch/blackfin/mach-bf538/include/mach/anomaly.h
arch/blackfin/mach-bf538/include/mach/blackfin.h
arch/blackfin/mach-bf538/include/mach/cdefBF538.h
arch/blackfin/mach-bf538/include/mach/defBF539.h
arch/blackfin/mach-bf548/Kconfig
arch/blackfin/mach-bf548/boards/ezkit.c
arch/blackfin/mach-bf548/include/mach/anomaly.h
arch/blackfin/mach-bf548/include/mach/portmux.h
arch/blackfin/mach-bf561/Kconfig
arch/blackfin/mach-bf561/boards/cm_bf561.c
arch/blackfin/mach-bf561/coreb.c
arch/blackfin/mach-bf561/include/mach/anomaly.h
arch/blackfin/mach-bf561/include/mach/cdefBF561.h
arch/blackfin/mach-bf561/include/mach/defBF561.h
arch/blackfin/mach-bf561/smp.c
arch/blackfin/mach-common/arch_checks.c
arch/blackfin/mach-common/cache.S
arch/blackfin/mach-common/clocks-init.c
arch/blackfin/mach-common/cpufreq.c
arch/blackfin/mach-common/entry.S
arch/blackfin/mach-common/head.S
arch/blackfin/mach-common/interrupt.S
arch/blackfin/mach-common/ints-priority.c
arch/blackfin/mach-common/smp.c
arch/blackfin/mm/blackfin_sram.h
arch/blackfin/mm/init.c
arch/blackfin/mm/isram-driver.c
arch/blackfin/mm/sram-alloc.c
arch/cris/include/asm/atomic.h
arch/cris/include/asm/bitsperlong.h [new file with mode: 0644]
arch/cris/include/asm/mman.h
arch/cris/include/asm/page.h
arch/cris/include/asm/signal.h
arch/cris/kernel/module.c
arch/frv/include/asm/atomic.h
arch/frv/include/asm/bitsperlong.h [new file with mode: 0644]
arch/frv/include/asm/mman.h
arch/frv/include/asm/page.h
arch/frv/include/asm/pci.h
arch/frv/include/asm/termios.h
arch/frv/kernel/module.c
arch/h8300/include/asm/atomic.h
arch/h8300/include/asm/bitsperlong.h [new file with mode: 0644]
arch/h8300/include/asm/mman.h
arch/h8300/include/asm/page.h
arch/h8300/include/asm/signal.h
arch/h8300/kernel/module.c
arch/ia64/include/asm/atomic.h
arch/ia64/include/asm/bitsperlong.h [new file with mode: 0644]
arch/ia64/include/asm/mman.h
arch/ia64/include/asm/signal.h
arch/ia64/include/asm/suspend.h [deleted file]
arch/ia64/include/asm/types.h
arch/ia64/mm/extable.c
arch/m32r/include/asm/atomic.h
arch/m32r/include/asm/bitsperlong.h [new file with mode: 0644]
arch/m32r/include/asm/mman.h
arch/m32r/include/asm/page.h
arch/m32r/include/asm/pci.h
arch/m32r/include/asm/signal.h
arch/m32r/kernel/module.c
arch/m68k/include/asm/atomic_mm.h
arch/m68k/include/asm/atomic_no.h
arch/m68k/include/asm/bitsperlong.h [new file with mode: 0644]
arch/m68k/include/asm/mman.h
arch/m68k/include/asm/page_mm.h
arch/m68k/include/asm/page_no.h
arch/m68k/include/asm/signal.h
arch/m68k/include/asm/suspend.h [deleted file]
arch/m68k/kernel/module.c
arch/m68knommu/kernel/module.c
arch/microblaze/Kconfig
arch/microblaze/Makefile
arch/microblaze/boot/Makefile
arch/microblaze/configs/mmu_defconfig [new file with mode: 0644]
arch/microblaze/include/asm/Kbuild
arch/microblaze/include/asm/atomic.h
arch/microblaze/include/asm/bitsperlong.h [new file with mode: 0644]
arch/microblaze/include/asm/cacheflush.h
arch/microblaze/include/asm/checksum.h
arch/microblaze/include/asm/current.h
arch/microblaze/include/asm/dma-mapping.h
arch/microblaze/include/asm/dma.h
arch/microblaze/include/asm/elf.h
arch/microblaze/include/asm/entry.h
arch/microblaze/include/asm/exceptions.h
arch/microblaze/include/asm/flat.h
arch/microblaze/include/asm/gpio.h
arch/microblaze/include/asm/io.h
arch/microblaze/include/asm/mmu.h
arch/microblaze/include/asm/mmu_context.h
arch/microblaze/include/asm/mmu_context_mm.h [new file with mode: 0644]
arch/microblaze/include/asm/mmu_context_no.h [new file with mode: 0644]
arch/microblaze/include/asm/page.h
arch/microblaze/include/asm/pgalloc.h
arch/microblaze/include/asm/pgtable.h
arch/microblaze/include/asm/posix_types.h
arch/microblaze/include/asm/processor.h
arch/microblaze/include/asm/ptrace.h
arch/microblaze/include/asm/registers.h
arch/microblaze/include/asm/sections.h
arch/microblaze/include/asm/segment.h
arch/microblaze/include/asm/setup.h
arch/microblaze/include/asm/signal.h
arch/microblaze/include/asm/stat.h
arch/microblaze/include/asm/string.h
arch/microblaze/include/asm/syscalls.h
arch/microblaze/include/asm/termios.h
arch/microblaze/include/asm/thread_info.h
arch/microblaze/include/asm/tlb.h
arch/microblaze/include/asm/tlbflush.h
arch/microblaze/include/asm/uaccess.h
arch/microblaze/include/asm/unaligned.h
arch/microblaze/kernel/Makefile
arch/microblaze/kernel/asm-offsets.c
arch/microblaze/kernel/early_printk.c
arch/microblaze/kernel/entry-nommu.S
arch/microblaze/kernel/entry.S [new file with mode: 0644]
arch/microblaze/kernel/exceptions.c
arch/microblaze/kernel/head.S
arch/microblaze/kernel/hw_exception_handler.S
arch/microblaze/kernel/microblaze_ksyms.c
arch/microblaze/kernel/misc.S [new file with mode: 0644]
arch/microblaze/kernel/process.c
arch/microblaze/kernel/prom.c
arch/microblaze/kernel/setup.c
arch/microblaze/kernel/signal.c
arch/microblaze/kernel/syscall_table.S
arch/microblaze/kernel/traps.c
arch/microblaze/kernel/vmlinux.lds.S
arch/microblaze/lib/Makefile
arch/microblaze/lib/checksum.c
arch/microblaze/lib/memcpy.c
arch/microblaze/lib/uaccess_old.S [new file with mode: 0644]
arch/microblaze/mm/Makefile
arch/microblaze/mm/fault.c [new file with mode: 0644]
arch/microblaze/mm/init.c
arch/microblaze/mm/mmu_context.c [new file with mode: 0644]
arch/microblaze/mm/pgtable.c [new file with mode: 0644]
arch/mips/include/asm/atomic.h
arch/mips/include/asm/bitsperlong.h [new file with mode: 0644]
arch/mips/include/asm/page.h
arch/mips/include/asm/signal.h
arch/mips/include/asm/suspend.h [deleted file]
arch/mips/include/asm/types.h
arch/mips/kernel/module.c
arch/mn10300/include/asm/atomic.h
arch/mn10300/include/asm/bitsperlong.h [new file with mode: 0644]
arch/mn10300/include/asm/mman.h
arch/mn10300/include/asm/signal.h
arch/mn10300/kernel/module.c
arch/parisc/include/asm/atomic.h
arch/parisc/include/asm/bitsperlong.h [new file with mode: 0644]
arch/parisc/include/asm/page.h
arch/parisc/include/asm/types.h
arch/parisc/include/asm/uaccess.h
arch/parisc/kernel/module.c
arch/powerpc/include/asm/atomic.h
arch/powerpc/include/asm/bitsperlong.h [new file with mode: 0644]
arch/powerpc/include/asm/mman.h
arch/powerpc/include/asm/mpc52xx_psc.h
arch/powerpc/include/asm/page_32.h
arch/powerpc/include/asm/page_64.h
arch/powerpc/include/asm/signal.h
arch/powerpc/include/asm/termios.h
arch/powerpc/include/asm/types.h
arch/powerpc/kernel/module.c
arch/powerpc/kernel/power7-pmu.c
arch/s390/include/asm/atomic.h
arch/s390/include/asm/bitsperlong.h [new file with mode: 0644]
arch/s390/include/asm/mman.h
arch/s390/include/asm/page.h
arch/s390/include/asm/signal.h
arch/s390/include/asm/suspend.h [deleted file]
arch/s390/include/asm/termios.h
arch/s390/include/asm/types.h
arch/s390/kernel/module.c
arch/sh/include/asm/atomic.h
arch/sh/include/asm/bitsperlong.h [new file with mode: 0644]
arch/sh/include/asm/mman.h
arch/sh/include/asm/page.h
arch/sh/include/asm/signal.h
arch/sh/kernel/module.c
arch/sparc/include/asm/atomic_32.h
arch/sparc/include/asm/atomic_64.h
arch/sparc/include/asm/bitsperlong.h [new file with mode: 0644]
arch/sparc/include/asm/mman.h
arch/sparc/include/asm/page_32.h
arch/sparc/include/asm/page_64.h
arch/sparc/include/asm/signal.h
arch/sparc/include/asm/types.h
arch/sparc/include/asm/uaccess_32.h
arch/sparc/include/asm/uaccess_64.h
arch/sparc/kernel/module.c
arch/sparc/mm/extable.c
arch/um/include/asm/page.h
arch/um/include/asm/pgtable.h
arch/um/include/asm/suspend.h [deleted file]
arch/um/sys-i386/Makefile
arch/um/sys-x86_64/Makefile
arch/um/sys-x86_64/um_module.c [deleted file]
arch/x86/include/asm/atomic_32.h
arch/x86/include/asm/atomic_64.h
arch/x86/include/asm/bitsperlong.h [new file with mode: 0644]
arch/x86/include/asm/lguest.h
arch/x86/include/asm/lguest_hcall.h
arch/x86/include/asm/mman.h
arch/x86/include/asm/page.h
arch/x86/include/asm/pgtable_32_types.h
arch/x86/include/asm/signal.h
arch/x86/include/asm/tlbflush.h
arch/x86/include/asm/types.h
arch/x86/kernel/Makefile
arch/x86/kernel/acpi/sleep.c
arch/x86/kernel/apm_32.c
arch/x86/kernel/asm-offsets_32.c
arch/x86/kernel/cpu/perf_counter.c
arch/x86/kernel/module.c [moved from arch/x86/kernel/module_64.c with 74% similarity]
arch/x86/kernel/module_32.c [deleted file]
arch/x86/kernel/setup.c
arch/x86/kernel/smpboot.c
arch/x86/kernel/vmlinux.lds.S
arch/x86/lguest/Kconfig
arch/x86/lguest/boot.c
arch/x86/lguest/i386_head.S
arch/x86/mm/init_32.c
arch/x86/power/Makefile
arch/x86/power/cpu.c [moved from arch/x86/power/cpu_64.c with 62% similarity]
arch/x86/power/cpu_32.c [deleted file]
arch/xtensa/include/asm/atomic.h
arch/xtensa/include/asm/bitsperlong.h [new file with mode: 0644]
arch/xtensa/include/asm/page.h
arch/xtensa/kernel/module.c
drivers/base/firmware_class.c
drivers/base/platform.c
drivers/base/power/main.c
drivers/base/sys.c
drivers/block/Kconfig
drivers/block/virtio_blk.c
drivers/char/Kconfig
drivers/char/agp/intel-agp.c
drivers/char/hw_random/virtio-rng.c
drivers/char/virtio_console.c
drivers/char/vt.c
drivers/gpio/Kconfig
drivers/gpu/drm/drm_bufs.c
drivers/gpu/drm/drm_edid.c
drivers/gpu/drm/drm_gem.c
drivers/gpu/drm/drm_hashtab.c
drivers/gpu/drm/drm_mm.c
drivers/gpu/drm/drm_modes.c
drivers/gpu/drm/drm_stub.c
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem.c
drivers/gpu/drm/i915/i915_gem_tiling.c
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/i915_suspend.c
drivers/gpu/drm/i915/intel_bios.c
drivers/gpu/drm/i915/intel_bios.h
drivers/gpu/drm/i915/intel_crt.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_fb.c
drivers/gpu/drm/i915/intel_hdmi.c
drivers/gpu/drm/i915/intel_lvds.c
drivers/gpu/drm/i915/intel_sdvo.c
drivers/gpu/drm/i915/intel_tv.c
drivers/gpu/drm/radeon/r600_cp.c
drivers/gpu/drm/radeon/radeon_cp.c
drivers/gpu/drm/radeon/radeon_drv.h
drivers/gpu/drm/via/via_dmablit.c
drivers/ide/at91_ide.c
drivers/ide/au1xxx-ide.c
drivers/ide/buddha.c
drivers/ide/cmd640.c
drivers/ide/cs5520.c
drivers/ide/delkin_cb.c
drivers/ide/falconide.c
drivers/ide/gayle.c
drivers/ide/hpt366.c
drivers/ide/icside.c
drivers/ide/ide-4drives.c
drivers/ide/ide-atapi.c
drivers/ide/ide-cs.c
drivers/ide/ide-disk.c
drivers/ide/ide-dma.c
drivers/ide/ide-eh.c
drivers/ide/ide-gd.c
drivers/ide/ide-generic.c
drivers/ide/ide-h8300.c
drivers/ide/ide-io.c
drivers/ide/ide-iops.c
drivers/ide/ide-legacy.c
drivers/ide/ide-pnp.c
drivers/ide/ide-probe.c
drivers/ide/ide-tape.c
drivers/ide/ide-taskfile.c
drivers/ide/ide.c
drivers/ide/ide_platform.c
drivers/ide/macide.c
drivers/ide/palm_bk3710.c
drivers/ide/pdc202xx_new.c
drivers/ide/pdc202xx_old.c
drivers/ide/pmac.c
drivers/ide/q40ide.c
drivers/ide/rapide.c
drivers/ide/scc_pata.c
drivers/ide/setup-pci.c
drivers/ide/sgiioc4.c
drivers/ide/siimage.c
drivers/ide/sl82c105.c
drivers/ide/tx4938ide.c
drivers/ide/tx4939ide.c
drivers/infiniband/ulp/iser/iscsi_iser.c
drivers/input/serio/Kconfig
drivers/lguest/Kconfig
drivers/lguest/core.c
drivers/lguest/hypercalls.c
drivers/lguest/interrupts_and_traps.c
drivers/lguest/lg.h
drivers/lguest/lguest_device.c
drivers/lguest/lguest_user.c
drivers/lguest/page_tables.c
drivers/lguest/segments.c
drivers/message/fusion/mptbase.c
drivers/message/fusion/mptbase.h
drivers/message/fusion/mptctl.c
drivers/message/fusion/mptdebug.h
drivers/message/fusion/mptfc.c
drivers/message/fusion/mptsas.c
drivers/message/fusion/mptsas.h
drivers/message/fusion/mptscsih.c
drivers/message/fusion/mptscsih.h
drivers/message/fusion/mptspi.c
drivers/net/Kconfig
drivers/net/Makefile
drivers/net/bnx2.c
drivers/net/bnx2.h
drivers/net/cnic.c [new file with mode: 0644]
drivers/net/cnic.h [new file with mode: 0644]
drivers/net/cnic_defs.h [new file with mode: 0644]
drivers/net/cnic_if.h [new file with mode: 0644]
drivers/net/virtio_net.c
drivers/of/Kconfig
drivers/pnp/resource.c
drivers/s390/kvm/kvm_virtio.c
drivers/s390/scsi/zfcp_ccw.c
drivers/s390/scsi/zfcp_dbf.c
drivers/s390/scsi/zfcp_def.h
drivers/s390/scsi/zfcp_erp.c
drivers/s390/scsi/zfcp_ext.h
drivers/s390/scsi/zfcp_fc.c
drivers/s390/scsi/zfcp_fsf.c
drivers/s390/scsi/zfcp_scsi.c
drivers/scsi/Kconfig
drivers/scsi/Makefile
drivers/scsi/NCR_D700.c
drivers/scsi/bnx2i/57xx_iscsi_constants.h [new file with mode: 0644]
drivers/scsi/bnx2i/57xx_iscsi_hsi.h [new file with mode: 0644]
drivers/scsi/bnx2i/Kconfig [new file with mode: 0644]
drivers/scsi/bnx2i/Makefile [new file with mode: 0644]
drivers/scsi/bnx2i/bnx2i.h [new file with mode: 0644]
drivers/scsi/bnx2i/bnx2i_hwi.c [new file with mode: 0644]
drivers/scsi/bnx2i/bnx2i_init.c [new file with mode: 0644]
drivers/scsi/bnx2i/bnx2i_iscsi.c [new file with mode: 0644]
drivers/scsi/bnx2i/bnx2i_sysfs.c [new file with mode: 0644]
drivers/scsi/cxgb3i/cxgb3i.h
drivers/scsi/cxgb3i/cxgb3i_iscsi.c
drivers/scsi/cxgb3i/cxgb3i_offload.c
drivers/scsi/cxgb3i/cxgb3i_offload.h
drivers/scsi/device_handler/scsi_dh_rdac.c
drivers/scsi/fcoe/fcoe.c
drivers/scsi/fcoe/fcoe.h
drivers/scsi/fcoe/libfcoe.c
drivers/scsi/fnic/fnic_main.c
drivers/scsi/gdth_proc.c
drivers/scsi/ibmvscsi/ibmvfc.c
drivers/scsi/ibmvscsi/ibmvfc.h
drivers/scsi/ibmvscsi/ibmvscsi.c
drivers/scsi/ibmvscsi/ibmvscsi.h
drivers/scsi/ibmvscsi/viosrp.h
drivers/scsi/ipr.c
drivers/scsi/libfc/fc_exch.c
drivers/scsi/libfc/fc_fcp.c
drivers/scsi/libfc/fc_rport.c
drivers/scsi/libiscsi.c
drivers/scsi/libiscsi_tcp.c
drivers/scsi/lpfc/lpfc.h
drivers/scsi/lpfc/lpfc_attr.c
drivers/scsi/lpfc/lpfc_crtn.h
drivers/scsi/lpfc/lpfc_ct.c
drivers/scsi/lpfc/lpfc_debugfs.c
drivers/scsi/lpfc/lpfc_disc.h
drivers/scsi/lpfc/lpfc_els.c
drivers/scsi/lpfc/lpfc_hbadisc.c
drivers/scsi/lpfc/lpfc_hw.h
drivers/scsi/lpfc/lpfc_hw4.h [new file with mode: 0644]
drivers/scsi/lpfc/lpfc_init.c
drivers/scsi/lpfc/lpfc_logmsg.h
drivers/scsi/lpfc/lpfc_mbox.c
drivers/scsi/lpfc/lpfc_mem.c
drivers/scsi/lpfc/lpfc_nportdisc.c
drivers/scsi/lpfc/lpfc_scsi.c
drivers/scsi/lpfc/lpfc_scsi.h
drivers/scsi/lpfc/lpfc_sli.c
drivers/scsi/lpfc/lpfc_sli.h
drivers/scsi/lpfc/lpfc_sli4.h [new file with mode: 0644]
drivers/scsi/lpfc/lpfc_version.h
drivers/scsi/lpfc/lpfc_vport.c
drivers/scsi/mpt2sas/mpt2sas_base.h
drivers/scsi/mpt2sas/mpt2sas_ctl.c
drivers/scsi/mpt2sas/mpt2sas_scsih.c
drivers/scsi/mpt2sas/mpt2sas_transport.c
drivers/scsi/mvsas.c [deleted file]
drivers/scsi/mvsas/Kconfig [new file with mode: 0644]
drivers/scsi/mvsas/Makefile [new file with mode: 0644]
drivers/scsi/mvsas/mv_64xx.c [new file with mode: 0644]
drivers/scsi/mvsas/mv_64xx.h [new file with mode: 0644]
drivers/scsi/mvsas/mv_94xx.c [new file with mode: 0644]
drivers/scsi/mvsas/mv_94xx.h [new file with mode: 0644]
drivers/scsi/mvsas/mv_chips.h [new file with mode: 0644]
drivers/scsi/mvsas/mv_defs.h [new file with mode: 0644]
drivers/scsi/mvsas/mv_init.c [new file with mode: 0644]
drivers/scsi/mvsas/mv_sas.c [new file with mode: 0644]
drivers/scsi/mvsas/mv_sas.h [new file with mode: 0644]
drivers/scsi/osd/Kbuild
drivers/scsi/osd/Makefile [deleted file]
drivers/scsi/osd/osd_initiator.c
drivers/scsi/osd/osd_uld.c
drivers/scsi/qla1280.c
drivers/scsi/qla1280.h
drivers/scsi/qla2xxx/qla_attr.c
drivers/scsi/qla2xxx/qla_dbg.c
drivers/scsi/qla2xxx/qla_def.h
drivers/scsi/qla2xxx/qla_fw.h
drivers/scsi/qla2xxx/qla_gbl.h
drivers/scsi/qla2xxx/qla_gs.c
drivers/scsi/qla2xxx/qla_init.c
drivers/scsi/qla2xxx/qla_iocb.c
drivers/scsi/qla2xxx/qla_isr.c
drivers/scsi/qla2xxx/qla_mbx.c
drivers/scsi/qla2xxx/qla_mid.c
drivers/scsi/qla2xxx/qla_os.c
drivers/scsi/qla2xxx/qla_sup.c
drivers/scsi/qla2xxx/qla_version.h
drivers/scsi/scsi.c
drivers/scsi/scsi_debug.c
drivers/scsi/scsi_error.c
drivers/scsi/scsi_lib.c
drivers/scsi/scsi_scan.c
drivers/scsi/scsi_transport_iscsi.c
drivers/scsi/sd.c
drivers/scsi/st.c
drivers/scsi/sym53c8xx_2/sym_glue.c
drivers/scsi/sym53c8xx_2/sym_hipd.c
drivers/scsi/sym53c8xx_2/sym_hipd.h
drivers/spi/Kconfig
drivers/usb/Kconfig
drivers/video/Kconfig
drivers/video/aty/aty128fb.c
drivers/video/cyber2000fb.c
drivers/video/uvesafb.c
drivers/virtio/virtio.c
drivers/virtio/virtio_balloon.c
drivers/virtio/virtio_pci.c
drivers/virtio/virtio_ring.c
drivers/xen/manage.c
fs/Kconfig
fs/dlm/dir.c
fs/dlm/lockspace.c
fs/dlm/lowcomms.c
fs/dlm/lowcomms.h
fs/dlm/member.c
fs/dlm/requestqueue.c
fs/eventfd.c
fs/exofs/common.h
fs/exofs/inode.c
fs/exofs/osd.c
fs/fuse/Makefile
fs/fuse/cuse.c [new file with mode: 0644]
fs/fuse/dev.c
fs/fuse/dir.c
fs/fuse/file.c
fs/fuse/fuse_i.h
fs/fuse/inode.c
fs/gfs2/Makefile
fs/gfs2/bmap.c
fs/gfs2/glock.c
fs/gfs2/log.c
fs/gfs2/lops.c
fs/gfs2/ops_fstype.c
fs/gfs2/rgrp.c
fs/gfs2/super.c
fs/gfs2/trace_gfs2.h [new file with mode: 0644]
fs/partitions/check.c
include/asm-generic/Kbuild
include/asm-generic/Kbuild.asm
include/asm-generic/atomic-long.h [new file with mode: 0644]
include/asm-generic/atomic.h
include/asm-generic/auxvec.h [new file with mode: 0644]
include/asm-generic/bitops.h
include/asm-generic/bitops/atomic.h
include/asm-generic/bitsperlong.h [new file with mode: 0644]
include/asm-generic/bugs.h [new file with mode: 0644]
include/asm-generic/cache.h [new file with mode: 0644]
include/asm-generic/cacheflush.h [new file with mode: 0644]
include/asm-generic/checksum.h [new file with mode: 0644]
include/asm-generic/current.h [new file with mode: 0644]
include/asm-generic/delay.h [new file with mode: 0644]
include/asm-generic/dma.h [new file with mode: 0644]
include/asm-generic/fb.h [new file with mode: 0644]
include/asm-generic/getorder.h [new file with mode: 0644]
include/asm-generic/hardirq.h [new file with mode: 0644]
include/asm-generic/hw_irq.h [new file with mode: 0644]
include/asm-generic/int-l64.h
include/asm-generic/int-ll64.h
include/asm-generic/io.h [new file with mode: 0644]
include/asm-generic/ioctls.h [new file with mode: 0644]
include/asm-generic/ipcbuf.h [new file with mode: 0644]
include/asm-generic/irq.h [new file with mode: 0644]
include/asm-generic/irqflags.h [new file with mode: 0644]
include/asm-generic/kmap_types.h [new file with mode: 0644]
include/asm-generic/linkage.h [new file with mode: 0644]
include/asm-generic/mman-common.h [new file with mode: 0644]
include/asm-generic/mman.h
include/asm-generic/mmu.h [new file with mode: 0644]
include/asm-generic/mmu_context.h [new file with mode: 0644]
include/asm-generic/module.h [new file with mode: 0644]
include/asm-generic/msgbuf.h [new file with mode: 0644]
include/asm-generic/mutex.h [new file with mode: 0644]
include/asm-generic/page.h
include/asm-generic/param.h [new file with mode: 0644]
include/asm-generic/parport.h [new file with mode: 0644]
include/asm-generic/pci.h
include/asm-generic/pgalloc.h [new file with mode: 0644]
include/asm-generic/posix_types.h [new file with mode: 0644]
include/asm-generic/rtc.h
include/asm-generic/scatterlist.h [new file with mode: 0644]
include/asm-generic/segment.h [new file with mode: 0644]
include/asm-generic/sembuf.h [new file with mode: 0644]
include/asm-generic/serial.h [new file with mode: 0644]
include/asm-generic/setup.h [new file with mode: 0644]
include/asm-generic/shmbuf.h [new file with mode: 0644]
include/asm-generic/shmparam.h [new file with mode: 0644]
include/asm-generic/signal-defs.h [new file with mode: 0644]
include/asm-generic/signal.h
include/asm-generic/socket.h [new file with mode: 0644]
include/asm-generic/sockios.h [new file with mode: 0644]
include/asm-generic/spinlock.h [new file with mode: 0644]
include/asm-generic/stat.h [new file with mode: 0644]
include/asm-generic/string.h [new file with mode: 0644]
include/asm-generic/swab.h [new file with mode: 0644]
include/asm-generic/syscalls.h [new file with mode: 0644]
include/asm-generic/system.h [new file with mode: 0644]
include/asm-generic/termbits.h [new file with mode: 0644]
include/asm-generic/termios-base.h [new file with mode: 0644]
include/asm-generic/termios.h
include/asm-generic/timex.h [new file with mode: 0644]
include/asm-generic/tlbflush.h [new file with mode: 0644]
include/asm-generic/types.h [new file with mode: 0644]
include/asm-generic/uaccess-unaligned.h [new file with mode: 0644]
include/asm-generic/uaccess.h
include/asm-generic/ucontext.h [new file with mode: 0644]
include/asm-generic/unaligned.h [new file with mode: 0644]
include/asm-generic/unistd.h [new file with mode: 0644]
include/asm-generic/user.h [new file with mode: 0644]
include/asm-generic/vga.h [new file with mode: 0644]
include/drm/drmP.h
include/drm/drm_hashtab.h
include/drm/drm_mm.h [new file with mode: 0644]
include/drm/drm_pciids.h
include/linux/blkdev.h
include/linux/compiler.h
include/linux/device.h
include/linux/dlm.h
include/linux/fuse.h
include/linux/genhd.h
include/linux/gfp.h
include/linux/ide.h
include/linux/if_ether.h
include/linux/interrupt.h
include/linux/lguest.h
include/linux/lguest_launcher.h
include/linux/module.h
include/linux/moduleparam.h
include/linux/page_cgroup.h
include/linux/pci_ids.h
include/linux/perf_counter.h
include/linux/pm.h
include/linux/pnp.h
include/linux/slab.h
include/linux/slob_def.h
include/linux/slub_def.h
include/linux/suspend.h
include/linux/syscalls.h
include/linux/virtio.h
include/linux/virtio_config.h
include/linux/virtio_pci.h
include/linux/virtio_ring.h
include/scsi/fc/fc_fip.h
include/scsi/iscsi_if.h
include/scsi/libfc.h
include/scsi/libiscsi.h
include/scsi/osd_attributes.h
include/scsi/osd_initiator.h
include/scsi/osd_protocol.h
include/scsi/scsi_transport_iscsi.h
include/sound/asound.h
include/sound/core.h
include/sound/driver.h [deleted file]
include/sound/pcm.h
include/sound/soc-dai.h
include/sound/soc-dapm.h
include/sound/soc.h
include/sound/wm9081.h [new file with mode: 0644]
init/Kconfig
init/main.c
kernel/irq/handle.c
kernel/kexec.c
kernel/module.c
kernel/params.c
kernel/perf_counter.c
kernel/power/Kconfig
kernel/power/Makefile
kernel/power/hibernate.c [moved from kernel/power/disk.c with 96% similarity]
kernel/power/hibernate_nvs.c [new file with mode: 0644]
kernel/power/main.c
kernel/power/power.h
kernel/power/snapshot.c
kernel/power/suspend.c [new file with mode: 0644]
kernel/power/suspend_test.c [new file with mode: 0644]
kernel/power/swsusp.c
kernel/sched.c
lib/Makefile
lib/checksum.c [new file with mode: 0644]
lib/extable.c
mm/page_cgroup.c
mm/slab.c
mm/slub.c
mm/vmscan.c
net/9p/trans_virtio.c
scripts/checksyscalls.sh
scripts/mod/file2alias.c
sound/aoa/fabrics/layout.c
sound/aoa/soundbus/i2sbus/core.c
sound/core/Kconfig
sound/core/init.c
sound/core/jack.c
sound/core/oss/pcm_oss.c
sound/core/pcm_lib.c
sound/core/pcm_native.c
sound/core/seq/Kconfig [new file with mode: 0644]
sound/core/seq/Makefile
sound/drivers/opl3/Makefile
sound/drivers/opl4/Makefile
sound/isa/Kconfig
sound/isa/es1688/es1688.c
sound/isa/gus/gusextreme.c
sound/isa/sb/Makefile
sound/isa/sc6000.c
sound/mips/sgio2audio.c
sound/parisc/harmony.c
sound/pci/Kconfig
sound/pci/Makefile
sound/pci/au88x0/au88x0_core.c
sound/pci/bt87x.c
sound/pci/ca0106/ca0106_main.c
sound/pci/ca0106/ca0106_mixer.c
sound/pci/ctxfi/Makefile [new file with mode: 0644]
sound/pci/ctxfi/ct20k1reg.h [new file with mode: 0644]
sound/pci/ctxfi/ct20k2reg.h [new file with mode: 0644]
sound/pci/ctxfi/ctamixer.c [new file with mode: 0644]
sound/pci/ctxfi/ctamixer.h [new file with mode: 0644]
sound/pci/ctxfi/ctatc.c [new file with mode: 0644]
sound/pci/ctxfi/ctatc.h [new file with mode: 0644]
sound/pci/ctxfi/ctdaio.c [new file with mode: 0644]
sound/pci/ctxfi/ctdaio.h [new file with mode: 0644]
sound/pci/ctxfi/cthardware.c [new file with mode: 0644]
sound/pci/ctxfi/cthardware.h [new file with mode: 0644]
sound/pci/ctxfi/cthw20k1.c [new file with mode: 0644]
sound/pci/ctxfi/cthw20k1.h [new file with mode: 0644]
sound/pci/ctxfi/cthw20k2.c [new file with mode: 0644]
sound/pci/ctxfi/cthw20k2.h [new file with mode: 0644]
sound/pci/ctxfi/ctimap.c [new file with mode: 0644]
sound/pci/ctxfi/ctimap.h [new file with mode: 0644]
sound/pci/ctxfi/ctmixer.c [new file with mode: 0644]
sound/pci/ctxfi/ctmixer.h [new file with mode: 0644]
sound/pci/ctxfi/ctpcm.c [new file with mode: 0644]
sound/pci/ctxfi/ctpcm.h [new file with mode: 0644]
sound/pci/ctxfi/ctresource.c [new file with mode: 0644]
sound/pci/ctxfi/ctresource.h [new file with mode: 0644]
sound/pci/ctxfi/ctsrc.c [new file with mode: 0644]
sound/pci/ctxfi/ctsrc.h [new file with mode: 0644]
sound/pci/ctxfi/cttimer.c [new file with mode: 0644]
sound/pci/ctxfi/cttimer.h [new file with mode: 0644]
sound/pci/ctxfi/ctvmem.c [new file with mode: 0644]
sound/pci/ctxfi/ctvmem.h [new file with mode: 0644]
sound/pci/ctxfi/xfi.c [new file with mode: 0644]
sound/pci/emu10k1/Makefile
sound/pci/emu10k1/emu10k1x.c
sound/pci/emu10k1/emupcm.c
sound/pci/hda/Kconfig
sound/pci/hda/Makefile
sound/pci/hda/hda_beep.c
sound/pci/hda/hda_beep.h
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_hwdep.c
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_proc.c
sound/pci/hda/patch_ca0110.c [new file with mode: 0644]
sound/pci/hda/patch_nvhdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c
sound/pci/ice1712/Makefile
sound/pci/ice1712/ice1712.h
sound/pci/ice1712/ice1724.c
sound/pci/ice1712/maya44.c [new file with mode: 0644]
sound/pci/ice1712/maya44.h [new file with mode: 0644]
sound/pci/lx6464es/Makefile [new file with mode: 0644]
sound/pci/lx6464es/lx6464es.c [new file with mode: 0644]
sound/pci/lx6464es/lx6464es.h [new file with mode: 0644]
sound/pci/lx6464es/lx_core.c [new file with mode: 0644]
sound/pci/lx6464es/lx_core.h [new file with mode: 0644]
sound/pci/lx6464es/lx_defs.h [new file with mode: 0644]
sound/pci/oxygen/oxygen_pcm.c
sound/pci/oxygen/virtuoso.c
sound/pci/riptide/riptide.c
sound/pci/rme9652/hdsp.c
sound/pci/rme9652/hdspm.c
sound/ppc/awacs.c
sound/ppc/beep.c
sound/ppc/burgundy.c
sound/ppc/daca.c
sound/ppc/keywest.c
sound/ppc/pmac.c
sound/ppc/snd_ps3.c
sound/ppc/tumbler.c
sound/soc/Kconfig
sound/soc/Makefile
sound/soc/atmel/Kconfig
sound/soc/atmel/Makefile
sound/soc/atmel/playpaq_wm8510.c
sound/soc/atmel/snd-soc-afeb9260.c [new file with mode: 0644]
sound/soc/blackfin/bf5xx-ac97.c
sound/soc/blackfin/bf5xx-sport.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ac97.c
sound/soc/codecs/ad1980.c
sound/soc/codecs/cs4270.c
sound/soc/codecs/spdif_transciever.c [new file with mode: 0644]
sound/soc/codecs/spdif_transciever.h [new file with mode: 0644]
sound/soc/codecs/ssm2602.c
sound/soc/codecs/stac9766.c [new file with mode: 0644]
sound/soc/codecs/stac9766.h [new file with mode: 0644]
sound/soc/codecs/tlv320aic23.c
sound/soc/codecs/twl4030.c
sound/soc/codecs/twl4030.h
sound/soc/codecs/uda134x.c
sound/soc/codecs/wm8350.c
sound/soc/codecs/wm8350.h
sound/soc/codecs/wm8400.c
sound/soc/codecs/wm8510.c
sound/soc/codecs/wm8580.c
sound/soc/codecs/wm8731.c
sound/soc/codecs/wm8753.c
sound/soc/codecs/wm8900.c
sound/soc/codecs/wm8903.c
sound/soc/codecs/wm8940.c [new file with mode: 0644]
sound/soc/codecs/wm8940.h [new file with mode: 0644]
sound/soc/codecs/wm8960.c [new file with mode: 0644]
sound/soc/codecs/wm8960.h [new file with mode: 0644]
sound/soc/codecs/wm8988.c [new file with mode: 0644]
sound/soc/codecs/wm8988.h [new file with mode: 0644]
sound/soc/codecs/wm8990.c
sound/soc/codecs/wm9081.c [new file with mode: 0644]
sound/soc/codecs/wm9081.h [new file with mode: 0644]
sound/soc/codecs/wm9705.c
sound/soc/codecs/wm9712.c
sound/soc/codecs/wm9713.c
sound/soc/fsl/Kconfig
sound/soc/fsl/Makefile
sound/soc/fsl/efika-audio-fabric.c [new file with mode: 0644]
sound/soc/fsl/fsl_ssi.c
sound/soc/fsl/mpc5200_dma.c [new file with mode: 0644]
sound/soc/fsl/mpc5200_dma.h [new file with mode: 0644]
sound/soc/fsl/mpc5200_psc_ac97.c [new file with mode: 0644]
sound/soc/fsl/mpc5200_psc_ac97.h [new file with mode: 0644]
sound/soc/fsl/mpc5200_psc_i2s.c
sound/soc/fsl/mpc5200_psc_i2s.h [new file with mode: 0644]
sound/soc/fsl/pcm030-audio-fabric.c [new file with mode: 0644]
sound/soc/omap/Kconfig
sound/soc/omap/Makefile
sound/soc/omap/n810.c
sound/soc/omap/omap-mcbsp.c
sound/soc/omap/omap-pcm.c
sound/soc/omap/omap2evm.c
sound/soc/omap/omap3beagle.c
sound/soc/omap/omap3evm.c [new file with mode: 0644]
sound/soc/omap/omap3pandora.c
sound/soc/omap/overo.c
sound/soc/omap/sdp3430.c
sound/soc/pxa/Kconfig
sound/soc/pxa/Makefile
sound/soc/pxa/em-x270.c
sound/soc/pxa/imote2.c [new file with mode: 0644]
sound/soc/pxa/magician.c
sound/soc/pxa/pxa-ssp.c
sound/soc/pxa/pxa2xx-i2s.c
sound/soc/s3c24xx/neo1973_wm8753.c
sound/soc/s3c24xx/s3c-i2s-v2.c
sound/soc/s3c24xx/s3c2412-i2s.c
sound/soc/s3c24xx/s3c64xx-i2s.c
sound/soc/s3c24xx/s3c64xx-i2s.h
sound/soc/s6000/Kconfig [new file with mode: 0644]
sound/soc/s6000/Makefile [new file with mode: 0644]
sound/soc/s6000/s6000-i2s.c [new file with mode: 0644]
sound/soc/s6000/s6000-i2s.h [new file with mode: 0644]
sound/soc/s6000/s6000-pcm.c [new file with mode: 0644]
sound/soc/s6000/s6000-pcm.h [new file with mode: 0644]
sound/soc/s6000/s6105-ipcam.c [new file with mode: 0644]
sound/soc/sh/ssi.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/txx9/Kconfig [new file with mode: 0644]
sound/soc/txx9/Makefile [new file with mode: 0644]
sound/soc/txx9/txx9aclc-ac97.c [new file with mode: 0644]
sound/soc/txx9/txx9aclc-generic.c [new file with mode: 0644]
sound/soc/txx9/txx9aclc.c [new file with mode: 0644]
sound/soc/txx9/txx9aclc.h [new file with mode: 0644]
sound/synth/Makefile
sound/synth/emux/Makefile
sound/usb/caiaq/audio.c
sound/usb/caiaq/device.c
sound/usb/caiaq/device.h
sound/usb/caiaq/midi.c
sound/usb/usbaudio.c
sound/usb/usbquirks.h
tools/perf/builtin-record.c
tools/perf/design.txt
tools/perf/perf.h
tools/perf/util/parse-events.c

index 0c78f4b1d9d9f1df5298e9147c058a32aee90353..e77bebfa7b0d9b7cd459050970581034281538ee 100644 (file)
@@ -216,6 +216,8 @@ Other kernel parameters for ide_core are:
 
 * "noflush=[interface_number.device_number]" to disable flush requests
 
+* "nohpa=[interface_number.device_number]" to disable Host Protected Area
+
 * "noprobe=[interface_number.device_number]" to skip probing
 
 * "nowerr=[interface_number.device_number]" to ignore the WRERR_STAT bit
index 7bcdebffdab3850d9d3471a2f3ca5ea3b1f0b32a..0bf8a882ee9e18eb1bfba3ca05de68137211d1d8 100644 (file)
@@ -887,11 +887,8 @@ and is between 256 and 4096 characters. It is defined in the file
 
        ide-core.nodma= [HW] (E)IDE subsystem
                        Format: =0.0 to prevent dma on hda, =0.1 hdb =1.0 hdc
-                       .vlb_clock .pci_clock .noflush .noprobe .nowerr .cdrom
-                       .chs .ignore_cable are additional options
-                       See Documentation/ide/ide.txt.
-
-       idebus=         [HW] (E)IDE subsystem - VLB/PCI bus speed
+                       .vlb_clock .pci_clock .noflush .nohpa .noprobe .nowerr
+                       .cdrom .chs .ignore_cable are additional options
                        See Documentation/ide/ide.txt.
 
        ide-pci-generic.all-generic-ide [HW] (E)IDE subsystem
index 1f4f9e888bd1ab0ae834bb1a87ccfcf6ed26c6de..28c8cdfcafd8693898ae4c4a4d9bc5f4f9f77d62 100644 (file)
@@ -1,6 +1,5 @@
 # This creates the demonstration utility "lguest" which runs a Linux guest.
-CFLAGS:=-Wall -Wmissing-declarations -Wmissing-prototypes -O3 -I../../include -I../../arch/x86/include -U_FORTIFY_SOURCE
-LDLIBS:=-lz
+CFLAGS:=-m32 -Wall -Wmissing-declarations -Wmissing-prototypes -O3 -I../../include -I../../arch/x86/include -U_FORTIFY_SOURCE
 
 all: lguest
 
index d36fcc0f2715c705d0f70d1aa9c13e20b8aaa917..9ebcd6ef361b565fc331bd09c202866f403fcf99 100644 (file)
@@ -16,6 +16,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
+#include <sys/eventfd.h>
 #include <fcntl.h>
 #include <stdbool.h>
 #include <errno.h>
@@ -59,7 +60,6 @@ typedef uint8_t u8;
 /*:*/
 
 #define PAGE_PRESENT 0x7       /* Present, RW, Execute */
-#define NET_PEERNUM 1
 #define BRIDGE_PFX "bridge:"
 #ifndef SIOCBRADDIF
 #define SIOCBRADDIF    0x89a2          /* add interface to bridge      */
@@ -76,19 +76,12 @@ static bool verbose;
        do { if (verbose) printf(args); } while(0)
 /*:*/
 
-/* File descriptors for the Waker. */
-struct {
-       int pipe[2];
-       int lguest_fd;
-} waker_fds;
-
 /* The pointer to the start of guest memory. */
 static void *guest_base;
 /* The maximum guest physical address allowed, and maximum possible. */
 static unsigned long guest_limit, guest_max;
-/* The pipe for signal hander to write to. */
-static int timeoutpipe[2];
-static unsigned int timeout_usec = 500;
+/* The /dev/lguest file descriptor. */
+static int lguest_fd;
 
 /* a per-cpu variable indicating whose vcpu is currently running */
 static unsigned int __thread cpu_id;
@@ -96,11 +89,6 @@ static unsigned int __thread cpu_id;
 /* This is our list of devices. */
 struct device_list
 {
-       /* Summary information about the devices in our list: ready to pass to
-        * select() to ask which need servicing.*/
-       fd_set infds;
-       int max_infd;
-
        /* Counter to assign interrupt numbers. */
        unsigned int next_irq;
 
@@ -126,22 +114,21 @@ struct device
        /* The linked-list pointer. */
        struct device *next;
 
-       /* The this device's descriptor, as mapped into the Guest. */
+       /* The device's descriptor, as mapped into the Guest. */
        struct lguest_device_desc *desc;
 
+       /* We can't trust desc values once Guest has booted: we use these. */
+       unsigned int feature_len;
+       unsigned int num_vq;
+
        /* The name of this device, for --verbose. */
        const char *name;
 
-       /* If handle_input is set, it wants to be called when this file
-        * descriptor is ready. */
-       int fd;
-       bool (*handle_input)(int fd, struct device *me);
-
        /* Any queues attached to this device */
        struct virtqueue *vq;
 
-       /* Handle status being finalized (ie. feature bits stable). */
-       void (*ready)(struct device *me);
+       /* Is it operational */
+       bool running;
 
        /* Device-specific data. */
        void *priv;
@@ -164,22 +151,28 @@ struct virtqueue
        /* Last available index we saw. */
        u16 last_avail_idx;
 
-       /* The routine to call when the Guest pings us, or timeout. */
-       void (*handle_output)(int fd, struct virtqueue *me, bool timeout);
+       /* How many are used since we sent last irq? */
+       unsigned int pending_used;
 
-       /* Outstanding buffers */
-       unsigned int inflight;
+       /* Eventfd where Guest notifications arrive. */
+       int eventfd;
 
-       /* Is this blocked awaiting a timer? */
-       bool blocked;
+       /* Function for the thread which is servicing this virtqueue. */
+       void (*service)(struct virtqueue *vq);
+       pid_t thread;
 };
 
 /* Remember the arguments to the program so we can "reboot" */
 static char **main_args;
 
-/* Since guest is UP and we don't run at the same time, we don't need barriers.
- * But I include them in the code in case others copy it. */
-#define wmb()
+/* The original tty settings to restore on exit. */
+static struct termios orig_term;
+
+/* We have to be careful with barriers: our devices are all run in separate
+ * threads and so we need to make sure that changes visible to the Guest happen
+ * in precise order. */
+#define wmb() __asm__ __volatile__("" : : : "memory")
+#define mb() __asm__ __volatile__("" : : : "memory")
 
 /* Convert an iovec element to the given type.
  *
@@ -245,7 +238,7 @@ static void iov_consume(struct iovec iov[], unsigned num_iov, unsigned len)
 static u8 *get_feature_bits(struct device *dev)
 {
        return (u8 *)(dev->desc + 1)
-               + dev->desc->num_vq * sizeof(struct lguest_vqconfig);
+               + dev->num_vq * sizeof(struct lguest_vqconfig);
 }
 
 /*L:100 The Launcher code itself takes us out into userspace, that scary place
@@ -505,99 +498,19 @@ static void concat(char *dst, char *args[])
  * saw the arguments it expects when we looked at initialize() in lguest_user.c:
  * the base of Guest "physical" memory, the top physical page to allow and the
  * entry point for the Guest. */
-static int tell_kernel(unsigned long start)
+static void tell_kernel(unsigned long start)
 {
        unsigned long args[] = { LHREQ_INITIALIZE,
                                 (unsigned long)guest_base,
                                 guest_limit / getpagesize(), start };
-       int fd;
-
        verbose("Guest: %p - %p (%#lx)\n",
                guest_base, guest_base + guest_limit, guest_limit);
-       fd = open_or_die("/dev/lguest", O_RDWR);
-       if (write(fd, args, sizeof(args)) < 0)
+       lguest_fd = open_or_die("/dev/lguest", O_RDWR);
+       if (write(lguest_fd, args, sizeof(args)) < 0)
                err(1, "Writing to /dev/lguest");
-
-       /* We return the /dev/lguest file descriptor to control this Guest */
-       return fd;
 }
 /*:*/
 
-static void add_device_fd(int fd)
-{
-       FD_SET(fd, &devices.infds);
-       if (fd > devices.max_infd)
-               devices.max_infd = fd;
-}
-
-/*L:200
- * The Waker.
- *
- * With console, block and network devices, we can have lots of input which we
- * need to process.  We could try to tell the kernel what file descriptors to
- * watch, but handing a file descriptor mask through to the kernel is fairly
- * icky.
- *
- * Instead, we clone off a thread which watches the file descriptors and writes
- * the LHREQ_BREAK command to the /dev/lguest file descriptor to tell the Host
- * stop running the Guest.  This causes the Launcher to return from the
- * /dev/lguest read with -EAGAIN, where it will write to /dev/lguest to reset
- * the LHREQ_BREAK and wake us up again.
- *
- * This, of course, is merely a different *kind* of icky.
- *
- * Given my well-known antipathy to threads, I'd prefer to use processes.  But
- * it's easier to share Guest memory with threads, and trivial to share the
- * devices.infds as the Launcher changes it.
- */
-static int waker(void *unused)
-{
-       /* Close the write end of the pipe: only the Launcher has it open. */
-       close(waker_fds.pipe[1]);
-
-       for (;;) {
-               fd_set rfds = devices.infds;
-               unsigned long args[] = { LHREQ_BREAK, 1 };
-               unsigned int maxfd = devices.max_infd;
-
-               /* We also listen to the pipe from the Launcher. */
-               FD_SET(waker_fds.pipe[0], &rfds);
-               if (waker_fds.pipe[0] > maxfd)
-                       maxfd = waker_fds.pipe[0];
-
-               /* Wait until input is ready from one of the devices. */
-               select(maxfd+1, &rfds, NULL, NULL, NULL);
-
-               /* Message from Launcher? */
-               if (FD_ISSET(waker_fds.pipe[0], &rfds)) {
-                       char c;
-                       /* If this fails, then assume Launcher has exited.
-                        * Don't do anything on exit: we're just a thread! */
-                       if (read(waker_fds.pipe[0], &c, 1) != 1)
-                               _exit(0);
-                       continue;
-               }
-
-               /* Send LHREQ_BREAK command to snap the Launcher out of it. */
-               pwrite(waker_fds.lguest_fd, args, sizeof(args), cpu_id);
-       }
-       return 0;
-}
-
-/* This routine just sets up a pipe to the Waker process. */
-static void setup_waker(int lguest_fd)
-{
-       /* This pipe is closed when Launcher dies, telling Waker. */
-       if (pipe(waker_fds.pipe) != 0)
-               err(1, "Creating pipe for Waker");
-
-       /* Waker also needs to know the lguest fd */
-       waker_fds.lguest_fd = lguest_fd;
-
-       if (clone(waker, malloc(4096) + 4096, CLONE_VM | SIGCHLD, NULL) == -1)
-               err(1, "Creating Waker");
-}
-
 /*
  * Device Handling.
  *
@@ -623,49 +536,90 @@ static void *_check_pointer(unsigned long addr, unsigned int size,
 /* Each buffer in the virtqueues is actually a chain of descriptors.  This
  * function returns the next descriptor in the chain, or vq->vring.num if we're
  * at the end. */
-static unsigned next_desc(struct virtqueue *vq, unsigned int i)
+static unsigned next_desc(struct vring_desc *desc,
+                         unsigned int i, unsigned int max)
 {
        unsigned int next;
 
        /* If this descriptor says it doesn't chain, we're done. */
-       if (!(vq->vring.desc[i].flags & VRING_DESC_F_NEXT))
-               return vq->vring.num;
+       if (!(desc[i].flags & VRING_DESC_F_NEXT))
+               return max;
 
        /* Check they're not leading us off end of descriptors. */
-       next = vq->vring.desc[i].next;
+       next = desc[i].next;
        /* Make sure compiler knows to grab that: we don't want it changing! */
        wmb();
 
-       if (next >= vq->vring.num)
+       if (next >= max)
                errx(1, "Desc next is %u", next);
 
        return next;
 }
 
+/* This actually sends the interrupt for this virtqueue */
+static void trigger_irq(struct virtqueue *vq)
+{
+       unsigned long buf[] = { LHREQ_IRQ, vq->config.irq };
+
+       /* Don't inform them if nothing used. */
+       if (!vq->pending_used)
+               return;
+       vq->pending_used = 0;
+
+       /* If they don't want an interrupt, don't send one, unless empty. */
+       if ((vq->vring.avail->flags & VRING_AVAIL_F_NO_INTERRUPT)
+           && lg_last_avail(vq) != vq->vring.avail->idx)
+               return;
+
+       /* Send the Guest an interrupt tell them we used something up. */
+       if (write(lguest_fd, buf, sizeof(buf)) != 0)
+               err(1, "Triggering irq %i", vq->config.irq);
+}
+
 /* This looks in the virtqueue and for the first available buffer, and converts
  * it to an iovec for convenient access.  Since descriptors consist of some
  * number of output then some number of input descriptors, it's actually two
  * iovecs, but we pack them into one and note how many of each there were.
  *
- * This function returns the descriptor number found, or vq->vring.num (which
- * is never a valid descriptor number) if none was found. */
-static unsigned get_vq_desc(struct virtqueue *vq,
-                           struct iovec iov[],
-                           unsigned int *out_num, unsigned int *in_num)
+ * This function returns the descriptor number found. */
+static unsigned wait_for_vq_desc(struct virtqueue *vq,
+                                struct iovec iov[],
+                                unsigned int *out_num, unsigned int *in_num)
 {
-       unsigned int i, head;
-       u16 last_avail;
+       unsigned int i, head, max;
+       struct vring_desc *desc;
+       u16 last_avail = lg_last_avail(vq);
+
+       while (last_avail == vq->vring.avail->idx) {
+               u64 event;
+
+               /* OK, tell Guest about progress up to now. */
+               trigger_irq(vq);
+
+               /* OK, now we need to know about added descriptors. */
+               vq->vring.used->flags &= ~VRING_USED_F_NO_NOTIFY;
+
+               /* They could have slipped one in as we were doing that: make
+                * sure it's written, then check again. */
+               mb();
+               if (last_avail != vq->vring.avail->idx) {
+                       vq->vring.used->flags |= VRING_USED_F_NO_NOTIFY;
+                       break;
+               }
+
+               /* Nothing new?  Wait for eventfd to tell us they refilled. */
+               if (read(vq->eventfd, &event, sizeof(event)) != sizeof(event))
+                       errx(1, "Event read failed?");
+
+               /* We don't need to be notified again. */
+               vq->vring.used->flags |= VRING_USED_F_NO_NOTIFY;
+       }
 
        /* Check it isn't doing very strange things with descriptor numbers. */
-       last_avail = lg_last_avail(vq);
        if ((u16)(vq->vring.avail->idx - last_avail) > vq->vring.num)
                errx(1, "Guest moved used index from %u to %u",
                     last_avail, vq->vring.avail->idx);
 
-       /* If there's nothing new since last we looked, return invalid. */
-       if (vq->vring.avail->idx == last_avail)
-               return vq->vring.num;
-
        /* Grab the next descriptor number they're advertising, and increment
         * the index we've seen. */
        head = vq->vring.avail->ring[last_avail % vq->vring.num];
@@ -678,15 +632,28 @@ static unsigned get_vq_desc(struct virtqueue *vq,
        /* When we start there are none of either input nor output. */
        *out_num = *in_num = 0;
 
+       max = vq->vring.num;
+       desc = vq->vring.desc;
        i = head;
+
+       /* If this is an indirect entry, then this buffer contains a descriptor
+        * table which we handle as if it's any normal descriptor chain. */
+       if (desc[i].flags & VRING_DESC_F_INDIRECT) {
+               if (desc[i].len % sizeof(struct vring_desc))
+                       errx(1, "Invalid size for indirect buffer table");
+
+               max = desc[i].len / sizeof(struct vring_desc);
+               desc = check_pointer(desc[i].addr, desc[i].len);
+               i = 0;
+       }
+
        do {
                /* Grab the first descriptor, and check it's OK. */
-               iov[*out_num + *in_num].iov_len = vq->vring.desc[i].len;
+               iov[*out_num + *in_num].iov_len = desc[i].len;
                iov[*out_num + *in_num].iov_base
-                       = check_pointer(vq->vring.desc[i].addr,
-                                       vq->vring.desc[i].len);
+                       = check_pointer(desc[i].addr, desc[i].len);
                /* If this is an input descriptor, increment that count. */
-               if (vq->vring.desc[i].flags & VRING_DESC_F_WRITE)
+               if (desc[i].flags & VRING_DESC_F_WRITE)
                        (*in_num)++;
                else {
                        /* If it's an output descriptor, they're all supposed
@@ -697,11 +664,10 @@ static unsigned get_vq_desc(struct virtqueue *vq,
                }
 
                /* If we've got too many, that implies a descriptor loop. */
-               if (*out_num + *in_num > vq->vring.num)
+               if (*out_num + *in_num > max)
                        errx(1, "Looped descriptor");
-       } while ((i = next_desc(vq, i)) != vq->vring.num);
+       } while ((i = next_desc(desc, i, max)) != max);
 
-       vq->inflight++;
        return head;
 }
 
@@ -719,44 +685,20 @@ static void add_used(struct virtqueue *vq, unsigned int head, int len)
        /* Make sure buffer is written before we update index. */
        wmb();
        vq->vring.used->idx++;
-       vq->inflight--;
-}
-
-/* This actually sends the interrupt for this virtqueue */
-static void trigger_irq(int fd, struct virtqueue *vq)
-{
-       unsigned long buf[] = { LHREQ_IRQ, vq->config.irq };
-
-       /* If they don't want an interrupt, don't send one, unless empty. */
-       if ((vq->vring.avail->flags & VRING_AVAIL_F_NO_INTERRUPT)
-           && vq->inflight)
-               return;
-
-       /* Send the Guest an interrupt tell them we used something up. */
-       if (write(fd, buf, sizeof(buf)) != 0)
-               err(1, "Triggering irq %i", vq->config.irq);
+       vq->pending_used++;
 }
 
 /* And here's the combo meal deal.  Supersize me! */
-static void add_used_and_trigger(int fd, struct virtqueue *vq,
-                                unsigned int head, int len)
+static void add_used_and_trigger(struct virtqueue *vq, unsigned head, int len)
 {
        add_used(vq, head, len);
-       trigger_irq(fd, vq);
+       trigger_irq(vq);
 }
 
 /*
  * The Console
  *
- * Here is the input terminal setting we save, and the routine to restore them
- * on exit so the user gets their terminal back. */
-static struct termios orig_term;
-static void restore_term(void)
-{
-       tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
-}
-
-/* We associate some data with the console for our exit hack. */
+ * We associate some data with the console for our exit hack. */
 struct console_abort
 {
        /* How many times have they hit ^C? */
@@ -766,276 +708,275 @@ struct console_abort
 };
 
 /* This is the routine which handles console input (ie. stdin). */
-static bool handle_console_input(int fd, struct device *dev)
+static void console_input(struct virtqueue *vq)
 {
        int len;
        unsigned int head, in_num, out_num;
-       struct iovec iov[dev->vq->vring.num];
-       struct console_abort *abort = dev->priv;
-
-       /* First we need a console buffer from the Guests's input virtqueue. */
-       head = get_vq_desc(dev->vq, iov, &out_num, &in_num);
-
-       /* If they're not ready for input, stop listening to this file
-        * descriptor.  We'll start again once they add an input buffer. */
-       if (head == dev->vq->vring.num)
-               return false;
+       struct console_abort *abort = vq->dev->priv;
+       struct iovec iov[vq->vring.num];
 
+       /* Make sure there's a descriptor waiting. */
+       head = wait_for_vq_desc(vq, iov, &out_num, &in_num);
        if (out_num)
                errx(1, "Output buffers in console in queue?");
 
-       /* This is why we convert to iovecs: the readv() call uses them, and so
-        * it reads straight into the Guest's buffer. */
-       len = readv(dev->fd, iov, in_num);
+       /* Read it in. */
+       len = readv(STDIN_FILENO, iov, in_num);
        if (len <= 0) {
-               /* This implies that the console is closed, is /dev/null, or
-                * something went terribly wrong. */
+               /* Ran out of input? */
                warnx("Failed to get console input, ignoring console.");
-               /* Put the input terminal back. */
-               restore_term();
-               /* Remove callback from input vq, so it doesn't restart us. */
-               dev->vq->handle_output = NULL;
-               /* Stop listening to this fd: don't call us again. */
-               return false;
+               /* For simplicity, dying threads kill the whole Launcher.  So
+                * just nap here. */
+               for (;;)
+                       pause();
        }
 
-       /* Tell the Guest about the new input. */
-       add_used_and_trigger(fd, dev->vq, head, len);
+       add_used_and_trigger(vq, head, len);
 
        /* Three ^C within one second?  Exit.
         *
-        * This is such a hack, but works surprisingly well.  Each ^C has to be
-        * in a buffer by itself, so they can't be too fast.  But we check that
-        * we get three within about a second, so they can't be too slow. */
-       if (len == 1 && ((char *)iov[0].iov_base)[0] == 3) {
-               if (!abort->count++)
-                       gettimeofday(&abort->start, NULL);
-               else if (abort->count == 3) {
-                       struct timeval now;
-                       gettimeofday(&now, NULL);
-                       if (now.tv_sec <= abort->start.tv_sec+1) {
-                               unsigned long args[] = { LHREQ_BREAK, 0 };
-                               /* Close the fd so Waker will know it has to
-                                * exit. */
-                               close(waker_fds.pipe[1]);
-                               /* Just in case Waker is blocked in BREAK, send
-                                * unbreak now. */
-                               write(fd, args, sizeof(args));
-                               exit(2);
-                       }
-                       abort->count = 0;
-               }
-       } else
-               /* Any other key resets the abort counter. */
+        * This is such a hack, but works surprisingly well.  Each ^C has to
+        * be in a buffer by itself, so they can't be too fast.  But we check
+        * that we get three within about a second, so they can't be too
+        * slow. */
+       if (len != 1 || ((char *)iov[0].iov_base)[0] != 3) {
                abort->count = 0;
+               return;
+       }
 
-       /* Everything went OK! */
-       return true;
+       abort->count++;
+       if (abort->count == 1)
+               gettimeofday(&abort->start, NULL);
+       else if (abort->count == 3) {
+               struct timeval now;
+               gettimeofday(&now, NULL);
+               /* Kill all Launcher processes with SIGINT, like normal ^C */
+               if (now.tv_sec <= abort->start.tv_sec+1)
+                       kill(0, SIGINT);
+               abort->count = 0;
+       }
 }
 
-/* Handling output for console is simple: we just get all the output buffers
- * and write them to stdout. */
-static void handle_console_output(int fd, struct virtqueue *vq, bool timeout)
+/* This is the routine which handles console output (ie. stdout). */
+static void console_output(struct virtqueue *vq)
 {
        unsigned int head, out, in;
-       int len;
        struct iovec iov[vq->vring.num];
 
-       /* Keep getting output buffers from the Guest until we run out. */
-       while ((head = get_vq_desc(vq, iov, &out, &in)) != vq->vring.num) {
-               if (in)
-                       errx(1, "Input buffers in output queue?");
-               len = writev(STDOUT_FILENO, iov, out);
-               add_used_and_trigger(fd, vq, head, len);
+       head = wait_for_vq_desc(vq, iov, &out, &in);
+       if (in)
+               errx(1, "Input buffers in console output queue?");
+       while (!iov_empty(iov, out)) {
+               int len = writev(STDOUT_FILENO, iov, out);
+               if (len <= 0)
+                       err(1, "Write to stdout gave %i", len);
+               iov_consume(iov, out, len);
        }
-}
-
-/* This is called when we no longer want to hear about Guest changes to a
- * virtqueue.  This is more efficient in high-traffic cases, but it means we
- * have to set a timer to check if any more changes have occurred. */
-static void block_vq(struct virtqueue *vq)
-{
-       struct itimerval itm;
-
-       vq->vring.used->flags |= VRING_USED_F_NO_NOTIFY;
-       vq->blocked = true;
-
-       itm.it_interval.tv_sec = 0;
-       itm.it_interval.tv_usec = 0;
-       itm.it_value.tv_sec = 0;
-       itm.it_value.tv_usec = timeout_usec;
-
-       setitimer(ITIMER_REAL, &itm, NULL);
+       add_used(vq, head, 0);
 }
 
 /*
  * The Network
  *
  * Handling output for network is also simple: we get all the output buffers
- * and write them (ignoring the first element) to this device's file descriptor
- * (/dev/net/tun).
+ * and write them to /dev/net/tun.
  */
-static void handle_net_output(int fd, struct virtqueue *vq, bool timeout)
+struct net_info {
+       int tunfd;
+};
+
+static void net_output(struct virtqueue *vq)
 {
-       unsigned int head, out, in, num = 0;
-       int len;
+       struct net_info *net_info = vq->dev->priv;
+       unsigned int head, out, in;
        struct iovec iov[vq->vring.num];
-       static int last_timeout_num;
-
-       /* Keep getting output buffers from the Guest until we run out. */
-       while ((head = get_vq_desc(vq, iov, &out, &in)) != vq->vring.num) {
-               if (in)
-                       errx(1, "Input buffers in output queue?");
-               len = writev(vq->dev->fd, iov, out);
-               if (len < 0)
-                       err(1, "Writing network packet to tun");
-               add_used_and_trigger(fd, vq, head, len);
-               num++;
-       }
 
-       /* Block further kicks and set up a timer if we saw anything. */
-       if (!timeout && num)
-               block_vq(vq);
-
-       /* We never quite know how long should we wait before we check the
-        * queue again for more packets.  We start at 500 microseconds, and if
-        * we get fewer packets than last time, we assume we made the timeout
-        * too small and increase it by 10 microseconds.  Otherwise, we drop it
-        * by one microsecond every time.  It seems to work well enough. */
-       if (timeout) {
-               if (num < last_timeout_num)
-                       timeout_usec += 10;
-               else if (timeout_usec > 1)
-                       timeout_usec--;
-               last_timeout_num = num;
-       }
+       head = wait_for_vq_desc(vq, iov, &out, &in);
+       if (in)
+               errx(1, "Input buffers in net output queue?");
+       if (writev(net_info->tunfd, iov, out) < 0)
+               errx(1, "Write to tun failed?");
+       add_used(vq, head, 0);
+}
+
+/* Will reading from this file descriptor block? */
+static bool will_block(int fd)
+{
+       fd_set fdset;
+       struct timeval zero = { 0, 0 };
+       FD_ZERO(&fdset);
+       FD_SET(fd, &fdset);
+       return select(fd+1, &fdset, NULL, NULL, &zero) != 1;
 }
 
-/* This is where we handle a packet coming in from the tun device to our
+/* This is where we handle packets coming in from the tun device to our
  * Guest. */
-static bool handle_tun_input(int fd, struct device *dev)
+static void net_input(struct virtqueue *vq)
 {
-       unsigned int head, in_num, out_num;
        int len;
-       struct iovec iov[dev->vq->vring.num];
-
-       /* First we need a network buffer from the Guests's recv virtqueue. */
-       head = get_vq_desc(dev->vq, iov, &out_num, &in_num);
-       if (head == dev->vq->vring.num) {
-               /* Now, it's expected that if we try to send a packet too
-                * early, the Guest won't be ready yet.  Wait until the device
-                * status says it's ready. */
-               /* FIXME: Actually want DRIVER_ACTIVE here. */
-
-               /* Now tell it we want to know if new things appear. */
-               dev->vq->vring.used->flags &= ~VRING_USED_F_NO_NOTIFY;
-               wmb();
-
-               /* We'll turn this back on if input buffers are registered. */
-               return false;
-       } else if (out_num)
-               errx(1, "Output buffers in network recv queue?");
-
-       /* Read the packet from the device directly into the Guest's buffer. */
-       len = readv(dev->fd, iov, in_num);
-       if (len <= 0)
-               err(1, "reading network");
+       unsigned int head, out, in;
+       struct iovec iov[vq->vring.num];
+       struct net_info *net_info = vq->dev->priv;
 
-       /* Tell the Guest about the new packet. */
-       add_used_and_trigger(fd, dev->vq, head, len);
+       head = wait_for_vq_desc(vq, iov, &out, &in);
+       if (out)
+               errx(1, "Output buffers in net input queue?");
 
-       verbose("tun input packet len %i [%02x %02x] (%s)\n", len,
-               ((u8 *)iov[1].iov_base)[0], ((u8 *)iov[1].iov_base)[1],
-               head != dev->vq->vring.num ? "sent" : "discarded");
+       /* Deliver interrupt now, since we're about to sleep. */
+       if (vq->pending_used && will_block(net_info->tunfd))
+               trigger_irq(vq);
 
-       /* All good. */
-       return true;
+       len = readv(net_info->tunfd, iov, in);
+       if (len <= 0)
+               err(1, "Failed to read from tun.");
+       add_used(vq, head, len);
 }
 
-/*L:215 This is the callback attached to the network and console input
- * virtqueues: it ensures we try again, in case we stopped console or net
- * delivery because Guest didn't have any buffers. */
-static void enable_fd(int fd, struct virtqueue *vq, bool timeout)
+/* This is the helper to create threads. */
+static int do_thread(void *_vq)
 {
-       add_device_fd(vq->dev->fd);
-       /* Snap the Waker out of its select loop. */
-       write(waker_fds.pipe[1], "", 1);
+       struct virtqueue *vq = _vq;
+
+       for (;;)
+               vq->service(vq);
+       return 0;
 }
 
-static void net_enable_fd(int fd, struct virtqueue *vq, bool timeout)
+/* When a child dies, we kill our entire process group with SIGTERM.  This
+ * also has the side effect that the shell restores the console for us! */
+static void kill_launcher(int signal)
 {
-       /* We don't need to know again when Guest refills receive buffer. */
-       vq->vring.used->flags |= VRING_USED_F_NO_NOTIFY;
-       enable_fd(fd, vq, timeout);
+       kill(0, SIGTERM);
 }
 
-/* When the Guest tells us they updated the status field, we handle it. */
-static void update_device_status(struct device *dev)
+static void reset_device(struct device *dev)
 {
        struct virtqueue *vq;
 
-       /* This is a reset. */
-       if (dev->desc->status == 0) {
-               verbose("Resetting device %s\n", dev->name);
+       verbose("Resetting device %s\n", dev->name);
 
-               /* Clear any features they've acked. */
-               memset(get_feature_bits(dev) + dev->desc->feature_len, 0,
-                      dev->desc->feature_len);
+       /* Clear any features they've acked. */
+       memset(get_feature_bits(dev) + dev->feature_len, 0, dev->feature_len);
 
-               /* Zero out the virtqueues. */
-               for (vq = dev->vq; vq; vq = vq->next) {
-                       memset(vq->vring.desc, 0,
-                              vring_size(vq->config.num, LGUEST_VRING_ALIGN));
-                       lg_last_avail(vq) = 0;
+       /* We're going to be explicitly killing threads, so ignore them. */
+       signal(SIGCHLD, SIG_IGN);
+
+       /* Zero out the virtqueues, get rid of their threads */
+       for (vq = dev->vq; vq; vq = vq->next) {
+               if (vq->thread != (pid_t)-1) {
+                       kill(vq->thread, SIGTERM);
+                       waitpid(vq->thread, NULL, 0);
+                       vq->thread = (pid_t)-1;
                }
-       } else if (dev->desc->status & VIRTIO_CONFIG_S_FAILED) {
+               memset(vq->vring.desc, 0,
+                      vring_size(vq->config.num, LGUEST_VRING_ALIGN));
+               lg_last_avail(vq) = 0;
+       }
+       dev->running = false;
+
+       /* Now we care if threads die. */
+       signal(SIGCHLD, (void *)kill_launcher);
+}
+
+static void create_thread(struct virtqueue *vq)
+{
+       /* Create stack for thread and run it.  Since stack grows
+        * upwards, we point the stack pointer to the end of this
+        * region. */
+       char *stack = malloc(32768);
+       unsigned long args[] = { LHREQ_EVENTFD,
+                                vq->config.pfn*getpagesize(), 0 };
+
+       /* Create a zero-initialized eventfd. */
+       vq->eventfd = eventfd(0, 0);
+       if (vq->eventfd < 0)
+               err(1, "Creating eventfd");
+       args[2] = vq->eventfd;
+
+       /* Attach an eventfd to this virtqueue: it will go off
+        * when the Guest does an LHCALL_NOTIFY for this vq. */
+       if (write(lguest_fd, &args, sizeof(args)) != 0)
+               err(1, "Attaching eventfd");
+
+       /* CLONE_VM: because it has to access the Guest memory, and
+        * SIGCHLD so we get a signal if it dies. */
+       vq->thread = clone(do_thread, stack + 32768, CLONE_VM | SIGCHLD, vq);
+       if (vq->thread == (pid_t)-1)
+               err(1, "Creating clone");
+       /* We close our local copy, now the child has it. */
+       close(vq->eventfd);
+}
+
+static void start_device(struct device *dev)
+{
+       unsigned int i;
+       struct virtqueue *vq;
+
+       verbose("Device %s OK: offered", dev->name);
+       for (i = 0; i < dev->feature_len; i++)
+               verbose(" %02x", get_feature_bits(dev)[i]);
+       verbose(", accepted");
+       for (i = 0; i < dev->feature_len; i++)
+               verbose(" %02x", get_feature_bits(dev)
+                       [dev->feature_len+i]);
+
+       for (vq = dev->vq; vq; vq = vq->next) {
+               if (vq->service)
+                       create_thread(vq);
+       }
+       dev->running = true;
+}
+
+static void cleanup_devices(void)
+{
+       struct device *dev;
+
+       for (dev = devices.dev; dev; dev = dev->next)
+               reset_device(dev);
+
+       /* If we saved off the original terminal settings, restore them now. */
+       if (orig_term.c_lflag & (ISIG|ICANON|ECHO))
+               tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
+}
+
+/* When the Guest tells us they updated the status field, we handle it. */
+static void update_device_status(struct device *dev)
+{
+       /* A zero status is a reset, otherwise it's a set of flags. */
+       if (dev->desc->status == 0)
+               reset_device(dev);
+       else if (dev->desc->status & VIRTIO_CONFIG_S_FAILED) {
                warnx("Device %s configuration FAILED", dev->name);
+               if (dev->running)
+                       reset_device(dev);
        } else if (dev->desc->status & VIRTIO_CONFIG_S_DRIVER_OK) {
-               unsigned int i;
-
-               verbose("Device %s OK: offered", dev->name);
-               for (i = 0; i < dev->desc->feature_len; i++)
-                       verbose(" %02x", get_feature_bits(dev)[i]);
-               verbose(", accepted");
-               for (i = 0; i < dev->desc->feature_len; i++)
-                       verbose(" %02x", get_feature_bits(dev)
-                               [dev->desc->feature_len+i]);
-
-               if (dev->ready)
-                       dev->ready(dev);
+               if (!dev->running)
+                       start_device(dev);
        }
 }
 
 /* This is the generic routine we call when the Guest uses LHCALL_NOTIFY. */
-static void handle_output(int fd, unsigned long addr)
+static void handle_output(unsigned long addr)
 {
        struct device *i;
-       struct virtqueue *vq;
 
-       /* Check each device and virtqueue. */
+       /* Check each device. */
        for (i = devices.dev; i; i = i->next) {
+               struct virtqueue *vq;
+
                /* Notifications to device descriptors update device status. */
                if (from_guest_phys(addr) == i->desc) {
                        update_device_status(i);
                        return;
                }
 
-               /* Notifications to virtqueues mean output has occurred. */
+               /* Devices *can* be used before status is set to DRIVER_OK. */
                for (vq = i->vq; vq; vq = vq->next) {
-                       if (vq->config.pfn != addr/getpagesize())
+                       if (addr != vq->config.pfn*getpagesize())
                                continue;
-
-                       /* Guest should acknowledge (and set features!)  before
-                        * using the device. */
-                       if (i->desc->status == 0) {
-                               warnx("%s gave early output", i->name);
-                               return;
-                       }
-
-                       if (strcmp(vq->dev->name, "console") != 0)
-                               verbose("Output to %s\n", vq->dev->name);
-                       if (vq->handle_output)
-                               vq->handle_output(fd, vq, false);
+                       if (i->running)
+                               errx(1, "Notification on running %s", i->name);
+                       start_device(i);
                        return;
                }
        }
@@ -1049,71 +990,6 @@ static void handle_output(int fd, unsigned long addr)
              strnlen(from_guest_phys(addr), guest_limit - addr));
 }
 
-static void handle_timeout(int fd)
-{
-       char buf[32];
-       struct device *i;
-       struct virtqueue *vq;
-
-       /* Clear the pipe */
-       read(timeoutpipe[0], buf, sizeof(buf));
-
-       /* Check each device and virtqueue: flush blocked ones. */
-       for (i = devices.dev; i; i = i->next) {
-               for (vq = i->vq; vq; vq = vq->next) {
-                       if (!vq->blocked)
-                               continue;
-
-                       vq->vring.used->flags &= ~VRING_USED_F_NO_NOTIFY;
-                       vq->blocked = false;
-                       if (vq->handle_output)
-                               vq->handle_output(fd, vq, true);
-               }
-       }
-}
-
-/* This is called when the Waker wakes us up: check for incoming file
- * descriptors. */
-static void handle_input(int fd)
-{
-       /* select() wants a zeroed timeval to mean "don't wait". */
-       struct timeval poll = { .tv_sec = 0, .tv_usec = 0 };
-
-       for (;;) {
-               struct device *i;
-               fd_set fds = devices.infds;
-               int num;
-
-               num = select(devices.max_infd+1, &fds, NULL, NULL, &poll);
-               /* Could get interrupted */
-               if (num < 0)
-                       continue;
-               /* If nothing is ready, we're done. */
-               if (num == 0)
-                       break;
-
-               /* Otherwise, call the device(s) which have readable file
-                * descriptors and a method of handling them.  */
-               for (i = devices.dev; i; i = i->next) {
-                       if (i->handle_input && FD_ISSET(i->fd, &fds)) {
-                               if (i->handle_input(fd, i))
-                                       continue;
-
-                               /* If handle_input() returns false, it means we
-                                * should no longer service it.  Networking and
-                                * console do this when there's no input
-                                * buffers to deliver into.  Console also uses
-                                * it when it discovers that stdin is closed. */
-                               FD_CLR(i->fd, &devices.infds);
-                       }
-               }
-
-               /* Is this the timeout fd? */
-               if (FD_ISSET(timeoutpipe[0], &fds))
-                       handle_timeout(fd);
-       }
-}
-
 /*L:190
  * Device Setup
  *
@@ -1129,8 +1005,8 @@ static void handle_input(int fd)
 static u8 *device_config(const struct device *dev)
 {
        return (void *)(dev->desc + 1)
-               + dev->desc->num_vq * sizeof(struct lguest_vqconfig)
-               + dev->desc->feature_len * 2;
+               + dev->num_vq * sizeof(struct lguest_vqconfig)
+               + dev->feature_len * 2;
 }
 
 /* This routine allocates a new "struct lguest_device_desc" from descriptor
@@ -1159,7 +1035,7 @@ static struct lguest_device_desc *new_dev_desc(u16 type)
 /* Each device descriptor is followed by the description of its virtqueues.  We
  * specify how many descriptors the virtqueue is to have. */
 static void add_virtqueue(struct device *dev, unsigned int num_descs,
-                         void (*handle_output)(int, struct virtqueue *, bool))
+                         void (*service)(struct virtqueue *))
 {
        unsigned int pages;
        struct virtqueue **i, *vq = malloc(sizeof(*vq));
@@ -1174,8 +1050,8 @@ static void add_virtqueue(struct device *dev, unsigned int num_descs,
        vq->next = NULL;
        vq->last_avail_idx = 0;
        vq->dev = dev;
-       vq->inflight = 0;
-       vq->blocked = false;
+       vq->service = service;
+       vq->thread = (pid_t)-1;
 
        /* Initialize the configuration. */
        vq->config.num = num_descs;
@@ -1191,6 +1067,7 @@ static void add_virtqueue(struct device *dev, unsigned int num_descs,
         * yet, otherwise we'd be overwriting them. */
        assert(dev->desc->config_len == 0 && dev->desc->feature_len == 0);
        memcpy(device_config(dev), &vq->config, sizeof(vq->config));
+       dev->num_vq++;
        dev->desc->num_vq++;
 
        verbose("Virtqueue page %#lx\n", to_guest_phys(p));
@@ -1199,15 +1076,6 @@ static void add_virtqueue(struct device *dev, unsigned int num_descs,
         * second.  */
        for (i = &dev->vq; *i; i = &(*i)->next);
        *i = vq;
-
-       /* Set the routine to call when the Guest does something to this
-        * virtqueue. */
-       vq->handle_output = handle_output;
-
-       /* As an optimization, set the advisory "Don't Notify Me" flag if we
-        * don't have a handler */
-       if (!handle_output)
-               vq->vring.used->flags = VRING_USED_F_NO_NOTIFY;
 }
 
 /* The first half of the feature bitmask is for us to advertise features.  The
@@ -1219,7 +1087,7 @@ static void add_feature(struct device *dev, unsigned bit)
        /* We can't extend the feature bits once we've added config bytes */
        if (dev->desc->feature_len <= bit / CHAR_BIT) {
                assert(dev->desc->config_len == 0);
-               dev->desc->feature_len = (bit / CHAR_BIT) + 1;
+               dev->feature_len = dev->desc->feature_len = (bit/CHAR_BIT) + 1;
        }
 
        features[bit / CHAR_BIT] |= (1 << (bit % CHAR_BIT));
@@ -1243,22 +1111,17 @@ static void set_config(struct device *dev, unsigned len, const void *conf)
  * calling new_dev_desc() to allocate the descriptor and device memory.
  *
  * See what I mean about userspace being boring? */
-static struct device *new_device(const char *name, u16 type, int fd,
-                                bool (*handle_input)(int, struct device *))
+static struct device *new_device(const char *name, u16 type)
 {
        struct device *dev = malloc(sizeof(*dev));
 
        /* Now we populate the fields one at a time. */
-       dev->fd = fd;
-       /* If we have an input handler for this file descriptor, then we add it
-        * to the device_list's fdset and maxfd. */
-       if (handle_input)
-               add_device_fd(dev->fd);
        dev->desc = new_dev_desc(type);
-       dev->handle_input = handle_input;
        dev->name = name;
        dev->vq = NULL;
-       dev->ready = NULL;
+       dev->feature_len = 0;
+       dev->num_vq = 0;
+       dev->running = false;
 
        /* Append to device list.  Prepending to a single-linked list is
         * easier, but the user expects the devices to be arranged on the bus
@@ -1286,13 +1149,10 @@ static void setup_console(void)
                 * raw input stream to the Guest. */
                term.c_lflag &= ~(ISIG|ICANON|ECHO);
                tcsetattr(STDIN_FILENO, TCSANOW, &term);
-               /* If we exit gracefully, the original settings will be
-                * restored so the user can see what they're typing. */
-               atexit(restore_term);
        }
 
-       dev = new_device("console", VIRTIO_ID_CONSOLE,
-                        STDIN_FILENO, handle_console_input);
+       dev = new_device("console", VIRTIO_ID_CONSOLE);
+
        /* We store the console state in dev->priv, and initialize it. */
        dev->priv = malloc(sizeof(struct console_abort));
        ((struct console_abort *)dev->priv)->count = 0;
@@ -1301,31 +1161,13 @@ static void setup_console(void)
         * they put something the input queue, we make sure we're listening to
         * stdin.  When they put something in the output queue, we write it to
         * stdout. */
-       add_virtqueue(dev, VIRTQUEUE_NUM, enable_fd);
-       add_virtqueue(dev, VIRTQUEUE_NUM, handle_console_output);
+       add_virtqueue(dev, VIRTQUEUE_NUM, console_input);
+       add_virtqueue(dev, VIRTQUEUE_NUM, console_output);
 
-       verbose("device %u: console\n", devices.device_num++);
+       verbose("device %u: console\n", ++devices.device_num);
 }
 /*:*/
 
-static void timeout_alarm(int sig)
-{
-       write(timeoutpipe[1], "", 1);
-}
-
-static void setup_timeout(void)
-{
-       if (pipe(timeoutpipe) != 0)
-               err(1, "Creating timeout pipe");
-
-       if (fcntl(timeoutpipe[1], F_SETFL,
-                 fcntl(timeoutpipe[1], F_GETFL) | O_NONBLOCK) != 0)
-               err(1, "Making timeout pipe nonblocking");
-
-       add_device_fd(timeoutpipe[0]);
-       signal(SIGALRM, timeout_alarm);
-}
-
 /*M:010 Inter-guest networking is an interesting area.  Simplest is to have a
  * --sharenet=<name> option which opens or creates a named pipe.  This can be
  * used to send packets to another guest in a 1:1 manner.
@@ -1447,21 +1289,23 @@ static int get_tun_device(char tapif[IFNAMSIZ])
 static void setup_tun_net(char *arg)
 {
        struct device *dev;
-       int netfd, ipfd;
+       struct net_info *net_info = malloc(sizeof(*net_info));
+       int ipfd;
        u32 ip = INADDR_ANY;
        bool bridging = false;
        char tapif[IFNAMSIZ], *p;
        struct virtio_net_config conf;
 
-       netfd = get_tun_device(tapif);
+       net_info->tunfd = get_tun_device(tapif);
 
        /* First we create a new network device. */
-       dev = new_device("net", VIRTIO_ID_NET, netfd, handle_tun_input);
+       dev = new_device("net", VIRTIO_ID_NET);
+       dev->priv = net_info;
 
        /* Network devices need a receive and a send queue, just like
         * console. */
-       add_virtqueue(dev, VIRTQUEUE_NUM, net_enable_fd);
-       add_virtqueue(dev, VIRTQUEUE_NUM, handle_net_output);
+       add_virtqueue(dev, VIRTQUEUE_NUM, net_input);
+       add_virtqueue(dev, VIRTQUEUE_NUM, net_output);
 
        /* We need a socket to perform the magic network ioctls to bring up the
         * tap interface, connect to the bridge etc.  Any socket will do! */
@@ -1502,6 +1346,8 @@ static void setup_tun_net(char *arg)
        add_feature(dev, VIRTIO_NET_F_HOST_TSO4);
        add_feature(dev, VIRTIO_NET_F_HOST_TSO6);
        add_feature(dev, VIRTIO_NET_F_HOST_ECN);
+       /* We handle indirect ring entries */
+       add_feature(dev, VIRTIO_RING_F_INDIRECT_DESC);
        set_config(dev, sizeof(conf), &conf);
 
        /* We don't need the socket any more; setup is done. */
@@ -1550,20 +1396,18 @@ struct vblk_info
  * Remember that the block device is handled by a separate I/O thread.  We head
  * straight into the core of that thread here:
  */
-static bool service_io(struct device *dev)
+static void blk_request(struct virtqueue *vq)
 {
-       struct vblk_info *vblk = dev->priv;
+       struct vblk_info *vblk = vq->dev->priv;
        unsigned int head, out_num, in_num, wlen;
        int ret;
        u8 *in;
        struct virtio_blk_outhdr *out;
-       struct iovec iov[dev->vq->vring.num];
+       struct iovec iov[vq->vring.num];
        off64_t off;
 
-       /* See if there's a request waiting.  If not, nothing to do. */
-       head = get_vq_desc(dev->vq, iov, &out_num, &in_num);
-       if (head == dev->vq->vring.num)
-               return false;
+       /* Get the next request. */
+       head = wait_for_vq_desc(vq, iov, &out_num, &in_num);
 
        /* Every block request should contain at least one output buffer
         * (detailing the location on disk and the type of request) and one
@@ -1637,83 +1481,21 @@ static bool service_io(struct device *dev)
        if (out->type & VIRTIO_BLK_T_BARRIER)
                fdatasync(vblk->fd);
 
-       /* We can't trigger an IRQ, because we're not the Launcher.  It does
-        * that when we tell it we're done. */
-       add_used(dev->vq, head, wlen);
-       return true;
-}
-
-/* This is the thread which actually services the I/O. */
-static int io_thread(void *_dev)
-{
-       struct device *dev = _dev;
-       struct vblk_info *vblk = dev->priv;
-       char c;
-
-       /* Close other side of workpipe so we get 0 read when main dies. */
-       close(vblk->workpipe[1]);
-       /* Close the other side of the done_fd pipe. */
-       close(dev->fd);
-
-       /* When this read fails, it means Launcher died, so we follow. */
-       while (read(vblk->workpipe[0], &c, 1) == 1) {
-               /* We acknowledge each request immediately to reduce latency,
-                * rather than waiting until we've done them all.  I haven't
-                * measured to see if it makes any difference.
-                *
-                * That would be an interesting test, wouldn't it?  You could
-                * also try having more than one I/O thread. */
-               while (service_io(dev))
-                       write(vblk->done_fd, &c, 1);
-       }
-       return 0;
-}
-
-/* Now we've seen the I/O thread, we return to the Launcher to see what happens
- * when that thread tells us it's completed some I/O. */
-static bool handle_io_finish(int fd, struct device *dev)
-{
-       char c;
-
-       /* If the I/O thread died, presumably it printed the error, so we
-        * simply exit. */
-       if (read(dev->fd, &c, 1) != 1)
-               exit(1);
-
-       /* It did some work, so trigger the irq. */
-       trigger_irq(fd, dev->vq);
-       return true;
-}
-
-/* When the Guest submits some I/O, we just need to wake the I/O thread. */
-static void handle_virtblk_output(int fd, struct virtqueue *vq, bool timeout)
-{
-       struct vblk_info *vblk = vq->dev->priv;
-       char c = 0;
-
-       /* Wake up I/O thread and tell it to go to work! */
-       if (write(vblk->workpipe[1], &c, 1) != 1)
-               /* Presumably it indicated why it died. */
-               exit(1);
+       add_used(vq, head, wlen);
 }
 
 /*L:198 This actually sets up a virtual block device. */
 static void setup_block_file(const char *filename)
 {
-       int p[2];
        struct device *dev;
        struct vblk_info *vblk;
-       void *stack;
        struct virtio_blk_config conf;
 
-       /* This is the pipe the I/O thread will use to tell us I/O is done. */
-       pipe(p);
-
        /* The device responds to return from I/O thread. */
-       dev = new_device("block", VIRTIO_ID_BLOCK, p[0], handle_io_finish);
+       dev = new_device("block", VIRTIO_ID_BLOCK);
 
        /* The device has one virtqueue, where the Guest places requests. */
-       add_virtqueue(dev, VIRTQUEUE_NUM, handle_virtblk_output);
+       add_virtqueue(dev, VIRTQUEUE_NUM, blk_request);
 
        /* Allocate the room for our own bookkeeping */
        vblk = dev->priv = malloc(sizeof(*vblk));
@@ -1735,49 +1517,29 @@ static void setup_block_file(const char *filename)
 
        set_config(dev, sizeof(conf), &conf);
 
-       /* The I/O thread writes to this end of the pipe when done. */
-       vblk->done_fd = p[1];
-
-       /* This is the second pipe, which is how we tell the I/O thread about
-        * more work. */
-       pipe(vblk->workpipe);
-
-       /* Create stack for thread and run it.  Since stack grows upwards, we
-        * point the stack pointer to the end of this region. */
-       stack = malloc(32768);
-       /* SIGCHLD - We dont "wait" for our cloned thread, so prevent it from
-        * becoming a zombie. */
-       if (clone(io_thread, stack + 32768, CLONE_VM | SIGCHLD, dev) == -1)
-               err(1, "Creating clone");
-
-       /* We don't need to keep the I/O thread's end of the pipes open. */
-       close(vblk->done_fd);
-       close(vblk->workpipe[0]);
-
        verbose("device %u: virtblock %llu sectors\n",
-               devices.device_num, le64_to_cpu(conf.capacity));
+               ++devices.device_num, le64_to_cpu(conf.capacity));
 }
 
+struct rng_info {
+       int rfd;
+};
+
 /* Our random number generator device reads from /dev/random into the Guest's
  * input buffers.  The usual case is that the Guest doesn't want random numbers
  * and so has no buffers although /dev/random is still readable, whereas
  * console is the reverse.
  *
  * The same logic applies, however. */
-static bool handle_rng_input(int fd, struct device *dev)
+static void rng_input(struct virtqueue *vq)
 {
        int len;
        unsigned int head, in_num, out_num, totlen = 0;
-       struct iovec iov[dev->vq->vring.num];
+       struct rng_info *rng_info = vq->dev->priv;
+       struct iovec iov[vq->vring.num];
 
        /* First we need a buffer from the Guests's virtqueue. */
-       head = get_vq_desc(dev->vq, iov, &out_num, &in_num);
-
-       /* If they're not ready for input, stop listening to this file
-        * descriptor.  We'll start again once they add an input buffer. */
-       if (head == dev->vq->vring.num)
-               return false;
-
+       head = wait_for_vq_desc(vq, iov, &out_num, &in_num);
        if (out_num)
                errx(1, "Output buffers in rng?");
 
@@ -1785,7 +1547,7 @@ static bool handle_rng_input(int fd, struct device *dev)
         * it reads straight into the Guest's buffer.  We loop to make sure we
         * fill it. */
        while (!iov_empty(iov, in_num)) {
-               len = readv(dev->fd, iov, in_num);
+               len = readv(rng_info->rfd, iov, in_num);
                if (len <= 0)
                        err(1, "Read from /dev/random gave %i", len);
                iov_consume(iov, in_num, len);
@@ -1793,25 +1555,23 @@ static bool handle_rng_input(int fd, struct device *dev)
        }
 
        /* Tell the Guest about the new input. */
-       add_used_and_trigger(fd, dev->vq, head, totlen);
-
-       /* Everything went OK! */
-       return true;
+       add_used(vq, head, totlen);
 }
 
 /* And this creates a "hardware" random number device for the Guest. */
 static void setup_rng(void)
 {
        struct device *dev;
-       int fd;
+       struct rng_info *rng_info = malloc(sizeof(*rng_info));
 
-       fd = open_or_die("/dev/random", O_RDONLY);
+       rng_info->rfd = open_or_die("/dev/random", O_RDONLY);
 
        /* The device responds to return from I/O thread. */
-       dev = new_device("rng", VIRTIO_ID_RNG, fd, handle_rng_input);
+       dev = new_device("rng", VIRTIO_ID_RNG);
+       dev->priv = rng_info;
 
        /* The device has one virtqueue, where the Guest places inbufs. */
-       add_virtqueue(dev, VIRTQUEUE_NUM, enable_fd);
+       add_virtqueue(dev, VIRTQUEUE_NUM, rng_input);
 
        verbose("device %u: rng\n", devices.device_num++);
 }
@@ -1827,17 +1587,18 @@ static void __attribute__((noreturn)) restart_guest(void)
        for (i = 3; i < FD_SETSIZE; i++)
                close(i);
 
-       /* The exec automatically gets rid of the I/O and Waker threads. */
+       /* Reset all the devices (kills all threads). */
+       cleanup_devices();
+
        execv(main_args[0], main_args);
        err(1, "Could not exec %s", main_args[0]);
 }
 
 /*L:220 Finally we reach the core of the Launcher which runs the Guest, serves
  * its input and output, and finally, lays it to rest. */
-static void __attribute__((noreturn)) run_guest(int lguest_fd)
+static void __attribute__((noreturn)) run_guest(void)
 {
        for (;;) {
-               unsigned long args[] = { LHREQ_BREAK, 0 };
                unsigned long notify_addr;
                int readval;
 
@@ -1848,8 +1609,7 @@ static void __attribute__((noreturn)) run_guest(int lguest_fd)
                /* One unsigned long means the Guest did HCALL_NOTIFY */
                if (readval == sizeof(notify_addr)) {
                        verbose("Notify on address %#lx\n", notify_addr);
-                       handle_output(lguest_fd, notify_addr);
-                       continue;
+                       handle_output(notify_addr);
                /* ENOENT means the Guest died.  Reading tells us why. */
                } else if (errno == ENOENT) {
                        char reason[1024] = { 0 };
@@ -1858,19 +1618,9 @@ static void __attribute__((noreturn)) run_guest(int lguest_fd)
                /* ERESTART means that we need to reboot the guest */
                } else if (errno == ERESTART) {
                        restart_guest();
-               /* EAGAIN means a signal (timeout).
-                * Anything else means a bug or incompatible change. */
-               } else if (errno != EAGAIN)
+               /* Anything else means a bug or incompatible change. */
+               } else
                        err(1, "Running guest failed");
-
-               /* Only service input on thread for CPU 0. */
-               if (cpu_id != 0)
-                       continue;
-
-               /* Service input, then unset the BREAK to release the Waker. */
-               handle_input(lguest_fd);
-               if (pwrite(lguest_fd, args, sizeof(args), cpu_id) < 0)
-                       err(1, "Resetting break");
        }
 }
 /*L:240
@@ -1904,8 +1654,8 @@ int main(int argc, char *argv[])
        /* Memory, top-level pagetable, code startpoint and size of the
         * (optional) initrd. */
        unsigned long mem = 0, start, initrd_size = 0;
-       /* Two temporaries and the /dev/lguest file descriptor. */
-       int i, c, lguest_fd;
+       /* Two temporaries. */
+       int i, c;
        /* The boot information for the Guest. */
        struct boot_params *boot;
        /* If they specify an initrd file to load. */
@@ -1913,18 +1663,10 @@ int main(int argc, char *argv[])
 
        /* Save the args: we "reboot" by execing ourselves again. */
        main_args = argv;
-       /* We don't "wait" for the children, so prevent them from becoming
-        * zombies. */
-       signal(SIGCHLD, SIG_IGN);
 
-       /* First we initialize the device list.  Since console and network
-        * device receive input from a file descriptor, we keep an fdset
-        * (infds) and the maximum fd number (max_infd) with the head of the
-        * list.  We also keep a pointer to the last device.  Finally, we keep
-        * the next interrupt number to use for devices (1: remember that 0 is
-        * used by the timer). */
-       FD_ZERO(&devices.infds);
-       devices.max_infd = -1;
+       /* First we initialize the device list.  We keep a pointer to the last
+        * device, and the next interrupt number to use for devices (1:
+        * remember that 0 is used by the timer). */
        devices.lastdev = NULL;
        devices.next_irq = 1;
 
@@ -1982,9 +1724,6 @@ int main(int argc, char *argv[])
        /* We always have a console device */
        setup_console();
 
-       /* We can timeout waiting for Guest network transmit. */
-       setup_timeout();
-
        /* Now we load the kernel */
        start = load_kernel(open_or_die(argv[optind+1], O_RDONLY));
 
@@ -2023,15 +1762,16 @@ int main(int argc, char *argv[])
 
        /* We tell the kernel to initialize the Guest: this returns the open
         * /dev/lguest file descriptor. */
-       lguest_fd = tell_kernel(start);
+       tell_kernel(start);
+
+       /* Ensure that we terminate if a child dies. */
+       signal(SIGCHLD, kill_launcher);
 
-       /* We clone off a thread, which wakes the Launcher whenever one of the
-        * input file descriptors needs attention.  We call this the Waker, and
-        * we'll cover it in a moment. */
-       setup_waker(lguest_fd);
+       /* If we exit via err(), this kills all the threads, restores tty. */
+       atexit(cleanup_devices);
 
        /* Finally, run the Guest.  This doesn't return. */
-       run_guest(lguest_fd);
+       run_guest();
 }
 /*:*/
 
index 28c747362f958162e70c5b092fad444d22e7d12e..efb3a6a045a284a43593fb531d936736c939bbf3 100644 (file)
@@ -37,7 +37,6 @@ Running Lguest:
      "Paravirtualized guest support" = Y
         "Lguest guest support" = Y
      "High Memory Support" = off/4GB
-     "PAE (Physical Address Extension) Support" = N
      "Alignment value to which kernel should be aligned" = 0x100000
         (CONFIG_PARAVIRT=y, CONFIG_LGUEST_GUEST=y, CONFIG_HIGHMEM64G=n and
          CONFIG_PHYSICAL_ALIGN=0x100000)
index 421e7d00ffd03a81fa1573656b39d489b045cd23..c9abbd86bc187bd8dddf88c14f295babf708e2bb 100644 (file)
@@ -75,9 +75,6 @@ may need to apply in domain-specific ways to their devices:
 struct bus_type {
        ...
        int  (*suspend)(struct device *dev, pm_message_t state);
-       int  (*suspend_late)(struct device *dev, pm_message_t state);
-
-       int  (*resume_early)(struct device *dev);
        int  (*resume)(struct device *dev);
 };
 
@@ -226,20 +223,7 @@ The phases are seen by driver notifications issued in this order:
 
        This call should handle parts of device suspend logic that require
        sleeping.  It probably does work to quiesce the device which hasn't
-       been abstracted into class.suspend() or bus.suspend_late().
-
-   3   bus.suspend_late(dev, message) is called with IRQs disabled, and
-       with only one CPU active.  Until the bus.resume_early() phase
-       completes (see later), IRQs are not enabled again.  This method
-       won't be exposed by all busses; for message based busses like USB,
-       I2C, or SPI, device interactions normally require IRQs.  This bus
-       call may be morphed into a driver call with bus-specific parameters.
-
-       This call might save low level hardware state that might otherwise
-       be lost in the upcoming low power state, and actually put the
-       device into a low power state ... so that in some cases the device
-       may stay partly usable until this late.  This "late" call may also
-       help when coping with hardware that behaves badly.
+       been abstracted into class.suspend().
 
 The pm_message_t parameter is currently used to refine those semantics
 (described later).
@@ -351,19 +335,11 @@ devices processing each phase's calls before the next phase begins.
 
 The phases are seen by driver notifications issued in this order:
 
-   1   bus.resume_early(dev) is called with IRQs disabled, and with
-       only one CPU active.  As with bus.suspend_late(), this method
-       won't be supported on busses that require IRQs in order to
-       interact with devices.
-
-       This reverses the effects of bus.suspend_late().
-
-   2   bus.resume(dev) is called next.  This may be morphed into a device
-       driver call with bus-specific parameters; implementations may sleep.
-
-       This reverses the effects of bus.suspend().
+   1   bus.resume(dev) reverses the effects of bus.suspend().  This may
+       be morphed into a device driver call with bus-specific parameters;
+       implementations may sleep.
 
-   3   class.resume(dev) is called for devices associated with a class
+   2   class.resume(dev) is called for devices associated with a class
        that has such a method.  Implementations may sleep.
 
        This reverses the effects of class.suspend(), and would usually
index 012858d2b11935aa711e4cf9d2d8fbe637403813..5c08d96f407cb2afc95836ae987963afd371bc7e 100644 (file)
@@ -460,6 +460,25 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     The power-management is supported.
 
+  Module snd-ctxfi
+  ----------------
+
+    Module for Creative Sound Blaster X-Fi boards (20k1 / 20k2 chips)
+       * Creative Sound Blaster X-Fi Titanium Fatal1ty Champion Series
+       * Creative Sound Blaster X-Fi Titanium Fatal1ty Professional Series
+       * Creative Sound Blaster X-Fi Titanium Professional Audio
+       * Creative Sound Blaster X-Fi Titanium
+       * Creative Sound Blaster X-Fi Elite Pro
+       * Creative Sound Blaster X-Fi Platinum
+       * Creative Sound Blaster X-Fi Fatal1ty
+       * Creative Sound Blaster X-Fi XtremeGamer
+       * Creative Sound Blaster X-Fi XtremeMusic
+
+    reference_rate     - reference sample rate, 44100 or 48000 (default)
+    multiple           - multiple to ref. sample rate, 1 or 2 (default)
+
+    This module supports multiple cards.
+
   Module snd-darla20
   ------------------
 
@@ -925,6 +944,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
                        * Onkyo SE-90PCI
                        * Onkyo SE-200PCI
                        * ESI Juli@
+                       * ESI Maya44
                        * Hercules Fortissimo IV
                        * EGO-SYS WaveTerminal 192M
 
@@ -933,7 +953,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
                  prodigy71xt, prodigy71hifi, prodigyhd2, prodigy192,
                  juli, aureon51, aureon71, universe, ap192, k8x800,
                  phase22, phase28, ms300, av710, se200pci, se90pci,
-                 fortissimo4, sn25p, WT192M
+                 fortissimo4, sn25p, WT192M, maya44
 
     This module supports multiple cards and autoprobe.
 
@@ -1093,6 +1113,13 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     This module supports multiple cards.
     The driver requires the firmware loader support on kernel.
 
+  Module snd-lx6464es
+  -------------------
+
+    Module for Digigram LX6464ES boards
+
+    This module supports multiple cards.
+
   Module snd-maestro3
   -------------------
 
@@ -1543,13 +1570,15 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
   Module snd-sc6000
   -----------------
 
-    Module for Gallant SC-6000 soundcard.
+    Module for Gallant SC-6000 soundcard and later models: SC-6600
+    and SC-7000.
 
     port       - Port # (0x220 or 0x240)
     mss_port   - MSS Port # (0x530 or 0xe80)
     irq                - IRQ # (5,7,9,10,11)
     mpu_irq    - MPU-401 IRQ # (5,7,9,10) ,0 - no MPU-401 irq
     dma                - DMA # (1,3,0)
+    joystick   - Enable gameport - 0 = disable (default), 1 = enable
 
     This module supports multiple cards.
 
@@ -1859,7 +1888,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
   -------------------
 
     Module for sound cards based on the Asus AV100/AV200 chips,
-    i.e., Xonar D1, DX, D2, D2X, HDAV1.3 (Deluxe), and Essence STX.
+    i.e., Xonar D1, DX, D2, D2X, HDAV1.3 (Deluxe), Essence ST
+    (Deluxe) and Essence STX.
 
     This module supports autoprobe and multiple cards.
 
index 322869fc8a9e867e3b21073d64378c82c6e8aad7..de8e10a94103008a6f92df4ad49539a2b81d4178 100644 (file)
@@ -36,6 +36,7 @@ ALC260
   acer         Acer TravelMate
   will         Will laptops (PB V7900)
   replacer     Replacer 672V
+  favorit100   Maxdata Favorit 100XS
   basic                fixed pin assignment (old default model)
   test         for testing/debugging purpose, almost all controls can
                adjusted.  Appearing only when compiled with
@@ -85,10 +86,11 @@ ALC269
   eeepc-p703   ASUS Eeepc P703 P900A
   eeepc-p901   ASUS Eeepc P901 S101
   fujitsu      FSC Amilo
+  lifebook     Fujitsu Lifebook S6420
   auto         auto-config reading BIOS (default)
 
-ALC662/663
-==========
+ALC662/663/272
+==============
   3stack-dig   3-stack (2-channel) with SPDIF
   3stack-6ch    3-stack (6-channel)
   3stack-6ch-dig 3-stack (6-channel) with SPDIF
@@ -107,6 +109,9 @@ ALC662/663
   asus-mode4   ASUS
   asus-mode5   ASUS
   asus-mode6   ASUS
+  dell         Dell with ALC272
+  dell-zm1     Dell ZM1 with ALC272
+  samsung-nc10 Samsung NC10 mini notebook
   auto         auto-config reading BIOS (default)
 
 ALC882/885
@@ -118,6 +123,7 @@ ALC882/885
   asus-a7j     ASUS A7J
   asus-a7m     ASUS A7M
   macpro       MacPro support
+  mb5          Macbook 5,1
   mbp3         Macbook Pro rev3
   imac24       iMac 24'' with jack detection
   w2jc         ASUS W2JC
@@ -133,10 +139,12 @@ ALC883/888
   acer         Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc)
   acer-aspire  Acer Aspire 9810
   acer-aspire-4930g Acer Aspire 4930G
+  acer-aspire-8930g Acer Aspire 8930G
   medion       Medion Laptops
   medion-md2   Medion MD2
   targa-dig    Targa/MSI
-  targa-2ch-dig        Targs/MSI with 2-channel
+  targa-2ch-dig        Targa/MSI with 2-channel
+  targa-8ch-dig Targa/MSI with 8-channel (MSI GX620)
   laptop-eapd   3-jack with SPDIF I/O and EAPD (Clevo M540JE, M550JE)
   lenovo-101e  Lenovo 101E
   lenovo-nb0763        Lenovo NB0763
@@ -150,6 +158,9 @@ ALC883/888
   fujitsu-pi2515 Fujitsu AMILO Pi2515
   fujitsu-xa3530 Fujitsu AMILO XA3530
   3stack-6ch-intel Intel DG33* boards
+  asus-p5q     ASUS P5Q-EM boards
+  mb31         MacBook 3,1
+  sony-vaio-tt  Sony VAIO TT
   auto         auto-config reading BIOS (default)
 
 ALC861/660
@@ -348,6 +359,7 @@ STAC92HD71B*
   hp-m4                HP mini 1000
   hp-dv5       HP dv series
   hp-hdx       HP HDX series
+  hp-dv4-1222nr        HP dv4-1222nr (with LED support)
   auto         BIOS setup (default)
 
 STAC92HD73*
index cfac20cf9e33fa150a6cf69f4e6d624e8116caa9..381908d8ca4231dde74f5c138b200782036760c1 100644 (file)
@@ -88,26 +88,34 @@ card*/pcm*/info
        substreams, etc.
 
 card*/pcm*/xrun_debug
-       This file appears when CONFIG_SND_DEBUG=y.
-       This shows the status of xrun (= buffer overrun/xrun) debug of
-       ALSA PCM middle layer, as an integer from 0 to 2.  The value
-       can be changed by writing to this file, such as
-
-                # cat 2 > /proc/asound/card0/pcm0p/xrun_debug
-
-       When this value is greater than 0, the driver will show the
-       messages to kernel log when an xrun is detected.  The debug
-       message is shown also when the invalid H/W pointer is detected
-       at the update of periods (usually called from the interrupt
+       This file appears when CONFIG_SND_DEBUG=y and
+       CONFIG_PCM_XRUN_DEBUG=y.
+       This shows the status of xrun (= buffer overrun/xrun) and
+       invalid PCM position debug/check of ALSA PCM middle layer.
+       It takes an integer value, can be changed by writing to this
+       file, such as
+
+                # cat 5 > /proc/asound/card0/pcm0p/xrun_debug
+
+       The value consists of the following bit flags:
+         bit 0 = Enable XRUN/jiffies debug messages
+         bit 1 = Show stack trace at XRUN / jiffies check
+         bit 2 = Enable additional jiffies check
+
+       When the bit 0 is set, the driver will show the messages to
+       kernel log when an xrun is detected.  The debug message is
+       shown also when the invalid H/W pointer is detected at the
+       update of periods (usually called from the interrupt
        handler).
 
-       When this value is greater than 1, the driver will show the
-       stack trace additionally.  This may help the debugging.
+       When the bit 1 is set, the driver will show the stack trace
+       additionally.  This may help the debugging.
 
-       Since 2.6.30, this option also enables the hwptr check using
+       Since 2.6.30, this option can enable the hwptr check using
        jiffies.  This detects spontaneous invalid pointer callback
        values, but can be lead to too much corrections for a (mostly
        buggy) hardware that doesn't give smooth pointer updates.
+       This feature is enabled via the bit 2.
 
 card*/pcm*/sub*/info
        The general information of this PCM sub-stream.
diff --git a/Documentation/sound/alsa/README.maya44 b/Documentation/sound/alsa/README.maya44
new file mode 100644 (file)
index 0000000..0e41576
--- /dev/null
@@ -0,0 +1,163 @@
+NOTE: The following is the original document of Rainer's patch that the
+current maya44 code based on.  Some contents might be obsoleted, but I
+keep here as reference -- tiwai
+
+----------------------------------------------------------------
+STATE OF DEVELOPMENT:
+
+This driver is being developed on the initiative of Piotr Makowski (oponek@gmail.com) and financed by Lars Bergmann.
+Development is carried out by Rainer Zimmermann (mail@lightshed.de).
+
+ESI provided a sample Maya44 card for the development work.
+
+However, unfortunately it has turned out difficult to get detailed programming information, so I (Rainer Zimmermann) had to find out some card-specific information by experiment and conjecture. Some information (in particular, several GPIO bits) is still missing.
+
+This is the first testing version of the Maya44 driver released to the alsa-devel mailing list (Feb 5, 2008).
+
+
+The following functions work, as tested by Rainer Zimmermann and Piotr Makowski:
+
+- playback and capture at all sampling rates
+- input/output level
+- crossmixing
+- line/mic switch
+- phantom power switch
+- analogue monitor a.k.a bypass
+
+
+The following functions *should* work, but are not fully tested:
+
+- Channel 3+4 analogue - S/PDIF input switching
+- S/PDIF output
+- all inputs/outputs on the M/IO/DIO extension card
+- internal/external clock selection
+
+
+*In particular, we would appreciate testing of these functions by anyone who has access to an M/IO/DIO extension card.*
+
+
+Things that do not seem to work:
+
+- The level meters ("multi track") in 'alsamixer' do not seem to react to signals in (if this is a bug, it would probably be in the existing ICE1724 code).
+
+- Ardour 2.1 seems to work only via JACK, not using ALSA directly or via OSS. This still needs to be tracked down.
+
+
+DRIVER DETAILS:
+
+the following files were added:
+
+pci/ice1724/maya44.c        - Maya44 specific code
+pci/ice1724/maya44.h
+pci/ice1724/ice1724.patch
+pci/ice1724/ice1724.h.patch - PROPOSED patch to ice1724.h (see SAMPLING RATES)
+i2c/other/wm8776.c  - low-level access routines for Wolfson WM8776 codecs 
+include/wm8776.h
+
+
+Note that the wm8776.c code is meant to be card-independent and does not actually register the codec with the ALSA infrastructure.
+This is done in maya44.c, mainly because some of the WM8776 controls are used in Maya44-specific ways, and should be named appropriately.
+
+
+the following files were created in pci/ice1724, simply #including the corresponding file from the alsa-kernel tree:
+
+wtm.h
+vt1720_mobo.h
+revo.h
+prodigy192.h
+pontis.h
+phase.h
+maya44.h
+juli.h
+aureon.h
+amp.h
+envy24ht.h
+se.h
+prodigy_hifi.h
+
+
+*I hope this is the correct way to do things.*
+
+
+SAMPLING RATES:
+
+The Maya44 card (or more exactly, the Wolfson WM8776 codecs) allow a maximum sampling rate of 192 kHz for playback and 92 kHz for capture.
+
+As the ICE1724 chip only allows one global sampling rate, this is handled as follows:
+
+* setting the sampling rate on any open PCM device on the maya44 card will always set the *global* sampling rate for all playback and capture channels.
+
+* In the current state of the driver, setting rates of up to 192 kHz is permitted even for capture devices.
+
+*AVOID CAPTURING AT RATES ABOVE 96kHz*, even though it may appear to work. The codec cannot actually capture at such rates, meaning poor quality.
+
+
+I propose some additional code for limiting the sampling rate when setting on a capture pcm device. However because of the global sampling rate, this logic would be somewhat problematic.
+
+The proposed code (currently deactivated) is in ice1712.h.patch, ice1724.c and maya44.c (in pci/ice1712).
+
+
+SOUND DEVICES:
+
+PCM devices correspond to inputs/outputs as follows (assuming Maya44 is card #0):
+
+hw:0,0 input - stereo, analog input 1+2
+hw:0,0 output - stereo, analog output 1+2
+hw:0,1 input - stereo, analog input 3+4 OR S/PDIF input
+hw:0,1 output - stereo, analog output 3+4 (and SPDIF out)
+
+
+NAMING OF MIXER CONTROLS:
+
+(for more information about the signal flow, please refer to the block diagram on p.24 of the ESI Maya44 manual, or in the ESI windows software).
+
+
+PCM: (digital) output level for channel 1+2
+PCM 1: same for channel 3+4
+
+Mic Phantom+48V: switch for +48V phantom power for electrostatic microphones on input 1/2.
+    Make sure this is not turned on while any other source is connected to input 1/2.
+    It might damage the source and/or the maya44 card.
+
+Mic/Line input: if switch is is on, input jack 1/2 is microphone input (mono), otherwise line input (stereo).
+
+Bypass: analogue bypass from ADC input to output for channel 1+2. Same as "Monitor" in the windows driver.
+Bypass 1: same for channel 3+4.
+
+Crossmix: cross-mixer from channels 1+2 to channels 3+4
+Crossmix 1: cross-mixer from channels 3+4 to channels 1+2
+
+IEC958 Output: switch for S/PDIF output.
+    This is not supported by the ESI windows driver.
+    S/PDIF should output the same signal as channel 3+4. [untested!]
+
+
+Digitial output selectors:
+
+    These switches allow a direct digital routing from the ADCs to the DACs.
+    Each switch determines where the digital input data to one of the DACs comes from.
+    They are not supported by the ESI windows driver.
+    For normal operation, they should all be set to "PCM out".
+
+H/W: Output source channel 1
+H/W 1: Output source channel 2
+H/W 2: Output source channel 3
+H/W 3: Output source channel 4
+
+H/W 4 ... H/W 9: unknown function, left in to enable testing.
+    Possibly some of these control S/PDIF output(s).
+    If these turn out to be unused, they will go away in later driver versions.
+
+Selectable values for each of the digital output selectors are:
+   "PCM out" -> DAC output of the corresponding channel (default setting)
+   "Input 1"...
+   "Input 4" -> direct routing from ADC output of the selected input channel
+
+
+--------
+
+Feb 14, 2008
+Rainer Zimmermann
+mail@lightshed.de
+
index 9e6763264a2ee1cb5b549cc27c51f47557d3ba0d..9ac842be9b4fccff2c2841d1fd88ab4be321d467 100644 (file)
@@ -62,6 +62,7 @@ Audio DAPM widgets fall into a number of types:-
  o Mic        - Mic (and optional Jack)
  o Line       - Line Input/Output (and optional Jack)
  o Speaker    - Speaker
+ o Supply     - Power or clock supply widget used by other widgets.
  o Pre        - Special PRE widget (exec before all others)
  o Post       - Special POST widget (exec after all others)
 
index c944d618dc83d1a45c63b038e8f12429dde38a8c..90f81283b722d13068ccb246d3c05ea043569c7a 100644 (file)
@@ -3363,6 +3363,16 @@ F:       drivers/serial/kgdboc.c
 F:     include/linux/kgdb.h
 F:     kernel/kgdb.c
 
+KMEMLEAK
+P:     Catalin Marinas
+M:     catalin.marinas@arm.com
+L:     linux-kernel@vger.kernel.org
+S:     Maintained
+F:     Documentation/kmemleak.txt
+F:     include/linux/kmemleak.h
+F:     mm/kmemleak.c
+F:     mm/kmemleak-test.c
+
 KMEMTRACE
 P:     Eduard - Gabriel Munteanu
 M:     eduard.munteanu@linux360.ro
@@ -3372,12 +3382,6 @@ F:       Documentation/trace/kmemtrace.txt
 F:     include/trace/kmemtrace.h
 F:     kernel/trace/kmemtrace.c
 
-KMEMLEAK
-P:     Catalin Marinas
-M:     catalin.marinas@arm.com
-L:     linux-kernel@vger.kernel.org
-S:     Maintained
-
 KPROBES
 P:     Ananth N Mavinakayanahalli
 M:     ananth@in.ibm.com
@@ -4603,7 +4607,8 @@ F:        drivers/pcmcia/pxa2xx*
 F:     drivers/spi/pxa2xx*
 F:     drivers/usb/gadget/pxa2*
 F:     include/sound/pxa2xx-lib.h
-F:     sound/soc/pxa/pxa2xx*
+F:     sound/arm/pxa*
+F:     sound/soc/pxa
 
 PXA168 SUPPORT
 P:     Eric Miao
@@ -5331,11 +5336,12 @@ P:      Liam Girdwood
 M:     lrg@slimlogic.co.uk
 P:     Mark Brown
 M:     broonie@opensource.wolfsonmicro.com
-T:     git git://opensource.wolfsonmicro.com/linux-2.6-asoc
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git
 L:     alsa-devel@alsa-project.org (subscribers-only)
 W:     http://alsa-project.org/main/index.php/ASoC
 S:     Supported
 F:     sound/soc/
+F:     include/sound/soc*
 
 SPARC + UltraSPARC (sparc/sparc64)
 P:     David S. Miller
index 62b363584b2bda3761263d53a7cd17c30890debe..610dff44d94b9bfae101f76149f965b4400d07ff 100644 (file)
@@ -256,5 +256,5 @@ static __inline__ int atomic64_add_unless(atomic64_t *v, long a, long u)
 #define smp_mb__before_atomic_inc()    smp_mb()
 #define smp_mb__after_atomic_inc()     smp_mb()
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 #endif /* _ALPHA_ATOMIC_H */
diff --git a/arch/alpha/include/asm/bitsperlong.h b/arch/alpha/include/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..ad57f78
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __ASM_ALPHA_BITSPERLONG_H
+#define __ASM_ALPHA_BITSPERLONG_H
+
+#define __BITS_PER_LONG 64
+
+#include <asm-generic/bitsperlong.h>
+
+#endif /* __ASM_ALPHA_BITSPERLONG_H */
index 0995f9d13417241e283cad83bd4f0729af014c66..07af062544fb75fd68866e05d4983850863b0f45 100644 (file)
@@ -93,6 +93,6 @@ typedef struct page *pgtable_t;
                                         VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
 
 #include <asm-generic/memory_model.h>
-#include <asm-generic/page.h>
+#include <asm-generic/getorder.h>
 
 #endif /* _ALPHA_PAGE_H */
index 13c2305d35ef781b2f784ea553856c53c3ee28fc..a9388300abb13b75c2d9cdbbfc0f682ba2465e00 100644 (file)
@@ -111,7 +111,7 @@ typedef unsigned long sigset_t;
 #define SIG_UNBLOCK        2   /* for unblocking signals */
 #define SIG_SETMASK        3   /* for setting the signal mask */
 
-#include <asm-generic/signal.h>
+#include <asm-generic/signal-defs.h>
 
 #ifdef __KERNEL__
 struct osf_sigaction {
diff --git a/arch/alpha/include/asm/suspend.h b/arch/alpha/include/asm/suspend.h
deleted file mode 100644 (file)
index c7042d5..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ALPHA_SUSPEND_H
-#define __ALPHA_SUSPEND_H
-
-/* Dummy include. */
-
-#endif  /* __ALPHA_SUSPEND_H */
index f072f344497e7adba62719db66324bb11144c5ae..bd621ecd1eb33a21516af7ac6c3629b0f098e65b 100644 (file)
@@ -25,9 +25,6 @@ typedef unsigned int umode_t;
  * These aren't exported outside the kernel to avoid name space clashes
  */
 #ifdef __KERNEL__
-
-#define BITS_PER_LONG 64
-
 #ifndef __ASSEMBLY__
 
 typedef u64 dma_addr_t;
index 62dc379d301af9f1f00e824a2088eb487f77f39f..813c9b63c0e17de3577b5924b54072ad382244d9 100644 (file)
@@ -48,6 +48,27 @@ void sort_extable(struct exception_table_entry *start,
             cmp_ex, swap_ex);
 }
 
+#ifdef CONFIG_MODULES
+/*
+ * Any entry referring to the module init will be at the beginning or
+ * the end.
+ */
+void trim_init_extable(struct module *m)
+{
+       /*trim the beginning*/
+       while (m->num_exentries &&
+              within_module_init(ex_to_addr(&m->extable[0]), m)) {
+               m->extable++;
+               m->num_exentries--;
+       }
+       /*trim the end*/
+       while (m->num_exentries &&
+              within_module_init(ex_to_addr(&m->extable[m->num_exentries-1]),
+                                 m))
+               m->num_exentries--;
+}
+#endif /* CONFIG_MODULES */
+
 const struct exception_table_entry *
 search_extable(const struct exception_table_entry *first,
               const struct exception_table_entry *last,
index 16b52f397983f7ead8079769a772265cab71657f..9e07fe50702913f6324b21faa9dbee4640334b20 100644 (file)
@@ -249,6 +249,6 @@ static inline int atomic_add_unless(atomic_t *v, int a, int u)
 #define smp_mb__before_atomic_inc()    smp_mb()
 #define smp_mb__after_atomic_inc()     smp_mb()
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 #endif
 #endif
diff --git a/arch/arm/include/asm/bitsperlong.h b/arch/arm/include/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..6dc0bb0
--- /dev/null
@@ -0,0 +1 @@
+#include <asm-generic/bitsperlong.h>
index 54570d2e95b7b4856430550f75022853a966434a..fc26976d8e3a7090716e8f5624bee962ebcb283a 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef __ARM_MMAN_H__
 #define __ARM_MMAN_H__
 
-#include <asm-generic/mman.h>
+#include <asm-generic/mman-common.h>
 
 #define MAP_GROWSDOWN  0x0100          /* stack-like segment */
 #define MAP_DENYWRITE  0x0800          /* ETXTBSY */
index 7b522770f29dc6e967f3c719817925ebf753c6e1..be962c1349c4aa17a65f4cd364fd6221f1ea83bc 100644 (file)
@@ -202,6 +202,6 @@ typedef struct page *pgtable_t;
        (((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0) | \
         VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
 
-#include <asm-generic/page.h>
+#include <asm-generic/getorder.h>
 
 #endif
index d0fb487aba4f268811d96abc1a37cec2d3bbfdfd..43ba0fb1c8ad2b774e42041715c1276b226ba7bf 100644 (file)
@@ -111,7 +111,7 @@ typedef unsigned long sigset_t;
 #define MINSIGSTKSZ    2048
 #define SIGSTKSZ       8192
 
-#include <asm-generic/signal.h>
+#include <asm-generic/signal-defs.h>
 
 #ifdef __KERNEL__
 struct old_sigaction {
diff --git a/arch/arm/include/asm/suspend.h b/arch/arm/include/asm/suspend.h
deleted file mode 100644 (file)
index cf0d0bd..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#ifndef _ASMARM_SUSPEND_H
-#define _ASMARM_SUSPEND_H
-
-#endif
index 318815107748dc8ee335e8fc7d2fc47fab8b10c0..b131c27ddf5785883a1c28a3078cf6f1bd5a5c0e 100644 (file)
@@ -196,6 +196,6 @@ static inline int atomic_sub_if_positive(int i, atomic_t *v)
 #define smp_mb__before_atomic_inc()    barrier()
 #define smp_mb__after_atomic_inc()     barrier()
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 
 #endif /*  __ASM_AVR32_ATOMIC_H */
diff --git a/arch/avr32/include/asm/bitsperlong.h b/arch/avr32/include/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..6dc0bb0
--- /dev/null
@@ -0,0 +1 @@
+#include <asm-generic/bitsperlong.h>
index 648f91e7187a089a670ed5ba057524ff2439a334..9a92b15f6a66c19eeae0c9c42ecc964447608268 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef __ASM_AVR32_MMAN_H__
 #define __ASM_AVR32_MMAN_H__
 
-#include <asm-generic/mman.h>
+#include <asm-generic/mman-common.h>
 
 #define MAP_GROWSDOWN  0x0100          /* stack-like segment */
 #define MAP_DENYWRITE  0x0800          /* ETXTBSY */
index caffefeeba1fed9deccd9e7c51685e4bb69a8068..8790dfc10d5ba25353398a94cd93916ea1fe50a0 100644 (file)
@@ -112,7 +112,7 @@ typedef unsigned long sigset_t;
 #define MINSIGSTKSZ    2048
 #define SIGSTKSZ       8192
 
-#include <asm-generic/signal.h>
+#include <asm-generic/signal-defs.h>
 
 #ifdef __KERNEL__
 struct old_sigaction {
index 0152aba35154b5a8b53644e1466f30cd3748296e..dd7e9da2548894e8a749bb05196898efd629fe6a 100644 (file)
@@ -55,7 +55,7 @@ struct termio {
 */
 #define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0"
 
-#include <asm-generic/termios.h>
+#include <asm-generic/termios-base.h>
 
 #endif /* __KERNEL__ */
 
index 1167fe9cf6c4884c3448c01c1fbf037ce282af65..98f94d041d9c1dd212a0519efac60def8034b7db 100644 (file)
@@ -32,8 +32,6 @@ void module_free(struct module *mod, void *module_region)
        mod->arch.syminfo = NULL;
 
        vfree(module_region);
-       /* FIXME: if module_region == mod->init_region, trim exception
-        * table entries. */
 }
 
 static inline int check_rela(Elf32_Rela *rela, struct module *module,
index 3640cdc38aace3db670bbd703eb31959bd76cf05..c56fd3eb7c107462a9dd83d1dd4be7bf3806e25b 100644 (file)
@@ -223,6 +223,7 @@ endchoice
 
 config SMP
        depends on BF561
+       select GENERIC_TIME
        bool "Symmetric multi-processing support"
        ---help---
          This enables support for systems with more than one CPU,
@@ -241,12 +242,6 @@ config IRQ_PER_CPU
        depends on SMP
        default y
 
-config TICK_SOURCE_SYSTMR0
-       bool
-       select BFIN_GPTIMERS
-       depends on SMP
-       default y
-
 config BF_REV_MIN
        int
        default 0 if (BF51x || BF52x || (BF54x && !BF54xM))
@@ -263,8 +258,8 @@ config BF_REV_MAX
 
 choice
        prompt "Silicon Rev"
-       default BF_REV_0_1 if (BF51x || BF52x || (BF54x && !BF54xM))
-       default BF_REV_0_2 if (BF534 || BF536 || BF537)
+       default BF_REV_0_0 if (BF51x || BF52x)
+       default BF_REV_0_2 if (BF534 || BF536 || BF537 || (BF54x && !BF54xM))
        default BF_REV_0_3 if (BF531 || BF532 || BF533 || BF54xM || BF561)
 
 config BF_REV_0_0
@@ -607,7 +602,6 @@ source kernel/Kconfig.hz
 
 config GENERIC_TIME
        bool "Generic time"
-       depends on !SMP
        default y
 
 config GENERIC_CLOCKEVENTS
@@ -615,12 +609,26 @@ config GENERIC_CLOCKEVENTS
        depends on GENERIC_TIME
        default y
 
+choice
+       prompt "Kernel Tick Source"
+       depends on GENERIC_CLOCKEVENTS
+       default TICKSOURCE_CORETMR
+
+config TICKSOURCE_GPTMR0
+       bool "Gptimer0 (SCLK domain)"
+       select BFIN_GPTIMERS
+       depends on !IPIPE
+
+config TICKSOURCE_CORETMR
+       bool "Core timer (CCLK domain)"
+
+endchoice
+
 config CYCLES_CLOCKSOURCE
-       bool "Use 'CYCLES' as a clocksource (EXPERIMENTAL)"
-       depends on EXPERIMENTAL
+       bool "Use 'CYCLES' as a clocksource"
        depends on GENERIC_CLOCKEVENTS
        depends on !BFIN_SCRATCH_REG_CYCLES
-       default n
+       depends on !SMP
        help
          If you say Y here, you will enable support for using the 'cycles'
          registers as a clock source.  Doing so means you will be unable to
@@ -628,6 +636,11 @@ config CYCLES_CLOCKSOURCE
          still be able to read it (such as for performance monitoring), but
          writing the registers will most likely crash the kernel.
 
+config GPTMR0_CLOCKSOURCE
+       bool "Use GPTimer0 as a clocksource (higher rating)"
+       depends on GENERIC_CLOCKEVENTS
+       depends on !TICKSOURCE_GPTMR0
+
 source kernel/time/Kconfig
 
 comment "Misc"
@@ -808,7 +821,7 @@ config APP_STACK_L1
 config EXCEPTION_L1_SCRATCH
        bool "Locate exception stack in L1 Scratch Memory"
        default n
-       depends on !APP_STACK_L1 && !SYSCALL_TAB_L1
+       depends on !APP_STACK_L1
        help
          Whenever an exception occurs, use the L1 Scratch memory for
          stack storage.  You cannot place the stacks of FLAT binaries
@@ -901,7 +914,7 @@ config BFIN_ICACHE_LOCK
        bool "Enable Instruction Cache Locking"
 
 choice
-       prompt "Policy"
+       prompt "External memory cache policy"
        depends on BFIN_DCACHE
        default BFIN_WB if !SMP
        default BFIN_WT if SMP
@@ -942,12 +955,22 @@ config BFIN_WT
 
 endchoice
 
-config BFIN_L2_CACHEABLE
-       bool "Cache L2 SRAM"
-       depends on (BFIN_DCACHE || BFIN_ICACHE) && (BF54x || (BF561 && !SMP))
-       default n
-       help
-         Select to make L2 SRAM cacheable in L1 data and instruction cache.
+choice
+       prompt "L2 SRAM cache policy"
+       depends on (BF54x || BF561)
+       default BFIN_L2_WT
+config BFIN_L2_WB
+       bool "Write back"
+       depends on !SMP
+
+config BFIN_L2_WT
+       bool "Write through"
+       depends on !SMP
+
+config BFIN_L2_NOT_CACHED
+       bool "Not cached"
+
+endchoice
 
 config MPU
        bool "Enable the memory protection unit (EXPERIMENTAL)"
@@ -1011,21 +1034,34 @@ endmenu
 
 menu "EBIU_AMBCTL Control"
 config BANK_0
-       hex "Bank 0"
+       hex "Bank 0 (AMBCTL0.L)"
        default 0x7BB0
+       help
+         These are the low 16 bits of the EBIU_AMBCTL0 MMR which are
+         used to control the Asynchronous Memory Bank 0 settings.
 
 config BANK_1
-       hex "Bank 1"
+       hex "Bank 1 (AMBCTL0.H)"
        default 0x7BB0
        default 0x5558 if BF54x
+       help
+         These are the high 16 bits of the EBIU_AMBCTL0 MMR which are
+         used to control the Asynchronous Memory Bank 1 settings.
 
 config BANK_2
-       hex "Bank 2"
+       hex "Bank 2 (AMBCTL1.L)"
        default 0x7BB0
+       help
+         These are the low 16 bits of the EBIU_AMBCTL1 MMR which are
+         used to control the Asynchronous Memory Bank 2 settings.
 
 config BANK_3
-       hex "Bank 3"
+       hex "Bank 3 (AMBCTL1.H)"
        default 0x99B3
+       help
+         These are the high 16 bits of the EBIU_AMBCTL1 MMR which are
+         used to control the Asynchronous Memory Bank 3 settings.
+
 endmenu
 
 config EBIU_MBSCTLVAL
index 79e7e63ab70985ddea291a2656052dcd6f8a2c5c..1fc4981d486f1371ebbf230ead0eeafaba6fe6b2 100644 (file)
@@ -54,6 +54,19 @@ config DEBUG_HWERR
          hardware error interrupts and need to know where they are coming
          from.
 
+config EXACT_HWERR
+       bool "Try to make Hardware errors exact"
+       depends on DEBUG_HWERR
+       help
+         By default, the Blackfin hardware errors are not exact - the error
+          be reported multiple cycles after the error happens. This delay
+         can cause the wrong application, or even the kernel to receive a
+         signal to be killed. If you are getting HW errors in your system,
+         try turning this on to ensure they are at least comming from the
+         proper thread.
+
+         On production systems, it is safe (and a small optimization) to say N.
+
 config DEBUG_DOUBLEFAULT
        bool "Debug Double Faults"
        default n
index c121d6e6e2b8d0bbe98860ef50e092b4d5492bf0..baec1337f2823c15223a92df63602024f64114c4 100644 (file)
@@ -1,6 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.28.7
+# Linux kernel version: 2.6.28.10
+# Thu May 21 05:50:01 2009
 #
 # CONFIG_MMU is not set
 # CONFIG_FPU is not set
@@ -42,10 +43,11 @@ CONFIG_LOG_BUF_SHIFT=14
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_INITRAMFS_SOURCE=""
 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
-# CONFIG_SYSCTL is not set
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
 CONFIG_EMBEDDED=y
 CONFIG_UID16=y
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_ALL is not set
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
@@ -53,16 +55,15 @@ CONFIG_HOTPLUG=y
 CONFIG_PRINTK=y
 CONFIG_BUG=y
 # CONFIG_ELF_CORE is not set
-CONFIG_COMPAT_BRK=y
 CONFIG_BASE_FULL=y
 # CONFIG_FUTEX is not set
-CONFIG_ANON_INODES=y
 CONFIG_EPOLL=y
-CONFIG_SIGNALFD=y
-CONFIG_TIMERFD=y
-CONFIG_EVENTFD=y
+# CONFIG_SIGNALFD is not set
+# CONFIG_TIMERFD is not set
+# CONFIG_EVENTFD is not set
 # CONFIG_AIO is not set
 CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_COMPAT_BRK=y
 CONFIG_SLAB=y
 # CONFIG_SLUB is not set
 # CONFIG_SLOB is not set
@@ -71,7 +72,6 @@ CONFIG_SLAB=y
 CONFIG_HAVE_OPROFILE=y
 # CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
 CONFIG_SLABINFO=y
-CONFIG_RT_MUTEXES=y
 CONFIG_TINY_SHMEM=y
 CONFIG_BASE_SMALL=0
 CONFIG_MODULES=y
@@ -259,7 +259,10 @@ CONFIG_HZ=250
 # CONFIG_SCHED_HRTICK is not set
 CONFIG_GENERIC_TIME=y
 CONFIG_GENERIC_CLOCKEVENTS=y
+# CONFIG_TICKSOURCE_GPTMR0 is not set
+CONFIG_TICKSOURCE_CORETMR=y
 # CONFIG_CYCLES_CLOCKSOURCE is not set
+# CONFIG_GPTMR0_CLOCKSOURCE is not set
 # CONFIG_NO_HZ is not set
 # CONFIG_HIGH_RES_TIMERS is not set
 CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
@@ -404,7 +407,7 @@ CONFIG_IP_PNP=y
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_ARPD is not set
-CONFIG_SYN_COOKIES=y
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_AH is not set
 # CONFIG_INET_ESP is not set
 # CONFIG_INET_IPCOMP is not set
@@ -688,14 +691,14 @@ CONFIG_INPUT_MISC=y
 # Character devices
 #
 # CONFIG_AD9960 is not set
-# CONFIG_SPI_ADC_BF533 is not set
-# CONFIG_BF5xx_PPIFCD is not set
+CONFIG_BFIN_DMA_INTERFACE=m
+# CONFIG_BFIN_PPI is not set
+# CONFIG_BFIN_PPIFCD is not set
 # CONFIG_BFIN_SIMPLE_TIMER is not set
-# CONFIG_BF5xx_PPI is not set
+# CONFIG_BFIN_SPI_ADC is not set
 # CONFIG_BFIN_SPORT is not set
 # CONFIG_BFIN_TIMER_LATENCY is not set
-# CONFIG_TWI_LCD is not set
-CONFIG_BFIN_DMA_INTERFACE=m
+# CONFIG_BFIN_TWI_LCD is not set
 CONFIG_SIMPLE_GPIO=m
 CONFIG_VT=y
 CONFIG_CONSOLE_TRANSLATIONS=y
@@ -802,7 +805,30 @@ CONFIG_SPI_BFIN=y
 # CONFIG_SPI_SPIDEV is not set
 # CONFIG_SPI_TLE62X0 is not set
 CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
-# CONFIG_GPIOLIB is not set
+CONFIG_GPIOLIB=y
+# CONFIG_DEBUG_GPIO is not set
+CONFIG_GPIO_SYSFS=y
+
+#
+# Memory mapped GPIO expanders:
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
 # CONFIG_W1 is not set
 # CONFIG_POWER_SUPPLY is not set
 # CONFIG_HWMON is not set
@@ -831,6 +857,7 @@ CONFIG_SSB_POSSIBLE=y
 # CONFIG_HTC_PASIC3 is not set
 # CONFIG_MFD_TMIO is not set
 # CONFIG_PMIC_DA903X is not set
+# CONFIG_PMIC_ADP5520 is not set
 # CONFIG_MFD_WM8400 is not set
 # CONFIG_MFD_WM8350_I2C is not set
 # CONFIG_REGULATOR is not set
@@ -962,7 +989,8 @@ CONFIG_RTC_DRV_BFIN=y
 #
 # File systems
 #
-# CONFIG_EXT2_FS is not set
+CONFIG_EXT2_FS=m
+# CONFIG_EXT2_FS_XATTR is not set
 # CONFIG_EXT3_FS is not set
 # CONFIG_EXT4_FS is not set
 # CONFIG_REISERFS_FS is not set
@@ -988,8 +1016,11 @@ CONFIG_INOTIFY_USER=y
 #
 # DOS/FAT/NT Filesystems
 #
+CONFIG_FAT_FS=m
 # CONFIG_MSDOS_FS is not set
-# CONFIG_VFAT_FS is not set
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
 # CONFIG_NTFS_FS is not set
 
 #
@@ -1012,8 +1043,8 @@ CONFIG_SYSFS=y
 # CONFIG_BEFS_FS is not set
 # CONFIG_BFS_FS is not set
 # CONFIG_EFS_FS is not set
-# CONFIG_YAFFS_FS is not set
 # CONFIG_JFFS2_FS is not set
+# CONFIG_YAFFS_FS is not set
 # CONFIG_CRAMFS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_MINIX_FS is not set
@@ -1048,9 +1079,9 @@ CONFIG_SMB_FS=m
 #
 # CONFIG_PARTITION_ADVANCED is not set
 CONFIG_MSDOS_PARTITION=y
-CONFIG_NLS=y
+CONFIG_NLS=m
 CONFIG_NLS_DEFAULT="iso8859-1"
-CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_437=m
 # CONFIG_NLS_CODEPAGE_737 is not set
 # CONFIG_NLS_CODEPAGE_775 is not set
 # CONFIG_NLS_CODEPAGE_850 is not set
@@ -1065,7 +1096,7 @@ CONFIG_NLS_CODEPAGE_437=y
 # 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_936=m
 # CONFIG_NLS_CODEPAGE_950 is not set
 # CONFIG_NLS_CODEPAGE_932 is not set
 # CONFIG_NLS_CODEPAGE_949 is not set
@@ -1074,7 +1105,7 @@ CONFIG_NLS_CODEPAGE_437=y
 # 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_1=m
 # CONFIG_NLS_ISO8859_2 is not set
 # CONFIG_NLS_ISO8859_3 is not set
 # CONFIG_NLS_ISO8859_4 is not set
@@ -1087,7 +1118,7 @@ CONFIG_NLS_ISO8859_1=y
 # CONFIG_NLS_ISO8859_15 is not set
 # CONFIG_NLS_KOI8_R is not set
 # CONFIG_NLS_KOI8_U is not set
-# CONFIG_NLS_UTF8 is not set
+CONFIG_NLS_UTF8=m
 # CONFIG_DLM is not set
 
 #
@@ -1102,7 +1133,7 @@ CONFIG_FRAME_WARN=1024
 CONFIG_DEBUG_FS=y
 # CONFIG_HEADERS_CHECK is not set
 CONFIG_DEBUG_KERNEL=y
-# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_DEBUG_SHIRQ=y
 CONFIG_DETECT_SOFTLOCKUP=y
 # CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
 CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
@@ -1111,8 +1142,6 @@ CONFIG_SCHED_DEBUG=y
 # CONFIG_TIMER_STATS is not set
 # CONFIG_DEBUG_OBJECTS is not set
 # CONFIG_DEBUG_SLAB is not set
-# CONFIG_DEBUG_RT_MUTEXES is not set
-# CONFIG_RT_MUTEX_TESTER is not set
 # CONFIG_DEBUG_SPINLOCK is not set
 # CONFIG_DEBUG_MUTEXES is not set
 # CONFIG_DEBUG_SPINLOCK_SLEEP is not set
@@ -1132,7 +1161,6 @@ CONFIG_DEBUG_INFO=y
 # CONFIG_BACKTRACE_SELF_TEST is not set
 # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
 # CONFIG_FAULT_INJECTION is not set
-CONFIG_SYSCTL_SYSCALL_CHECK=y
 
 #
 # Tracers
@@ -1148,16 +1176,20 @@ CONFIG_HAVE_ARCH_KGDB=y
 # CONFIG_DEBUG_STACK_USAGE is not set
 CONFIG_DEBUG_VERBOSE=y
 CONFIG_DEBUG_MMRS=y
-# CONFIG_DEBUG_HWERR is not set
-# CONFIG_DEBUG_DOUBLEFAULT is not set
+CONFIG_DEBUG_HWERR=y
+CONFIG_EXACT_HWERR=y
+CONFIG_DEBUG_DOUBLEFAULT=y
+CONFIG_DEBUG_DOUBLEFAULT_PRINT=y
+# CONFIG_DEBUG_DOUBLEFAULT_RESET is not set
+# CONFIG_DEBUG_ICACHE_CHECK is not set
 CONFIG_DEBUG_HUNT_FOR_ZERO=y
 CONFIG_DEBUG_BFIN_HWTRACE_ON=y
-CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF=y
-# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_ONE is not set
+# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF is not set
+CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_ONE=y
 # CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_TWO is not set
-CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION=0
+CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION=1
 # CONFIG_DEBUG_BFIN_HWTRACE_EXPAND is not set
-# CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE is not set
+CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE=y
 CONFIG_EARLY_PRINTK=y
 CONFIG_CPLB_INFO=y
 CONFIG_ACCESS_CHECK=y
@@ -1269,7 +1301,6 @@ CONFIG_CRC32=y
 # CONFIG_CRC7 is not set
 # CONFIG_LIBCRC32C is not set
 CONFIG_ZLIB_INFLATE=y
-CONFIG_PLIST=y
 CONFIG_HAS_IOMEM=y
 CONFIG_HAS_IOPORT=y
 CONFIG_HAS_DMA=y
index 3e562b2775d4b5221b839cca0629bd933f4b21ca..c06262e41f7c9058b8a61a8cf2dff01ae215d3f2 100644 (file)
@@ -1,6 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.28.7
+# Linux kernel version: 2.6.28.10
+# Thu May 21 05:50:01 2009
 #
 # CONFIG_MMU is not set
 # CONFIG_FPU is not set
@@ -42,10 +43,11 @@ CONFIG_LOG_BUF_SHIFT=14
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_INITRAMFS_SOURCE=""
 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
-# CONFIG_SYSCTL is not set
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
 CONFIG_EMBEDDED=y
 CONFIG_UID16=y
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_ALL is not set
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
@@ -53,16 +55,15 @@ CONFIG_HOTPLUG=y
 CONFIG_PRINTK=y
 CONFIG_BUG=y
 # CONFIG_ELF_CORE is not set
-CONFIG_COMPAT_BRK=y
 CONFIG_BASE_FULL=y
 # CONFIG_FUTEX is not set
-CONFIG_ANON_INODES=y
 CONFIG_EPOLL=y
-CONFIG_SIGNALFD=y
-CONFIG_TIMERFD=y
-CONFIG_EVENTFD=y
+# CONFIG_SIGNALFD is not set
+# CONFIG_TIMERFD is not set
+# CONFIG_EVENTFD is not set
 # CONFIG_AIO is not set
 CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_COMPAT_BRK=y
 CONFIG_SLAB=y
 # CONFIG_SLUB is not set
 # CONFIG_SLOB is not set
@@ -71,7 +72,6 @@ CONFIG_SLAB=y
 CONFIG_HAVE_OPROFILE=y
 # CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
 CONFIG_SLABINFO=y
-CONFIG_RT_MUTEXES=y
 CONFIG_TINY_SHMEM=y
 CONFIG_BASE_SMALL=0
 CONFIG_MODULES=y
@@ -144,8 +144,8 @@ CONFIG_BF526=y
 # CONFIG_BF561 is not set
 CONFIG_BF_REV_MIN=0
 CONFIG_BF_REV_MAX=2
-# CONFIG_BF_REV_0_0 is not set
-CONFIG_BF_REV_0_1=y
+CONFIG_BF_REV_0_0=y
+# CONFIG_BF_REV_0_1 is not set
 # CONFIG_BF_REV_0_2 is not set
 # CONFIG_BF_REV_0_3 is not set
 # CONFIG_BF_REV_0_4 is not set
@@ -264,7 +264,10 @@ CONFIG_HZ=250
 # CONFIG_SCHED_HRTICK is not set
 CONFIG_GENERIC_TIME=y
 CONFIG_GENERIC_CLOCKEVENTS=y
+# CONFIG_TICKSOURCE_GPTMR0 is not set
+CONFIG_TICKSOURCE_CORETMR=y
 # CONFIG_CYCLES_CLOCKSOURCE is not set
+# CONFIG_GPTMR0_CLOCKSOURCE is not set
 # CONFIG_NO_HZ is not set
 # CONFIG_HIGH_RES_TIMERS is not set
 CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
@@ -409,7 +412,7 @@ CONFIG_IP_PNP=y
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_ARPD is not set
-CONFIG_SYN_COOKIES=y
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_AH is not set
 # CONFIG_INET_ESP is not set
 # CONFIG_INET_IPCOMP is not set
@@ -491,7 +494,7 @@ CONFIG_MTD_PARTITIONS=y
 #
 # User Modules And Translation Layers
 #
-CONFIG_MTD_CHAR=m
+CONFIG_MTD_CHAR=y
 CONFIG_MTD_BLKDEVS=y
 CONFIG_MTD_BLOCK=y
 # CONFIG_FTL is not set
@@ -504,9 +507,9 @@ CONFIG_MTD_BLOCK=y
 #
 # RAM/ROM/Flash chip drivers
 #
-# CONFIG_MTD_CFI is not set
-CONFIG_MTD_JEDECPROBE=m
-CONFIG_MTD_GEN_PROBE=m
+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
@@ -518,9 +521,10 @@ 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_INTELEXT=y
 # CONFIG_MTD_CFI_AMDSTD is not set
 # CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
 CONFIG_MTD_RAM=y
 CONFIG_MTD_ROM=m
 # CONFIG_MTD_ABSENT is not set
@@ -529,7 +533,8 @@ CONFIG_MTD_ROM=m
 # Mapping drivers for chip access
 #
 CONFIG_MTD_COMPLEX_MAPPINGS=y
-# CONFIG_MTD_PHYSMAP is not set
+CONFIG_MTD_PHYSMAP=y
+# CONFIG_MTD_PHYSMAP_COMPAT is not set
 # CONFIG_MTD_GPIO_ADDR is not set
 # CONFIG_MTD_UCLINUX is not set
 # CONFIG_MTD_PLATRAM is not set
@@ -597,9 +602,42 @@ CONFIG_HAVE_IDE=y
 # SCSI device support
 #
 # CONFIG_RAID_ATTRS is not set
-# CONFIG_SCSI is not set
-# CONFIG_SCSI_DMA is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
 # CONFIG_SCSI_NETLINK is not set
+# CONFIG_SCSI_PROC_FS is not set
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=m
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+# CONFIG_SCSI_LOWLEVEL is not set
+# CONFIG_SCSI_DH is not set
 # CONFIG_ATA is not set
 # CONFIG_MD is not set
 CONFIG_NETDEVICES=y
@@ -644,9 +682,8 @@ CONFIG_BFIN_MAC_RMII=y
 # CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
 # CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
 # CONFIG_B44 is not set
-CONFIG_NETDEV_1000=y
-# CONFIG_AX88180 is not set
-CONFIG_NETDEV_10000=y
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
 
 #
 # Wireless LAN
@@ -715,14 +752,14 @@ CONFIG_INPUT_MISC=y
 # Character devices
 #
 # CONFIG_AD9960 is not set
-# CONFIG_SPI_ADC_BF533 is not set
-# CONFIG_BF5xx_PPIFCD is not set
+CONFIG_BFIN_DMA_INTERFACE=m
+# CONFIG_BFIN_PPI is not set
+# CONFIG_BFIN_PPIFCD is not set
 # CONFIG_BFIN_SIMPLE_TIMER is not set
-# CONFIG_BF5xx_PPI is not set
+# CONFIG_BFIN_SPI_ADC is not set
 # CONFIG_BFIN_SPORT is not set
 # CONFIG_BFIN_TIMER_LATENCY is not set
-# CONFIG_TWI_LCD is not set
-CONFIG_BFIN_DMA_INTERFACE=m
+# CONFIG_BFIN_TWI_LCD is not set
 CONFIG_SIMPLE_GPIO=m
 CONFIG_VT=y
 CONFIG_CONSOLE_TRANSLATIONS=y
@@ -832,11 +869,35 @@ CONFIG_SPI_BFIN=y
 # CONFIG_SPI_SPIDEV is not set
 # CONFIG_SPI_TLE62X0 is not set
 CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
-# CONFIG_GPIOLIB is not set
+CONFIG_GPIOLIB=y
+# CONFIG_DEBUG_GPIO is not set
+CONFIG_GPIO_SYSFS=y
+
+#
+# Memory mapped GPIO expanders:
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
 # CONFIG_W1 is not set
 # CONFIG_POWER_SUPPLY is not set
 CONFIG_HWMON=y
 # CONFIG_HWMON_VID is not set
+# CONFIG_SENSORS_AD5252 is not set
 # CONFIG_SENSORS_AD7414 is not set
 # CONFIG_SENSORS_AD7418 is not set
 # CONFIG_SENSORS_ADCXX is not set
@@ -920,6 +981,7 @@ CONFIG_SSB_POSSIBLE=y
 # CONFIG_HTC_PASIC3 is not set
 # CONFIG_MFD_TMIO is not set
 # CONFIG_PMIC_DA903X is not set
+# CONFIG_PMIC_ADP5520 is not set
 # CONFIG_MFD_WM8400 is not set
 # CONFIG_MFD_WM8350_I2C is not set
 # CONFIG_REGULATOR is not set
@@ -1008,8 +1070,8 @@ CONFIG_USB=y
 #
 # Miscellaneous USB options
 #
-# CONFIG_USB_DEVICEFS is not set
-CONFIG_USB_DEVICE_CLASS=y
+CONFIG_USB_DEVICEFS=y
+# CONFIG_USB_DEVICE_CLASS is not set
 # CONFIG_USB_DYNAMIC_MINORS is not set
 # CONFIG_USB_OTG is not set
 # CONFIG_USB_OTG_WHITELIST is not set
@@ -1037,10 +1099,10 @@ CONFIG_USB_MUSB_SOC=y
 CONFIG_USB_MUSB_HOST=y
 # CONFIG_USB_MUSB_PERIPHERAL is not set
 # CONFIG_USB_MUSB_OTG is not set
-# CONFIG_USB_GADGET_MUSB_HDRC is not set
 CONFIG_USB_MUSB_HDRC_HCD=y
-CONFIG_MUSB_PIO_ONLY=y
-CONFIG_MUSB_DMA_POLL=y
+# CONFIG_MUSB_PIO_ONLY is not set
+CONFIG_USB_INVENTRA_DMA=y
+# CONFIG_USB_TI_CPPI_DMA is not set
 # CONFIG_USB_MUSB_DEBUG is not set
 
 #
@@ -1058,7 +1120,7 @@ CONFIG_MUSB_DMA_POLL=y
 #
 # see USB_STORAGE Help for more information
 #
-CONFIG_USB_STORAGE=m
+CONFIG_USB_STORAGE=y
 # CONFIG_USB_STORAGE_DEBUG is not set
 # CONFIG_USB_STORAGE_DATAFAB is not set
 # CONFIG_USB_STORAGE_FREECOM is not set
@@ -1107,33 +1169,10 @@ CONFIG_USB_STORAGE=m
 # CONFIG_USB_LD is not set
 # CONFIG_USB_TRANCEVIBRATOR is not set
 # CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
 # CONFIG_USB_ISIGHTFW is not set
 # CONFIG_USB_VST is not set
 # CONFIG_USB_GADGET is not set
-# CONFIG_USB_GADGET_AT91 is not set
-# CONFIG_USB_GADGET_ATMEL_USBA is not set
-# CONFIG_USB_GADGET_FSL_USB2 is not set
-# CONFIG_USB_GADGET_LH7A40X is not set
-# CONFIG_USB_GADGET_OMAP is not set
-# CONFIG_USB_GADGET_PXA25X is not set
-# CONFIG_USB_GADGET_PXA27X is not set
-# CONFIG_USB_GADGET_S3C2410 is not set
-# CONFIG_USB_GADGET_M66592 is not set
-# CONFIG_USB_GADGET_AMD5536UDC is not set
-# CONFIG_USB_GADGET_FSL_QE is not set
-# CONFIG_USB_GADGET_NET2272 is not set
-# CONFIG_USB_GADGET_NET2280 is not set
-# CONFIG_USB_GADGET_GOKU is not set
-# CONFIG_USB_GADGET_DUMMY_HCD is not set
-# CONFIG_USB_ZERO is not set
-# CONFIG_USB_AUDIO is not set
-# CONFIG_USB_ETH is not set
-# CONFIG_USB_GADGETFS is not set
-# CONFIG_USB_FILE_STORAGE is not set
-# CONFIG_USB_G_SERIAL is not set
-# CONFIG_USB_MIDI_GADGET is not set
-# CONFIG_USB_G_PRINTER is not set
-# CONFIG_USB_CDC_COMPOSITE is not set
 # CONFIG_MMC is not set
 # CONFIG_MEMSTICK is not set
 # CONFIG_NEW_LEDS is not set
@@ -1206,7 +1245,8 @@ CONFIG_RTC_DRV_BFIN=y
 #
 # File systems
 #
-# CONFIG_EXT2_FS is not set
+CONFIG_EXT2_FS=m
+# CONFIG_EXT2_FS_XATTR is not set
 # CONFIG_EXT3_FS is not set
 # CONFIG_EXT4_FS is not set
 # CONFIG_REISERFS_FS is not set
@@ -1226,14 +1266,19 @@ CONFIG_INOTIFY_USER=y
 #
 # CD-ROM/DVD Filesystems
 #
-# CONFIG_ISO9660_FS is not set
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+# CONFIG_ZISOFS is not set
 # CONFIG_UDF_FS is not set
 
 #
 # DOS/FAT/NT Filesystems
 #
+CONFIG_FAT_FS=m
 # CONFIG_MSDOS_FS is not set
-# CONFIG_VFAT_FS is not set
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
 # CONFIG_NTFS_FS is not set
 
 #
@@ -1256,16 +1301,6 @@ CONFIG_SYSFS=y
 # CONFIG_BEFS_FS is not set
 # CONFIG_BFS_FS is not set
 # CONFIG_EFS_FS is not set
-CONFIG_YAFFS_FS=m
-CONFIG_YAFFS_YAFFS1=y
-# CONFIG_YAFFS_9BYTE_TAGS is not set
-# CONFIG_YAFFS_DOES_ECC is not set
-CONFIG_YAFFS_YAFFS2=y
-CONFIG_YAFFS_AUTO_YAFFS2=y
-# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set
-# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set
-# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set
-CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y
 CONFIG_JFFS2_FS=m
 CONFIG_JFFS2_FS_DEBUG=0
 CONFIG_JFFS2_FS_WRITEBUFFER=y
@@ -1277,6 +1312,16 @@ CONFIG_JFFS2_ZLIB=y
 # CONFIG_JFFS2_LZO is not set
 CONFIG_JFFS2_RTIME=y
 # CONFIG_JFFS2_RUBIN is not set
+CONFIG_YAFFS_FS=m
+CONFIG_YAFFS_YAFFS1=y
+# CONFIG_YAFFS_9BYTE_TAGS is not set
+# CONFIG_YAFFS_DOES_ECC is not set
+CONFIG_YAFFS_YAFFS2=y
+CONFIG_YAFFS_AUTO_YAFFS2=y
+# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set
+# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set
+# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set
+CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y
 # CONFIG_CRAMFS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_MINIX_FS is not set
@@ -1313,7 +1358,7 @@ CONFIG_SMB_FS=m
 CONFIG_MSDOS_PARTITION=y
 CONFIG_NLS=m
 CONFIG_NLS_DEFAULT="iso8859-1"
-# CONFIG_NLS_CODEPAGE_437 is not set
+CONFIG_NLS_CODEPAGE_437=m
 # CONFIG_NLS_CODEPAGE_737 is not set
 # CONFIG_NLS_CODEPAGE_775 is not set
 # CONFIG_NLS_CODEPAGE_850 is not set
@@ -1328,7 +1373,7 @@ CONFIG_NLS_DEFAULT="iso8859-1"
 # 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_936=m
 # CONFIG_NLS_CODEPAGE_950 is not set
 # CONFIG_NLS_CODEPAGE_932 is not set
 # CONFIG_NLS_CODEPAGE_949 is not set
@@ -1337,7 +1382,7 @@ CONFIG_NLS_DEFAULT="iso8859-1"
 # CONFIG_NLS_CODEPAGE_1250 is not set
 # CONFIG_NLS_CODEPAGE_1251 is not set
 # CONFIG_NLS_ASCII is not set
-# CONFIG_NLS_ISO8859_1 is not set
+CONFIG_NLS_ISO8859_1=m
 # CONFIG_NLS_ISO8859_2 is not set
 # CONFIG_NLS_ISO8859_3 is not set
 # CONFIG_NLS_ISO8859_4 is not set
@@ -1350,7 +1395,7 @@ CONFIG_NLS_DEFAULT="iso8859-1"
 # CONFIG_NLS_ISO8859_15 is not set
 # CONFIG_NLS_KOI8_R is not set
 # CONFIG_NLS_KOI8_U is not set
-# CONFIG_NLS_UTF8 is not set
+CONFIG_NLS_UTF8=m
 # CONFIG_DLM is not set
 
 #
@@ -1365,7 +1410,7 @@ CONFIG_FRAME_WARN=1024
 CONFIG_DEBUG_FS=y
 # CONFIG_HEADERS_CHECK is not set
 CONFIG_DEBUG_KERNEL=y
-# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_DEBUG_SHIRQ=y
 CONFIG_DETECT_SOFTLOCKUP=y
 # CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
 CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
@@ -1374,8 +1419,6 @@ CONFIG_SCHED_DEBUG=y
 # CONFIG_TIMER_STATS is not set
 # CONFIG_DEBUG_OBJECTS is not set
 # CONFIG_DEBUG_SLAB is not set
-# CONFIG_DEBUG_RT_MUTEXES is not set
-# CONFIG_RT_MUTEX_TESTER is not set
 # CONFIG_DEBUG_SPINLOCK is not set
 # CONFIG_DEBUG_MUTEXES is not set
 # CONFIG_DEBUG_SPINLOCK_SLEEP is not set
@@ -1395,7 +1438,6 @@ CONFIG_DEBUG_INFO=y
 # CONFIG_BACKTRACE_SELF_TEST is not set
 # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
 # CONFIG_FAULT_INJECTION is not set
-CONFIG_SYSCTL_SYSCALL_CHECK=y
 
 #
 # Tracers
@@ -1411,16 +1453,20 @@ CONFIG_HAVE_ARCH_KGDB=y
 # CONFIG_DEBUG_STACK_USAGE is not set
 CONFIG_DEBUG_VERBOSE=y
 CONFIG_DEBUG_MMRS=y
-# CONFIG_DEBUG_HWERR is not set
-# CONFIG_DEBUG_DOUBLEFAULT is not set
+CONFIG_DEBUG_HWERR=y
+CONFIG_EXACT_HWERR=y
+CONFIG_DEBUG_DOUBLEFAULT=y
+CONFIG_DEBUG_DOUBLEFAULT_PRINT=y
+# CONFIG_DEBUG_DOUBLEFAULT_RESET is not set
+# CONFIG_DEBUG_ICACHE_CHECK is not set
 CONFIG_DEBUG_HUNT_FOR_ZERO=y
 CONFIG_DEBUG_BFIN_HWTRACE_ON=y
-CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF=y
-# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_ONE is not set
+# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF is not set
+CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_ONE=y
 # CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_TWO is not set
-CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION=0
+CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION=1
 # CONFIG_DEBUG_BFIN_HWTRACE_EXPAND is not set
-# CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE is not set
+CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE=y
 CONFIG_EARLY_PRINTK=y
 CONFIG_CPLB_INFO=y
 CONFIG_ACCESS_CHECK=y
@@ -1534,7 +1580,6 @@ CONFIG_CRC32=y
 # CONFIG_LIBCRC32C is not set
 CONFIG_ZLIB_INFLATE=y
 CONFIG_ZLIB_DEFLATE=m
-CONFIG_PLIST=y
 CONFIG_HAS_IOMEM=y
 CONFIG_HAS_IOPORT=y
 CONFIG_HAS_DMA=y
index 911b5dba1dbc5022e9fd1b37301f4be13e136a9b..e9175c608aa7175c9ef8f8b72ec423200cdf55a7 100644 (file)
@@ -1,6 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.28.7
+# Linux kernel version: 2.6.28.10
+# Thu May 21 05:50:01 2009
 #
 # CONFIG_MMU is not set
 # CONFIG_FPU is not set
@@ -42,10 +43,11 @@ CONFIG_LOG_BUF_SHIFT=14
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_INITRAMFS_SOURCE=""
 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
-# CONFIG_SYSCTL is not set
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
 CONFIG_EMBEDDED=y
 CONFIG_UID16=y
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_ALL is not set
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
@@ -53,16 +55,15 @@ CONFIG_HOTPLUG=y
 CONFIG_PRINTK=y
 CONFIG_BUG=y
 # CONFIG_ELF_CORE is not set
-CONFIG_COMPAT_BRK=y
 CONFIG_BASE_FULL=y
 # CONFIG_FUTEX is not set
-CONFIG_ANON_INODES=y
 CONFIG_EPOLL=y
-CONFIG_SIGNALFD=y
-CONFIG_TIMERFD=y
-CONFIG_EVENTFD=y
+# CONFIG_SIGNALFD is not set
+# CONFIG_TIMERFD is not set
+# CONFIG_EVENTFD is not set
 # CONFIG_AIO is not set
 CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_COMPAT_BRK=y
 CONFIG_SLAB=y
 # CONFIG_SLUB is not set
 # CONFIG_SLOB is not set
@@ -71,7 +72,6 @@ CONFIG_SLAB=y
 CONFIG_HAVE_OPROFILE=y
 # CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
 CONFIG_SLABINFO=y
-CONFIG_RT_MUTEXES=y
 CONFIG_TINY_SHMEM=y
 CONFIG_BASE_SMALL=0
 CONFIG_MODULES=y
@@ -145,8 +145,8 @@ CONFIG_BF527=y
 CONFIG_BF_REV_MIN=0
 CONFIG_BF_REV_MAX=2
 # CONFIG_BF_REV_0_0 is not set
-CONFIG_BF_REV_0_1=y
-# CONFIG_BF_REV_0_2 is not set
+# CONFIG_BF_REV_0_1 is not set
+CONFIG_BF_REV_0_2=y
 # CONFIG_BF_REV_0_3 is not set
 # CONFIG_BF_REV_0_4 is not set
 # CONFIG_BF_REV_0_5 is not set
@@ -264,7 +264,10 @@ CONFIG_HZ=250
 # CONFIG_SCHED_HRTICK is not set
 CONFIG_GENERIC_TIME=y
 CONFIG_GENERIC_CLOCKEVENTS=y
+# CONFIG_TICKSOURCE_GPTMR0 is not set
+CONFIG_TICKSOURCE_CORETMR=y
 # CONFIG_CYCLES_CLOCKSOURCE is not set
+# CONFIG_GPTMR0_CLOCKSOURCE is not set
 # CONFIG_NO_HZ is not set
 # CONFIG_HIGH_RES_TIMERS is not set
 CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
@@ -318,7 +321,7 @@ CONFIG_SPLIT_PTLOCK_CPUS=4
 # CONFIG_PHYS_ADDR_T_64BIT is not set
 CONFIG_ZONE_DMA_FLAG=1
 CONFIG_VIRT_TO_BUS=y
-CONFIG_BFIN_GPTIMERS=m
+CONFIG_BFIN_GPTIMERS=y
 # CONFIG_DMA_UNCACHED_4M is not set
 # CONFIG_DMA_UNCACHED_2M is not set
 CONFIG_DMA_UNCACHED_1M=y
@@ -409,7 +412,7 @@ CONFIG_IP_PNP=y
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_ARPD is not set
-CONFIG_SYN_COOKIES=y
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_AH is not set
 # CONFIG_INET_ESP is not set
 # CONFIG_INET_IPCOMP is not set
@@ -639,9 +642,42 @@ CONFIG_HAVE_IDE=y
 # SCSI device support
 #
 # CONFIG_RAID_ATTRS is not set
-# CONFIG_SCSI is not set
-# CONFIG_SCSI_DMA is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
 # CONFIG_SCSI_NETLINK is not set
+# CONFIG_SCSI_PROC_FS is not set
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=m
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+# CONFIG_SCSI_LOWLEVEL is not set
+# CONFIG_SCSI_DH is not set
 # CONFIG_ATA is not set
 # CONFIG_MD is not set
 CONFIG_NETDEVICES=y
@@ -687,9 +723,8 @@ CONFIG_BFIN_MAC_RMII=y
 # CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
 # CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
 # CONFIG_B44 is not set
-CONFIG_NETDEV_1000=y
-# CONFIG_AX88180 is not set
-CONFIG_NETDEV_10000=y
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
 
 #
 # Wireless LAN
@@ -758,14 +793,14 @@ CONFIG_INPUT_MISC=y
 # Character devices
 #
 # CONFIG_AD9960 is not set
-# CONFIG_SPI_ADC_BF533 is not set
-# CONFIG_BF5xx_PPIFCD is not set
+CONFIG_BFIN_DMA_INTERFACE=m
+# CONFIG_BFIN_PPI is not set
+# CONFIG_BFIN_PPIFCD is not set
 # CONFIG_BFIN_SIMPLE_TIMER is not set
-CONFIG_BF5xx_PPI=m
+# CONFIG_BFIN_SPI_ADC is not set
 CONFIG_BFIN_SPORT=m
 # CONFIG_BFIN_TIMER_LATENCY is not set
-# CONFIG_TWI_LCD is not set
-CONFIG_BFIN_DMA_INTERFACE=m
+# CONFIG_BFIN_TWI_LCD is not set
 CONFIG_SIMPLE_GPIO=m
 CONFIG_VT=y
 CONFIG_CONSOLE_TRANSLATIONS=y
@@ -875,7 +910,30 @@ CONFIG_SPI_BFIN=y
 # CONFIG_SPI_SPIDEV is not set
 # CONFIG_SPI_TLE62X0 is not set
 CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
-# CONFIG_GPIOLIB is not set
+CONFIG_GPIOLIB=y
+# CONFIG_DEBUG_GPIO is not set
+CONFIG_GPIO_SYSFS=y
+
+#
+# Memory mapped GPIO expanders:
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
 # CONFIG_W1 is not set
 # CONFIG_POWER_SUPPLY is not set
 # CONFIG_HWMON is not set
@@ -909,6 +967,7 @@ CONFIG_SSB_POSSIBLE=y
 # CONFIG_HTC_PASIC3 is not set
 # CONFIG_MFD_TMIO is not set
 # CONFIG_PMIC_DA903X is not set
+# CONFIG_PMIC_ADP5520 is not set
 # CONFIG_MFD_WM8400 is not set
 # CONFIG_MFD_WM8350_I2C is not set
 # CONFIG_REGULATOR is not set
@@ -1091,8 +1150,8 @@ CONFIG_USB=y
 #
 # Miscellaneous USB options
 #
-# CONFIG_USB_DEVICEFS is not set
-CONFIG_USB_DEVICE_CLASS=y
+CONFIG_USB_DEVICEFS=y
+# CONFIG_USB_DEVICE_CLASS is not set
 # CONFIG_USB_DYNAMIC_MINORS is not set
 # CONFIG_USB_OTG is not set
 # CONFIG_USB_OTG_WHITELIST is not set
@@ -1120,10 +1179,10 @@ CONFIG_USB_MUSB_SOC=y
 CONFIG_USB_MUSB_HOST=y
 # CONFIG_USB_MUSB_PERIPHERAL is not set
 # CONFIG_USB_MUSB_OTG is not set
-# CONFIG_USB_GADGET_MUSB_HDRC is not set
 CONFIG_USB_MUSB_HDRC_HCD=y
-CONFIG_MUSB_PIO_ONLY=y
-CONFIG_MUSB_DMA_POLL=y
+# CONFIG_MUSB_PIO_ONLY is not set
+CONFIG_USB_INVENTRA_DMA=y
+# CONFIG_USB_TI_CPPI_DMA is not set
 # CONFIG_USB_MUSB_DEBUG is not set
 
 #
@@ -1141,7 +1200,7 @@ CONFIG_MUSB_DMA_POLL=y
 #
 # see USB_STORAGE Help for more information
 #
-CONFIG_USB_STORAGE=m
+CONFIG_USB_STORAGE=y
 # CONFIG_USB_STORAGE_DEBUG is not set
 # CONFIG_USB_STORAGE_DATAFAB is not set
 # CONFIG_USB_STORAGE_FREECOM is not set
@@ -1190,33 +1249,10 @@ CONFIG_USB_STORAGE=m
 # CONFIG_USB_LD is not set
 # CONFIG_USB_TRANCEVIBRATOR is not set
 # CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
 # CONFIG_USB_ISIGHTFW is not set
 # CONFIG_USB_VST is not set
 # CONFIG_USB_GADGET is not set
-# CONFIG_USB_GADGET_AT91 is not set
-# CONFIG_USB_GADGET_ATMEL_USBA is not set
-# CONFIG_USB_GADGET_FSL_USB2 is not set
-# CONFIG_USB_GADGET_LH7A40X is not set
-# CONFIG_USB_GADGET_OMAP is not set
-# CONFIG_USB_GADGET_PXA25X is not set
-# CONFIG_USB_GADGET_PXA27X is not set
-# CONFIG_USB_GADGET_S3C2410 is not set
-# CONFIG_USB_GADGET_M66592 is not set
-# CONFIG_USB_GADGET_AMD5536UDC is not set
-# CONFIG_USB_GADGET_FSL_QE is not set
-# CONFIG_USB_GADGET_NET2272 is not set
-# CONFIG_USB_GADGET_NET2280 is not set
-# CONFIG_USB_GADGET_GOKU is not set
-# CONFIG_USB_GADGET_DUMMY_HCD is not set
-# CONFIG_USB_ZERO is not set
-# CONFIG_USB_AUDIO is not set
-# CONFIG_USB_ETH is not set
-# CONFIG_USB_GADGETFS is not set
-# CONFIG_USB_FILE_STORAGE is not set
-# CONFIG_USB_G_SERIAL is not set
-# CONFIG_USB_MIDI_GADGET is not set
-# CONFIG_USB_G_PRINTER is not set
-# CONFIG_USB_CDC_COMPOSITE is not set
 # CONFIG_MMC is not set
 # CONFIG_MEMSTICK is not set
 # CONFIG_NEW_LEDS is not set
@@ -1289,7 +1325,8 @@ CONFIG_RTC_DRV_BFIN=y
 #
 # File systems
 #
-# CONFIG_EXT2_FS is not set
+CONFIG_EXT2_FS=m
+# CONFIG_EXT2_FS_XATTR is not set
 # CONFIG_EXT3_FS is not set
 # CONFIG_EXT4_FS is not set
 # CONFIG_REISERFS_FS is not set
@@ -1309,14 +1346,20 @@ CONFIG_INOTIFY_USER=y
 #
 # CD-ROM/DVD Filesystems
 #
-# CONFIG_ISO9660_FS is not set
-# CONFIG_UDF_FS is not set
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+# CONFIG_ZISOFS is not set
+CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
 
 #
 # DOS/FAT/NT Filesystems
 #
+CONFIG_FAT_FS=m
 # CONFIG_MSDOS_FS is not set
-# CONFIG_VFAT_FS is not set
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
 # CONFIG_NTFS_FS is not set
 
 #
@@ -1339,16 +1382,6 @@ CONFIG_SYSFS=y
 # CONFIG_BEFS_FS is not set
 # CONFIG_BFS_FS is not set
 # CONFIG_EFS_FS is not set
-CONFIG_YAFFS_FS=m
-CONFIG_YAFFS_YAFFS1=y
-# CONFIG_YAFFS_9BYTE_TAGS is not set
-# CONFIG_YAFFS_DOES_ECC is not set
-CONFIG_YAFFS_YAFFS2=y
-CONFIG_YAFFS_AUTO_YAFFS2=y
-# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set
-# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set
-# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set
-CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y
 CONFIG_JFFS2_FS=m
 CONFIG_JFFS2_FS_DEBUG=0
 CONFIG_JFFS2_FS_WRITEBUFFER=y
@@ -1360,6 +1393,16 @@ CONFIG_JFFS2_ZLIB=y
 # CONFIG_JFFS2_LZO is not set
 CONFIG_JFFS2_RTIME=y
 # CONFIG_JFFS2_RUBIN is not set
+CONFIG_YAFFS_FS=m
+CONFIG_YAFFS_YAFFS1=y
+# CONFIG_YAFFS_9BYTE_TAGS is not set
+# CONFIG_YAFFS_DOES_ECC is not set
+CONFIG_YAFFS_YAFFS2=y
+CONFIG_YAFFS_AUTO_YAFFS2=y
+# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set
+# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set
+# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set
+CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y
 # CONFIG_CRAMFS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_MINIX_FS is not set
@@ -1396,7 +1439,7 @@ CONFIG_SMB_FS=m
 CONFIG_MSDOS_PARTITION=y
 CONFIG_NLS=m
 CONFIG_NLS_DEFAULT="iso8859-1"
-# CONFIG_NLS_CODEPAGE_437 is not set
+CONFIG_NLS_CODEPAGE_437=m
 # CONFIG_NLS_CODEPAGE_737 is not set
 # CONFIG_NLS_CODEPAGE_775 is not set
 # CONFIG_NLS_CODEPAGE_850 is not set
@@ -1411,7 +1454,7 @@ CONFIG_NLS_DEFAULT="iso8859-1"
 # 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_936=m
 # CONFIG_NLS_CODEPAGE_950 is not set
 # CONFIG_NLS_CODEPAGE_932 is not set
 # CONFIG_NLS_CODEPAGE_949 is not set
@@ -1420,7 +1463,7 @@ CONFIG_NLS_DEFAULT="iso8859-1"
 # CONFIG_NLS_CODEPAGE_1250 is not set
 # CONFIG_NLS_CODEPAGE_1251 is not set
 # CONFIG_NLS_ASCII is not set
-# CONFIG_NLS_ISO8859_1 is not set
+CONFIG_NLS_ISO8859_1=m
 # CONFIG_NLS_ISO8859_2 is not set
 # CONFIG_NLS_ISO8859_3 is not set
 # CONFIG_NLS_ISO8859_4 is not set
@@ -1433,7 +1476,7 @@ CONFIG_NLS_DEFAULT="iso8859-1"
 # CONFIG_NLS_ISO8859_15 is not set
 # CONFIG_NLS_KOI8_R is not set
 # CONFIG_NLS_KOI8_U is not set
-# CONFIG_NLS_UTF8 is not set
+CONFIG_NLS_UTF8=m
 # CONFIG_DLM is not set
 
 #
@@ -1448,7 +1491,7 @@ CONFIG_FRAME_WARN=1024
 CONFIG_DEBUG_FS=y
 # CONFIG_HEADERS_CHECK is not set
 CONFIG_DEBUG_KERNEL=y
-# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_DEBUG_SHIRQ=y
 CONFIG_DETECT_SOFTLOCKUP=y
 # CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
 CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
@@ -1457,8 +1500,6 @@ CONFIG_SCHED_DEBUG=y
 # CONFIG_TIMER_STATS is not set
 # CONFIG_DEBUG_OBJECTS is not set
 # CONFIG_DEBUG_SLAB is not set
-# CONFIG_DEBUG_RT_MUTEXES is not set
-# CONFIG_RT_MUTEX_TESTER is not set
 # CONFIG_DEBUG_SPINLOCK is not set
 # CONFIG_DEBUG_MUTEXES is not set
 # CONFIG_DEBUG_SPINLOCK_SLEEP is not set
@@ -1478,7 +1519,6 @@ CONFIG_DEBUG_INFO=y
 # CONFIG_BACKTRACE_SELF_TEST is not set
 # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
 # CONFIG_FAULT_INJECTION is not set
-# CONFIG_SYSCTL_SYSCALL_CHECK is not set
 
 #
 # Tracers
@@ -1494,16 +1534,20 @@ CONFIG_HAVE_ARCH_KGDB=y
 # CONFIG_DEBUG_STACK_USAGE is not set
 CONFIG_DEBUG_VERBOSE=y
 CONFIG_DEBUG_MMRS=y
-# CONFIG_DEBUG_HWERR is not set
-# CONFIG_DEBUG_DOUBLEFAULT is not set
+CONFIG_DEBUG_HWERR=y
+CONFIG_EXACT_HWERR=y
+CONFIG_DEBUG_DOUBLEFAULT=y
+CONFIG_DEBUG_DOUBLEFAULT_PRINT=y
+# CONFIG_DEBUG_DOUBLEFAULT_RESET is not set
+# CONFIG_DEBUG_ICACHE_CHECK is not set
 CONFIG_DEBUG_HUNT_FOR_ZERO=y
 CONFIG_DEBUG_BFIN_HWTRACE_ON=y
-CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF=y
-# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_ONE is not set
+# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF is not set
+CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_ONE=y
 # CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_TWO is not set
-CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION=0
+CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION=1
 # CONFIG_DEBUG_BFIN_HWTRACE_EXPAND is not set
-# CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE is not set
+CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE=y
 CONFIG_EARLY_PRINTK=y
 CONFIG_CPLB_INFO=y
 CONFIG_ACCESS_CHECK=y
@@ -1611,13 +1655,12 @@ CONFIG_BITREVERSE=y
 CONFIG_CRC_CCITT=m
 # CONFIG_CRC16 is not set
 # CONFIG_CRC_T10DIF is not set
-# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC_ITU_T=m
 CONFIG_CRC32=y
 # CONFIG_CRC7 is not set
 # CONFIG_LIBCRC32C is not set
 CONFIG_ZLIB_INFLATE=y
 CONFIG_ZLIB_DEFLATE=m
-CONFIG_PLIST=y
 CONFIG_HAS_IOMEM=y
 CONFIG_HAS_IOPORT=y
 CONFIG_HAS_DMA=y
index 4c41e03efe0f943e3e6d4cbd6a35ce757ba2ecd4..5aa63bafdd624ea1ff4f998e042d777a98c93f2e 100644 (file)
@@ -1,6 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.28.7
+# Linux kernel version: 2.6.28.10
+# Thu May 21 05:50:01 2009
 #
 # CONFIG_MMU is not set
 # CONFIG_FPU is not set
@@ -42,10 +43,11 @@ CONFIG_LOG_BUF_SHIFT=14
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_INITRAMFS_SOURCE=""
 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
-# CONFIG_SYSCTL is not set
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
 CONFIG_EMBEDDED=y
 CONFIG_UID16=y
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_ALL is not set
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
@@ -53,16 +55,15 @@ CONFIG_HOTPLUG=y
 CONFIG_PRINTK=y
 CONFIG_BUG=y
 # CONFIG_ELF_CORE is not set
-CONFIG_COMPAT_BRK=y
 CONFIG_BASE_FULL=y
 # CONFIG_FUTEX is not set
-CONFIG_ANON_INODES=y
 CONFIG_EPOLL=y
-CONFIG_SIGNALFD=y
-CONFIG_TIMERFD=y
-CONFIG_EVENTFD=y
+# CONFIG_SIGNALFD is not set
+# CONFIG_TIMERFD is not set
+# CONFIG_EVENTFD is not set
 # CONFIG_AIO is not set
 CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_COMPAT_BRK=y
 CONFIG_SLAB=y
 # CONFIG_SLUB is not set
 # CONFIG_SLOB is not set
@@ -71,7 +72,6 @@ CONFIG_SLAB=y
 CONFIG_HAVE_OPROFILE=y
 # CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
 CONFIG_SLABINFO=y
-CONFIG_RT_MUTEXES=y
 CONFIG_TINY_SHMEM=y
 CONFIG_BASE_SMALL=0
 CONFIG_MODULES=y
@@ -225,7 +225,10 @@ CONFIG_HZ=250
 CONFIG_SCHED_HRTICK=y
 CONFIG_GENERIC_TIME=y
 CONFIG_GENERIC_CLOCKEVENTS=y
+# CONFIG_TICKSOURCE_GPTMR0 is not set
+CONFIG_TICKSOURCE_CORETMR=y
 # CONFIG_CYCLES_CLOCKSOURCE is not set
+# CONFIG_GPTMR0_CLOCKSOURCE is not set
 CONFIG_TICK_ONESHOT=y
 # CONFIG_NO_HZ is not set
 CONFIG_HIGH_RES_TIMERS=y
@@ -382,7 +385,7 @@ CONFIG_IP_PNP=y
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_ARPD is not set
-CONFIG_SYN_COOKIES=y
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_AH is not set
 # CONFIG_INET_ESP is not set
 # CONFIG_INET_IPCOMP is not set
@@ -613,9 +616,8 @@ CONFIG_SMC91X=y
 # CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
 # CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
 # CONFIG_B44 is not set
-CONFIG_NETDEV_1000=y
-# CONFIG_AX88180 is not set
-CONFIG_NETDEV_10000=y
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
 
 #
 # Wireless LAN
@@ -667,13 +669,13 @@ CONFIG_INPUT_EVDEV=m
 # Character devices
 #
 # CONFIG_AD9960 is not set
-# CONFIG_SPI_ADC_BF533 is not set
-# CONFIG_BF5xx_PPIFCD is not set
+CONFIG_BFIN_DMA_INTERFACE=m
+# CONFIG_BFIN_PPI is not set
+# CONFIG_BFIN_PPIFCD is not set
 # CONFIG_BFIN_SIMPLE_TIMER is not set
-# CONFIG_BF5xx_PPI is not set
+# CONFIG_BFIN_SPI_ADC is not set
 CONFIG_BFIN_SPORT=y
 # CONFIG_BFIN_TIMER_LATENCY is not set
-CONFIG_BFIN_DMA_INTERFACE=m
 CONFIG_SIMPLE_GPIO=m
 # CONFIG_VT is not set
 # CONFIG_DEVKMEM is not set
@@ -729,7 +731,30 @@ CONFIG_SPI_BFIN=y
 # CONFIG_SPI_SPIDEV is not set
 # CONFIG_SPI_TLE62X0 is not set
 CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
-# CONFIG_GPIOLIB is not set
+CONFIG_GPIOLIB=y
+# CONFIG_DEBUG_GPIO is not set
+CONFIG_GPIO_SYSFS=y
+
+#
+# Memory mapped GPIO expanders:
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
 # CONFIG_W1 is not set
 # CONFIG_POWER_SUPPLY is not set
 # CONFIG_HWMON is not set
@@ -904,16 +929,6 @@ CONFIG_SYSFS=y
 # CONFIG_BEFS_FS is not set
 # CONFIG_BFS_FS is not set
 # CONFIG_EFS_FS is not set
-CONFIG_YAFFS_FS=m
-CONFIG_YAFFS_YAFFS1=y
-# CONFIG_YAFFS_9BYTE_TAGS is not set
-# CONFIG_YAFFS_DOES_ECC is not set
-CONFIG_YAFFS_YAFFS2=y
-CONFIG_YAFFS_AUTO_YAFFS2=y
-# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set
-# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set
-# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set
-CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y
 CONFIG_JFFS2_FS=m
 CONFIG_JFFS2_FS_DEBUG=0
 CONFIG_JFFS2_FS_WRITEBUFFER=y
@@ -925,6 +940,16 @@ CONFIG_JFFS2_ZLIB=y
 # CONFIG_JFFS2_LZO is not set
 CONFIG_JFFS2_RTIME=y
 # CONFIG_JFFS2_RUBIN is not set
+CONFIG_YAFFS_FS=m
+CONFIG_YAFFS_YAFFS1=y
+# CONFIG_YAFFS_9BYTE_TAGS is not set
+# CONFIG_YAFFS_DOES_ECC is not set
+CONFIG_YAFFS_YAFFS2=y
+CONFIG_YAFFS_AUTO_YAFFS2=y
+# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set
+# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set
+# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set
+CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y
 # CONFIG_CRAMFS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_MINIX_FS is not set
@@ -1013,7 +1038,7 @@ CONFIG_FRAME_WARN=1024
 CONFIG_DEBUG_FS=y
 # CONFIG_HEADERS_CHECK is not set
 CONFIG_DEBUG_KERNEL=y
-# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_DEBUG_SHIRQ=y
 CONFIG_DETECT_SOFTLOCKUP=y
 # CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
 CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
@@ -1022,8 +1047,6 @@ CONFIG_SCHED_DEBUG=y
 # CONFIG_TIMER_STATS is not set
 # CONFIG_DEBUG_OBJECTS is not set
 # CONFIG_DEBUG_SLAB is not set
-# CONFIG_DEBUG_RT_MUTEXES is not set
-# CONFIG_RT_MUTEX_TESTER is not set
 # CONFIG_DEBUG_SPINLOCK is not set
 # CONFIG_DEBUG_MUTEXES is not set
 # CONFIG_DEBUG_SPINLOCK_SLEEP is not set
@@ -1043,7 +1066,6 @@ CONFIG_DEBUG_INFO=y
 # CONFIG_BACKTRACE_SELF_TEST is not set
 # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
 # CONFIG_FAULT_INJECTION is not set
-# CONFIG_SYSCTL_SYSCALL_CHECK is not set
 
 #
 # Tracers
@@ -1059,16 +1081,20 @@ CONFIG_HAVE_ARCH_KGDB=y
 # CONFIG_DEBUG_STACK_USAGE is not set
 CONFIG_DEBUG_VERBOSE=y
 CONFIG_DEBUG_MMRS=y
-# CONFIG_DEBUG_HWERR is not set
-# CONFIG_DEBUG_DOUBLEFAULT is not set
+CONFIG_DEBUG_HWERR=y
+CONFIG_EXACT_HWERR=y
+CONFIG_DEBUG_DOUBLEFAULT=y
+CONFIG_DEBUG_DOUBLEFAULT_PRINT=y
+# CONFIG_DEBUG_DOUBLEFAULT_RESET is not set
+# CONFIG_DEBUG_ICACHE_CHECK is not set
 CONFIG_DEBUG_HUNT_FOR_ZERO=y
 CONFIG_DEBUG_BFIN_HWTRACE_ON=y
-CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF=y
-# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_ONE is not set
+# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF is not set
+CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_ONE=y
 # CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_TWO is not set
-CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION=0
+CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION=1
 # CONFIG_DEBUG_BFIN_HWTRACE_EXPAND is not set
-# CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE is not set
+CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE=y
 CONFIG_EARLY_PRINTK=y
 CONFIG_CPLB_INFO=y
 CONFIG_ACCESS_CHECK=y
@@ -1181,7 +1207,6 @@ CONFIG_CRC32=y
 # CONFIG_LIBCRC32C is not set
 CONFIG_ZLIB_INFLATE=y
 CONFIG_ZLIB_DEFLATE=m
-CONFIG_PLIST=y
 CONFIG_HAS_IOMEM=y
 CONFIG_HAS_IOPORT=y
 CONFIG_HAS_DMA=y
index 9c482cd1b34393ce8f05e724608b34636f97ddc1..fed25329e13ced60cb37c4e75be9e06a5d054b42 100644 (file)
@@ -1,6 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.28.7
+# Linux kernel version: 2.6.28.10
+# Thu May 21 05:50:01 2009
 #
 # CONFIG_MMU is not set
 # CONFIG_FPU is not set
@@ -42,10 +43,11 @@ CONFIG_LOG_BUF_SHIFT=14
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_INITRAMFS_SOURCE=""
 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
-# CONFIG_SYSCTL is not set
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
 CONFIG_EMBEDDED=y
 CONFIG_UID16=y
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_ALL is not set
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
@@ -53,16 +55,15 @@ CONFIG_HOTPLUG=y
 CONFIG_PRINTK=y
 CONFIG_BUG=y
 # CONFIG_ELF_CORE is not set
-CONFIG_COMPAT_BRK=y
 CONFIG_BASE_FULL=y
 # CONFIG_FUTEX is not set
-CONFIG_ANON_INODES=y
 CONFIG_EPOLL=y
-CONFIG_SIGNALFD=y
-CONFIG_TIMERFD=y
-CONFIG_EVENTFD=y
+# CONFIG_SIGNALFD is not set
+# CONFIG_TIMERFD is not set
+# CONFIG_EVENTFD is not set
 # CONFIG_AIO is not set
 CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_COMPAT_BRK=y
 CONFIG_SLAB=y
 # CONFIG_SLUB is not set
 # CONFIG_SLOB is not set
@@ -71,7 +72,6 @@ CONFIG_SLAB=y
 CONFIG_HAVE_OPROFILE=y
 # CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
 CONFIG_SLABINFO=y
-CONFIG_RT_MUTEXES=y
 CONFIG_TINY_SHMEM=y
 CONFIG_BASE_SMALL=0
 CONFIG_MODULES=y
@@ -225,7 +225,10 @@ CONFIG_HZ=250
 CONFIG_SCHED_HRTICK=y
 CONFIG_GENERIC_TIME=y
 CONFIG_GENERIC_CLOCKEVENTS=y
+# CONFIG_TICKSOURCE_GPTMR0 is not set
+CONFIG_TICKSOURCE_CORETMR=y
 # CONFIG_CYCLES_CLOCKSOURCE is not set
+# CONFIG_GPTMR0_CLOCKSOURCE is not set
 CONFIG_TICK_ONESHOT=y
 # CONFIG_NO_HZ is not set
 CONFIG_HIGH_RES_TIMERS=y
@@ -382,7 +385,7 @@ CONFIG_IP_PNP=y
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_ARPD is not set
-CONFIG_SYN_COOKIES=y
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_AH is not set
 # CONFIG_INET_ESP is not set
 # CONFIG_INET_IPCOMP is not set
@@ -618,9 +621,8 @@ CONFIG_SMC91X=y
 # CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
 # CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
 # CONFIG_B44 is not set
-CONFIG_NETDEV_1000=y
-# CONFIG_AX88180 is not set
-CONFIG_NETDEV_10000=y
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
 
 #
 # Wireless LAN
@@ -674,14 +676,14 @@ CONFIG_CONFIG_INPUT_PCF8574=m
 # Character devices
 #
 # CONFIG_AD9960 is not set
-# CONFIG_SPI_ADC_BF533 is not set
-# CONFIG_BF5xx_PPIFCD is not set
+CONFIG_BFIN_DMA_INTERFACE=m
+# CONFIG_BFIN_PPI is not set
+# CONFIG_BFIN_PPIFCD is not set
 # CONFIG_BFIN_SIMPLE_TIMER is not set
-CONFIG_BF5xx_PPI=m
+# CONFIG_BFIN_SPI_ADC is not set
 CONFIG_BFIN_SPORT=m
 # CONFIG_BFIN_TIMER_LATENCY is not set
-# CONFIG_TWI_LCD is not set
-CONFIG_BFIN_DMA_INTERFACE=m
+# CONFIG_BFIN_TWI_LCD is not set
 CONFIG_SIMPLE_GPIO=m
 # CONFIG_VT is not set
 # CONFIG_DEVKMEM is not set
@@ -781,7 +783,30 @@ CONFIG_SPI_BFIN=y
 # CONFIG_SPI_SPIDEV is not set
 # CONFIG_SPI_TLE62X0 is not set
 CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
-# CONFIG_GPIOLIB is not set
+CONFIG_GPIOLIB=y
+# CONFIG_DEBUG_GPIO is not set
+CONFIG_GPIO_SYSFS=y
+
+#
+# Memory mapped GPIO expanders:
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
 # CONFIG_W1 is not set
 # CONFIG_POWER_SUPPLY is not set
 # CONFIG_HWMON is not set
@@ -1068,16 +1093,6 @@ CONFIG_SYSFS=y
 # CONFIG_BEFS_FS is not set
 # CONFIG_BFS_FS is not set
 # CONFIG_EFS_FS is not set
-CONFIG_YAFFS_FS=m
-CONFIG_YAFFS_YAFFS1=y
-# CONFIG_YAFFS_9BYTE_TAGS is not set
-# CONFIG_YAFFS_DOES_ECC is not set
-CONFIG_YAFFS_YAFFS2=y
-CONFIG_YAFFS_AUTO_YAFFS2=y
-# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set
-# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set
-# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set
-CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y
 CONFIG_JFFS2_FS=m
 CONFIG_JFFS2_FS_DEBUG=0
 CONFIG_JFFS2_FS_WRITEBUFFER=y
@@ -1089,6 +1104,16 @@ CONFIG_JFFS2_ZLIB=y
 # CONFIG_JFFS2_LZO is not set
 CONFIG_JFFS2_RTIME=y
 # CONFIG_JFFS2_RUBIN is not set
+CONFIG_YAFFS_FS=m
+CONFIG_YAFFS_YAFFS1=y
+# CONFIG_YAFFS_9BYTE_TAGS is not set
+# CONFIG_YAFFS_DOES_ECC is not set
+CONFIG_YAFFS_YAFFS2=y
+CONFIG_YAFFS_AUTO_YAFFS2=y
+# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set
+# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set
+# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set
+CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y
 # CONFIG_CRAMFS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_MINIX_FS is not set
@@ -1177,7 +1202,7 @@ CONFIG_FRAME_WARN=1024
 CONFIG_DEBUG_FS=y
 # CONFIG_HEADERS_CHECK is not set
 CONFIG_DEBUG_KERNEL=y
-# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_DEBUG_SHIRQ=y
 CONFIG_DETECT_SOFTLOCKUP=y
 # CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
 CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
@@ -1186,8 +1211,6 @@ CONFIG_SCHED_DEBUG=y
 # CONFIG_TIMER_STATS is not set
 # CONFIG_DEBUG_OBJECTS is not set
 # CONFIG_DEBUG_SLAB is not set
-# CONFIG_DEBUG_RT_MUTEXES is not set
-# CONFIG_RT_MUTEX_TESTER is not set
 # CONFIG_DEBUG_SPINLOCK is not set
 # CONFIG_DEBUG_MUTEXES is not set
 # CONFIG_DEBUG_SPINLOCK_SLEEP is not set
@@ -1207,7 +1230,6 @@ CONFIG_DEBUG_INFO=y
 # CONFIG_BACKTRACE_SELF_TEST is not set
 # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
 # CONFIG_FAULT_INJECTION is not set
-# CONFIG_SYSCTL_SYSCALL_CHECK is not set
 
 #
 # Tracers
@@ -1223,16 +1245,20 @@ CONFIG_HAVE_ARCH_KGDB=y
 # CONFIG_DEBUG_STACK_USAGE is not set
 CONFIG_DEBUG_VERBOSE=y
 CONFIG_DEBUG_MMRS=y
-# CONFIG_DEBUG_HWERR is not set
-# CONFIG_DEBUG_DOUBLEFAULT is not set
+CONFIG_DEBUG_HWERR=y
+CONFIG_EXACT_HWERR=y
+CONFIG_DEBUG_DOUBLEFAULT=y
+CONFIG_DEBUG_DOUBLEFAULT_PRINT=y
+# CONFIG_DEBUG_DOUBLEFAULT_RESET is not set
+# CONFIG_DEBUG_ICACHE_CHECK is not set
 CONFIG_DEBUG_HUNT_FOR_ZERO=y
 CONFIG_DEBUG_BFIN_HWTRACE_ON=y
-CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF=y
-# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_ONE is not set
+# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF is not set
+CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_ONE=y
 # CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_TWO is not set
-CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION=0
+CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION=1
 # CONFIG_DEBUG_BFIN_HWTRACE_EXPAND is not set
-# CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE is not set
+CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE=y
 CONFIG_EARLY_PRINTK=y
 CONFIG_CPLB_INFO=y
 CONFIG_ACCESS_CHECK=y
@@ -1345,7 +1371,6 @@ CONFIG_CRC32=y
 # CONFIG_LIBCRC32C is not set
 CONFIG_ZLIB_INFLATE=y
 CONFIG_ZLIB_DEFLATE=m
-CONFIG_PLIST=y
 CONFIG_HAS_IOMEM=y
 CONFIG_HAS_IOPORT=y
 CONFIG_HAS_DMA=y
index 591f6edda4f791dedc3011c3042e09e5c460c3ed..f9ac20d55799e81a213f6baa9f76f43bd0589f06 100644 (file)
@@ -1,6 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.28.7
+# Linux kernel version: 2.6.28.10
+# Thu May 21 05:50:01 2009
 #
 # CONFIG_MMU is not set
 # CONFIG_FPU is not set
@@ -42,10 +43,11 @@ CONFIG_LOG_BUF_SHIFT=14
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_INITRAMFS_SOURCE=""
 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
-# CONFIG_SYSCTL is not set
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
 CONFIG_EMBEDDED=y
 CONFIG_UID16=y
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_ALL is not set
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
@@ -53,16 +55,15 @@ CONFIG_HOTPLUG=y
 CONFIG_PRINTK=y
 CONFIG_BUG=y
 # CONFIG_ELF_CORE is not set
-CONFIG_COMPAT_BRK=y
 CONFIG_BASE_FULL=y
 # CONFIG_FUTEX is not set
-CONFIG_ANON_INODES=y
 CONFIG_EPOLL=y
-CONFIG_SIGNALFD=y
-CONFIG_TIMERFD=y
-CONFIG_EVENTFD=y
+# CONFIG_SIGNALFD is not set
+# CONFIG_TIMERFD is not set
+# CONFIG_EVENTFD is not set
 # CONFIG_AIO is not set
 CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_COMPAT_BRK=y
 CONFIG_SLAB=y
 # CONFIG_SLUB is not set
 # CONFIG_SLOB is not set
@@ -71,7 +72,6 @@ CONFIG_SLAB=y
 CONFIG_HAVE_OPROFILE=y
 # CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
 CONFIG_SLABINFO=y
-CONFIG_RT_MUTEXES=y
 CONFIG_TINY_SHMEM=y
 CONFIG_BASE_SMALL=0
 CONFIG_MODULES=y
@@ -232,7 +232,10 @@ CONFIG_HZ=250
 CONFIG_SCHED_HRTICK=y
 CONFIG_GENERIC_TIME=y
 CONFIG_GENERIC_CLOCKEVENTS=y
+# CONFIG_TICKSOURCE_GPTMR0 is not set
+CONFIG_TICKSOURCE_CORETMR=y
 # CONFIG_CYCLES_CLOCKSOURCE is not set
+# CONFIG_GPTMR0_CLOCKSOURCE is not set
 CONFIG_TICK_ONESHOT=y
 # CONFIG_NO_HZ is not set
 CONFIG_HIGH_RES_TIMERS=y
@@ -390,7 +393,7 @@ CONFIG_IP_PNP=y
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_ARPD is not set
-CONFIG_SYN_COOKIES=y
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_AH is not set
 # CONFIG_INET_ESP is not set
 # CONFIG_INET_IPCOMP is not set
@@ -548,9 +551,7 @@ CONFIG_MTD_ROM=m
 #
 # CONFIG_MTD_COMPLEX_MAPPINGS is not set
 CONFIG_MTD_PHYSMAP=m
-CONFIG_MTD_PHYSMAP_START=0x20000000
-CONFIG_MTD_PHYSMAP_LEN=0x0
-CONFIG_MTD_PHYSMAP_BANKWIDTH=2
+# CONFIG_MTD_PHYSMAP_COMPAT is not set
 # CONFIG_MTD_UCLINUX is not set
 # CONFIG_MTD_PLATRAM is not set
 
@@ -649,9 +650,8 @@ CONFIG_BFIN_RX_DESC_NUM=20
 # CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
 # CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
 # CONFIG_B44 is not set
-CONFIG_NETDEV_1000=y
-# CONFIG_AX88180 is not set
-CONFIG_NETDEV_10000=y
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
 
 #
 # Wireless LAN
@@ -708,14 +708,14 @@ CONFIG_SERIO_LIBPS2=y
 # Character devices
 #
 # CONFIG_AD9960 is not set
-# CONFIG_SPI_ADC_BF533 is not set
-# CONFIG_BF5xx_PPIFCD is not set
+CONFIG_BFIN_DMA_INTERFACE=m
+# CONFIG_BFIN_PPI is not set
+# CONFIG_BFIN_PPIFCD is not set
 # CONFIG_BFIN_SIMPLE_TIMER is not set
-CONFIG_BF5xx_PPI=m
+# CONFIG_BFIN_SPI_ADC is not set
 CONFIG_BFIN_SPORT=m
 # CONFIG_BFIN_TIMER_LATENCY is not set
-# CONFIG_TWI_LCD is not set
-CONFIG_BFIN_DMA_INTERFACE=m
+# CONFIG_BFIN_TWI_LCD is not set
 CONFIG_SIMPLE_GPIO=m
 # CONFIG_VT is not set
 # CONFIG_DEVKMEM is not set
@@ -823,7 +823,30 @@ CONFIG_SPI_BFIN=y
 # CONFIG_SPI_SPIDEV is not set
 # CONFIG_SPI_TLE62X0 is not set
 CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
-# CONFIG_GPIOLIB is not set
+CONFIG_GPIOLIB=y
+# CONFIG_DEBUG_GPIO is not set
+CONFIG_GPIO_SYSFS=y
+
+#
+# Memory mapped GPIO expanders:
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
 # CONFIG_W1 is not set
 # CONFIG_POWER_SUPPLY is not set
 # CONFIG_HWMON is not set
@@ -1123,16 +1146,6 @@ CONFIG_SYSFS=y
 # CONFIG_BEFS_FS is not set
 # CONFIG_BFS_FS is not set
 # CONFIG_EFS_FS is not set
-CONFIG_YAFFS_FS=m
-CONFIG_YAFFS_YAFFS1=y
-# CONFIG_YAFFS_9BYTE_TAGS is not set
-# CONFIG_YAFFS_DOES_ECC is not set
-CONFIG_YAFFS_YAFFS2=y
-CONFIG_YAFFS_AUTO_YAFFS2=y
-# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set
-# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set
-# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set
-CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y
 CONFIG_JFFS2_FS=m
 CONFIG_JFFS2_FS_DEBUG=0
 CONFIG_JFFS2_FS_WRITEBUFFER=y
@@ -1144,6 +1157,16 @@ CONFIG_JFFS2_ZLIB=y
 # CONFIG_JFFS2_LZO is not set
 CONFIG_JFFS2_RTIME=y
 # CONFIG_JFFS2_RUBIN is not set
+CONFIG_YAFFS_FS=m
+CONFIG_YAFFS_YAFFS1=y
+# CONFIG_YAFFS_9BYTE_TAGS is not set
+# CONFIG_YAFFS_DOES_ECC is not set
+CONFIG_YAFFS_YAFFS2=y
+CONFIG_YAFFS_AUTO_YAFFS2=y
+# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set
+# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set
+# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set
+CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y
 # CONFIG_CRAMFS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_MINIX_FS is not set
@@ -1232,7 +1255,7 @@ CONFIG_FRAME_WARN=1024
 CONFIG_DEBUG_FS=y
 # CONFIG_HEADERS_CHECK is not set
 CONFIG_DEBUG_KERNEL=y
-# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_DEBUG_SHIRQ=y
 CONFIG_DETECT_SOFTLOCKUP=y
 # CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
 CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
@@ -1241,8 +1264,6 @@ CONFIG_SCHED_DEBUG=y
 # CONFIG_TIMER_STATS is not set
 # CONFIG_DEBUG_OBJECTS is not set
 # CONFIG_DEBUG_SLAB is not set
-# CONFIG_DEBUG_RT_MUTEXES is not set
-# CONFIG_RT_MUTEX_TESTER is not set
 # CONFIG_DEBUG_SPINLOCK is not set
 # CONFIG_DEBUG_MUTEXES is not set
 # CONFIG_DEBUG_SPINLOCK_SLEEP is not set
@@ -1262,7 +1283,6 @@ CONFIG_DEBUG_INFO=y
 # CONFIG_BACKTRACE_SELF_TEST is not set
 # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
 # CONFIG_FAULT_INJECTION is not set
-# CONFIG_SYSCTL_SYSCALL_CHECK is not set
 
 #
 # Tracers
@@ -1278,16 +1298,20 @@ CONFIG_HAVE_ARCH_KGDB=y
 # CONFIG_DEBUG_STACK_USAGE is not set
 CONFIG_DEBUG_VERBOSE=y
 CONFIG_DEBUG_MMRS=y
-# CONFIG_DEBUG_HWERR is not set
-# CONFIG_DEBUG_DOUBLEFAULT is not set
+CONFIG_DEBUG_HWERR=y
+CONFIG_EXACT_HWERR=y
+CONFIG_DEBUG_DOUBLEFAULT=y
+CONFIG_DEBUG_DOUBLEFAULT_PRINT=y
+# CONFIG_DEBUG_DOUBLEFAULT_RESET is not set
+# CONFIG_DEBUG_ICACHE_CHECK is not set
 CONFIG_DEBUG_HUNT_FOR_ZERO=y
 CONFIG_DEBUG_BFIN_HWTRACE_ON=y
-CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF=y
-# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_ONE is not set
+# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF is not set
+CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_ONE=y
 # CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_TWO is not set
-CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION=0
+CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION=1
 # CONFIG_DEBUG_BFIN_HWTRACE_EXPAND is not set
-# CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE is not set
+CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE=y
 CONFIG_EARLY_PRINTK=y
 CONFIG_CPLB_INFO=y
 CONFIG_ACCESS_CHECK=y
@@ -1400,7 +1424,6 @@ CONFIG_CRC32=y
 # CONFIG_LIBCRC32C is not set
 CONFIG_ZLIB_INFLATE=y
 CONFIG_ZLIB_DEFLATE=m
-CONFIG_PLIST=y
 CONFIG_HAS_IOMEM=y
 CONFIG_HAS_IOPORT=y
 CONFIG_HAS_DMA=y
index 1a8e8c3adf980a0a7fcf4dcb2c9e71d319d7d7a3..ee98e227b887a47435275bb57148be95abcabbad 100644 (file)
@@ -1,6 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.28.7
+# Linux kernel version: 2.6.28.10
+# Thu May 21 05:50:01 2009
 #
 # CONFIG_MMU is not set
 # CONFIG_FPU is not set
@@ -42,10 +43,11 @@ CONFIG_LOG_BUF_SHIFT=14
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_INITRAMFS_SOURCE=""
 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
-# CONFIG_SYSCTL is not set
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
 CONFIG_EMBEDDED=y
 CONFIG_UID16=y
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_ALL is not set
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
@@ -53,16 +55,15 @@ CONFIG_HOTPLUG=y
 CONFIG_PRINTK=y
 CONFIG_BUG=y
 # CONFIG_ELF_CORE is not set
-CONFIG_COMPAT_BRK=y
 CONFIG_BASE_FULL=y
 # CONFIG_FUTEX is not set
-CONFIG_ANON_INODES=y
 CONFIG_EPOLL=y
-CONFIG_SIGNALFD=y
-CONFIG_TIMERFD=y
-CONFIG_EVENTFD=y
+# CONFIG_SIGNALFD is not set
+# CONFIG_TIMERFD is not set
+# CONFIG_EVENTFD is not set
 # CONFIG_AIO is not set
 CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_COMPAT_BRK=y
 CONFIG_SLAB=y
 # CONFIG_SLUB is not set
 # CONFIG_SLOB is not set
@@ -71,7 +72,6 @@ CONFIG_SLAB=y
 CONFIG_HAVE_OPROFILE=y
 # CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
 CONFIG_SLABINFO=y
-CONFIG_RT_MUTEXES=y
 CONFIG_TINY_SHMEM=y
 CONFIG_BASE_SMALL=0
 CONFIG_MODULES=y
@@ -243,7 +243,10 @@ CONFIG_HZ=250
 CONFIG_SCHED_HRTICK=y
 CONFIG_GENERIC_TIME=y
 CONFIG_GENERIC_CLOCKEVENTS=y
+# CONFIG_TICKSOURCE_GPTMR0 is not set
+CONFIG_TICKSOURCE_CORETMR=y
 # CONFIG_CYCLES_CLOCKSOURCE is not set
+# CONFIG_GPTMR0_CLOCKSOURCE is not set
 CONFIG_TICK_ONESHOT=y
 # CONFIG_NO_HZ is not set
 CONFIG_HIGH_RES_TIMERS=y
@@ -389,7 +392,7 @@ CONFIG_IP_PNP=y
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_ARPD is not set
-CONFIG_SYN_COOKIES=y
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_AH is not set
 # CONFIG_INET_ESP is not set
 # CONFIG_INET_IPCOMP is not set
@@ -546,9 +549,7 @@ CONFIG_MTD_ROM=m
 #
 # CONFIG_MTD_COMPLEX_MAPPINGS is not set
 CONFIG_MTD_PHYSMAP=m
-CONFIG_MTD_PHYSMAP_START=0x20000000
-CONFIG_MTD_PHYSMAP_LEN=0x0
-CONFIG_MTD_PHYSMAP_BANKWIDTH=2
+# CONFIG_MTD_PHYSMAP_COMPAT is not set
 # CONFIG_MTD_UCLINUX is not set
 # CONFIG_MTD_PLATRAM is not set
 
@@ -691,11 +692,11 @@ CONFIG_INPUT_EVDEV=m
 # CONFIG_INPUT_JOYSTICK is not set
 # CONFIG_INPUT_TABLET is not set
 CONFIG_INPUT_TOUCHSCREEN=y
-# CONFIG_TOUCHSCREEN_ADS7846 is not set
 # CONFIG_TOUCHSCREEN_AD7877 is not set
 # CONFIG_TOUCHSCREEN_AD7879_I2C is not set
 CONFIG_TOUCHSCREEN_AD7879_SPI=y
 CONFIG_TOUCHSCREEN_AD7879=y
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
 # CONFIG_TOUCHSCREEN_FUJITSU is not set
 # CONFIG_TOUCHSCREEN_GUNZE is not set
 # CONFIG_TOUCHSCREEN_ELO is not set
@@ -720,14 +721,14 @@ CONFIG_INPUT_MISC=y
 # Character devices
 #
 # CONFIG_AD9960 is not set
-# CONFIG_SPI_ADC_BF533 is not set
-# CONFIG_BF5xx_PPIFCD is not set
+CONFIG_BFIN_DMA_INTERFACE=m
+# CONFIG_BFIN_PPI is not set
+# CONFIG_BFIN_PPIFCD is not set
 # CONFIG_BFIN_SIMPLE_TIMER is not set
-CONFIG_BF5xx_PPI=m
+# CONFIG_BFIN_SPI_ADC is not set
 CONFIG_BFIN_SPORT=m
 # CONFIG_BFIN_TIMER_LATENCY is not set
-# CONFIG_TWI_LCD is not set
-CONFIG_BFIN_DMA_INTERFACE=m
+# CONFIG_BFIN_TWI_LCD is not set
 CONFIG_SIMPLE_GPIO=m
 # CONFIG_VT is not set
 # CONFIG_DEVKMEM is not set
@@ -833,7 +834,30 @@ CONFIG_SPI_BFIN=y
 # CONFIG_SPI_SPIDEV is not set
 # CONFIG_SPI_TLE62X0 is not set
 CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
-# CONFIG_GPIOLIB is not set
+CONFIG_GPIOLIB=y
+# CONFIG_DEBUG_GPIO is not set
+CONFIG_GPIO_SYSFS=y
+
+#
+# Memory mapped GPIO expanders:
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
 # CONFIG_W1 is not set
 # CONFIG_POWER_SUPPLY is not set
 # CONFIG_HWMON is not set
@@ -1056,16 +1080,6 @@ CONFIG_SYSFS=y
 # CONFIG_BEFS_FS is not set
 # CONFIG_BFS_FS is not set
 # CONFIG_EFS_FS is not set
-CONFIG_YAFFS_FS=m
-CONFIG_YAFFS_YAFFS1=y
-# CONFIG_YAFFS_9BYTE_TAGS is not set
-# CONFIG_YAFFS_DOES_ECC is not set
-CONFIG_YAFFS_YAFFS2=y
-CONFIG_YAFFS_AUTO_YAFFS2=y
-# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set
-# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set
-# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set
-CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y
 CONFIG_JFFS2_FS=m
 CONFIG_JFFS2_FS_DEBUG=0
 CONFIG_JFFS2_FS_WRITEBUFFER=y
@@ -1077,6 +1091,16 @@ CONFIG_JFFS2_ZLIB=y
 # CONFIG_JFFS2_LZO is not set
 CONFIG_JFFS2_RTIME=y
 # CONFIG_JFFS2_RUBIN is not set
+CONFIG_YAFFS_FS=m
+CONFIG_YAFFS_YAFFS1=y
+# CONFIG_YAFFS_9BYTE_TAGS is not set
+# CONFIG_YAFFS_DOES_ECC is not set
+CONFIG_YAFFS_YAFFS2=y
+CONFIG_YAFFS_AUTO_YAFFS2=y
+# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set
+# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set
+# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set
+CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y
 # CONFIG_CRAMFS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_MINIX_FS is not set
@@ -1165,7 +1189,7 @@ CONFIG_FRAME_WARN=1024
 CONFIG_DEBUG_FS=y
 # CONFIG_HEADERS_CHECK is not set
 CONFIG_DEBUG_KERNEL=y
-# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_DEBUG_SHIRQ=y
 CONFIG_DETECT_SOFTLOCKUP=y
 # CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
 CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
@@ -1174,8 +1198,6 @@ CONFIG_SCHED_DEBUG=y
 # CONFIG_TIMER_STATS is not set
 # CONFIG_DEBUG_OBJECTS is not set
 # CONFIG_DEBUG_SLAB is not set
-# CONFIG_DEBUG_RT_MUTEXES is not set
-# CONFIG_RT_MUTEX_TESTER is not set
 # CONFIG_DEBUG_SPINLOCK is not set
 # CONFIG_DEBUG_MUTEXES is not set
 # CONFIG_DEBUG_SPINLOCK_SLEEP is not set
@@ -1195,7 +1217,6 @@ CONFIG_DEBUG_INFO=y
 # CONFIG_BACKTRACE_SELF_TEST is not set
 # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
 # CONFIG_FAULT_INJECTION is not set
-CONFIG_SYSCTL_SYSCALL_CHECK=y
 
 #
 # Tracers
@@ -1211,16 +1232,20 @@ CONFIG_HAVE_ARCH_KGDB=y
 # CONFIG_DEBUG_STACK_USAGE is not set
 CONFIG_DEBUG_VERBOSE=y
 CONFIG_DEBUG_MMRS=y
-# CONFIG_DEBUG_HWERR is not set
-# CONFIG_DEBUG_DOUBLEFAULT is not set
+CONFIG_DEBUG_HWERR=y
+CONFIG_EXACT_HWERR=y
+CONFIG_DEBUG_DOUBLEFAULT=y
+CONFIG_DEBUG_DOUBLEFAULT_PRINT=y
+# CONFIG_DEBUG_DOUBLEFAULT_RESET is not set
+# CONFIG_DEBUG_ICACHE_CHECK is not set
 CONFIG_DEBUG_HUNT_FOR_ZERO=y
 CONFIG_DEBUG_BFIN_HWTRACE_ON=y
-CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF=y
-# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_ONE is not set
+# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF is not set
+CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_ONE=y
 # CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_TWO is not set
-CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION=0
+CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION=1
 # CONFIG_DEBUG_BFIN_HWTRACE_EXPAND is not set
-# CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE is not set
+CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE=y
 CONFIG_EARLY_PRINTK=y
 CONFIG_CPLB_INFO=y
 CONFIG_ACCESS_CHECK=y
@@ -1333,7 +1358,6 @@ CONFIG_CRC32=y
 # CONFIG_LIBCRC32C is not set
 CONFIG_ZLIB_INFLATE=y
 CONFIG_ZLIB_DEFLATE=m
-CONFIG_PLIST=y
 CONFIG_HAS_IOMEM=y
 CONFIG_HAS_IOPORT=y
 CONFIG_HAS_DMA=y
index 2cd1c2b218d7ce63a6cd64ec449af55262a79c7f..deeabef8ab805a04e0be4cf39b6998b6b7333a65 100644 (file)
@@ -1,6 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.28.7
+# Linux kernel version: 2.6.28.10
+# Thu May 21 05:50:01 2009
 #
 # CONFIG_MMU is not set
 # CONFIG_FPU is not set
@@ -42,10 +43,11 @@ CONFIG_LOG_BUF_SHIFT=14
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_INITRAMFS_SOURCE=""
 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
-# CONFIG_SYSCTL is not set
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
 CONFIG_EMBEDDED=y
 CONFIG_UID16=y
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_ALL is not set
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
@@ -53,16 +55,15 @@ CONFIG_HOTPLUG=y
 CONFIG_PRINTK=y
 CONFIG_BUG=y
 # CONFIG_ELF_CORE is not set
-CONFIG_COMPAT_BRK=y
 CONFIG_BASE_FULL=y
 # CONFIG_FUTEX is not set
-CONFIG_ANON_INODES=y
 CONFIG_EPOLL=y
-CONFIG_SIGNALFD=y
-CONFIG_TIMERFD=y
-CONFIG_EVENTFD=y
+# CONFIG_SIGNALFD is not set
+# CONFIG_TIMERFD is not set
+# CONFIG_EVENTFD is not set
 # CONFIG_AIO is not set
 CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_COMPAT_BRK=y
 CONFIG_SLAB=y
 # CONFIG_SLUB is not set
 # CONFIG_SLOB is not set
@@ -71,7 +72,6 @@ CONFIG_SLAB=y
 CONFIG_HAVE_OPROFILE=y
 # CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
 CONFIG_SLABINFO=y
-CONFIG_RT_MUTEXES=y
 CONFIG_TINY_SHMEM=y
 CONFIG_BASE_SMALL=0
 CONFIG_MODULES=y
@@ -196,6 +196,7 @@ CONFIG_BFIN548_EZKIT=y
 # BF548 Specific Configuration
 #
 # CONFIG_DEB_DMA_URGENT is not set
+# CONFIG_BF548_ATAPI_ALTERNATIVE_PORT is not set
 
 #
 # Interrupt Priority Assignment
@@ -298,7 +299,10 @@ CONFIG_HZ=250
 # CONFIG_SCHED_HRTICK is not set
 CONFIG_GENERIC_TIME=y
 CONFIG_GENERIC_CLOCKEVENTS=y
+# CONFIG_TICKSOURCE_GPTMR0 is not set
+CONFIG_TICKSOURCE_CORETMR=y
 # CONFIG_CYCLES_CLOCKSOURCE is not set
+# CONFIG_GPTMR0_CLOCKSOURCE is not set
 # CONFIG_NO_HZ is not set
 # CONFIG_HIGH_RES_TIMERS is not set
 CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
@@ -367,7 +371,9 @@ CONFIG_BFIN_DCACHE=y
 # CONFIG_BFIN_ICACHE_LOCK is not set
 CONFIG_BFIN_WB=y
 # CONFIG_BFIN_WT is not set
-# CONFIG_BFIN_L2_CACHEABLE is not set
+# CONFIG_BFIN_L2_WB is not set
+CONFIG_BFIN_L2_WT=y
+# CONFIG_BFIN_L2_NOT_CACHED is not set
 # CONFIG_MPU is not set
 
 #
@@ -447,7 +453,7 @@ CONFIG_IP_PNP=y
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_ARPD is not set
-CONFIG_SYN_COOKIES=y
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_AH is not set
 # CONFIG_INET_ESP is not set
 # CONFIG_INET_IPCOMP is not set
@@ -616,9 +622,7 @@ CONFIG_MTD_RAM=y
 #
 CONFIG_MTD_COMPLEX_MAPPINGS=y
 CONFIG_MTD_PHYSMAP=y
-CONFIG_MTD_PHYSMAP_START=0x20000000
-CONFIG_MTD_PHYSMAP_LEN=0
-CONFIG_MTD_PHYSMAP_BANKWIDTH=2
+# CONFIG_MTD_PHYSMAP_COMPAT is not set
 # CONFIG_MTD_GPIO_ADDR is not set
 # CONFIG_MTD_UCLINUX is not set
 # CONFIG_MTD_PLATRAM is not set
@@ -696,7 +700,7 @@ CONFIG_SCSI_DMA=y
 CONFIG_BLK_DEV_SD=y
 # CONFIG_CHR_DEV_ST is not set
 # CONFIG_CHR_DEV_OSST is not set
-CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR=m
 # CONFIG_BLK_DEV_SR_VENDOR is not set
 # CONFIG_CHR_DEV_SG is not set
 # CONFIG_CHR_DEV_SCH is not set
@@ -718,9 +722,7 @@ CONFIG_SCSI_WAIT_SCAN=m
 # CONFIG_SCSI_ISCSI_ATTRS is not set
 # CONFIG_SCSI_SAS_LIBSAS is not set
 # CONFIG_SCSI_SRP_ATTRS is not set
-CONFIG_SCSI_LOWLEVEL=y
-# CONFIG_ISCSI_TCP is not set
-# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_LOWLEVEL is not set
 # CONFIG_SCSI_DH is not set
 CONFIG_ATA=y
 # CONFIG_ATA_NONSTANDARD is not set
@@ -752,9 +754,8 @@ CONFIG_SMSC911X=y
 # CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
 # CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
 # CONFIG_B44 is not set
-CONFIG_NETDEV_1000=y
-# CONFIG_AX88180 is not set
-CONFIG_NETDEV_10000=y
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
 
 #
 # Wireless LAN
@@ -821,11 +822,11 @@ CONFIG_KEYBOARD_BFIN=y
 # CONFIG_INPUT_JOYSTICK is not set
 # CONFIG_INPUT_TABLET is not set
 CONFIG_INPUT_TOUCHSCREEN=y
-# CONFIG_TOUCHSCREEN_ADS7846 is not set
 CONFIG_TOUCHSCREEN_AD7877=m
 # CONFIG_TOUCHSCREEN_AD7879_I2C is not set
 # CONFIG_TOUCHSCREEN_AD7879_SPI is not set
 # CONFIG_TOUCHSCREEN_AD7879 is not set
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
 # CONFIG_TOUCHSCREEN_FUJITSU is not set
 # CONFIG_TOUCHSCREEN_GUNZE is not set
 # CONFIG_TOUCHSCREEN_ELO is not set
@@ -858,14 +859,14 @@ CONFIG_INPUT_MISC=y
 # Character devices
 #
 # CONFIG_AD9960 is not set
-# CONFIG_SPI_ADC_BF533 is not set
-# CONFIG_BF5xx_PPIFCD is not set
+CONFIG_BFIN_DMA_INTERFACE=m
+# CONFIG_BFIN_PPI is not set
+# CONFIG_BFIN_PPIFCD is not set
 # CONFIG_BFIN_SIMPLE_TIMER is not set
-CONFIG_BF5xx_PPI=m
+# CONFIG_BFIN_SPI_ADC is not set
 CONFIG_BFIN_SPORT=m
 # CONFIG_BFIN_TIMER_LATENCY is not set
-# CONFIG_TWI_LCD is not set
-CONFIG_BFIN_DMA_INTERFACE=m
+# CONFIG_BFIN_TWI_LCD is not set
 CONFIG_SIMPLE_GPIO=m
 CONFIG_VT=y
 CONFIG_CONSOLE_TRANSLATIONS=y
@@ -977,7 +978,30 @@ CONFIG_SPI_BFIN=y
 # CONFIG_SPI_SPIDEV is not set
 # CONFIG_SPI_TLE62X0 is not set
 CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
-# CONFIG_GPIOLIB is not set
+CONFIG_GPIOLIB=y
+# CONFIG_DEBUG_GPIO is not set
+CONFIG_GPIO_SYSFS=y
+
+#
+# Memory mapped GPIO expanders:
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
 # CONFIG_W1 is not set
 # CONFIG_POWER_SUPPLY is not set
 # CONFIG_HWMON is not set
@@ -1011,6 +1035,7 @@ CONFIG_SSB_POSSIBLE=y
 # CONFIG_HTC_PASIC3 is not set
 # CONFIG_MFD_TMIO is not set
 # CONFIG_PMIC_DA903X is not set
+# CONFIG_PMIC_ADP5520 is not set
 # CONFIG_MFD_WM8400 is not set
 # CONFIG_MFD_WM8350_I2C is not set
 # CONFIG_REGULATOR is not set
@@ -1193,8 +1218,8 @@ CONFIG_USB=y
 #
 # Miscellaneous USB options
 #
-# CONFIG_USB_DEVICEFS is not set
-CONFIG_USB_DEVICE_CLASS=y
+CONFIG_USB_DEVICEFS=y
+# CONFIG_USB_DEVICE_CLASS is not set
 # CONFIG_USB_DYNAMIC_MINORS is not set
 # CONFIG_USB_OTG is not set
 # CONFIG_USB_OTG_WHITELIST is not set
@@ -1222,10 +1247,10 @@ CONFIG_USB_MUSB_SOC=y
 CONFIG_USB_MUSB_HOST=y
 # CONFIG_USB_MUSB_PERIPHERAL is not set
 # CONFIG_USB_MUSB_OTG is not set
-# CONFIG_USB_GADGET_MUSB_HDRC is not set
 CONFIG_USB_MUSB_HDRC_HCD=y
-CONFIG_MUSB_PIO_ONLY=y
-CONFIG_MUSB_DMA_POLL=y
+# CONFIG_MUSB_PIO_ONLY is not set
+CONFIG_USB_INVENTRA_DMA=y
+# CONFIG_USB_TI_CPPI_DMA is not set
 # CONFIG_USB_MUSB_DEBUG is not set
 
 #
@@ -1243,7 +1268,7 @@ CONFIG_MUSB_DMA_POLL=y
 #
 # see USB_STORAGE Help for more information
 #
-CONFIG_USB_STORAGE=m
+CONFIG_USB_STORAGE=y
 # CONFIG_USB_STORAGE_DEBUG is not set
 # CONFIG_USB_STORAGE_DATAFAB is not set
 # CONFIG_USB_STORAGE_FREECOM is not set
@@ -1292,33 +1317,10 @@ CONFIG_USB_STORAGE=m
 # CONFIG_USB_LD is not set
 # CONFIG_USB_TRANCEVIBRATOR is not set
 # CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
 # CONFIG_USB_ISIGHTFW is not set
 # CONFIG_USB_VST is not set
 # CONFIG_USB_GADGET is not set
-# CONFIG_USB_GADGET_AT91 is not set
-# CONFIG_USB_GADGET_ATMEL_USBA is not set
-# CONFIG_USB_GADGET_FSL_USB2 is not set
-# CONFIG_USB_GADGET_LH7A40X is not set
-# CONFIG_USB_GADGET_OMAP is not set
-# CONFIG_USB_GADGET_PXA25X is not set
-# CONFIG_USB_GADGET_PXA27X is not set
-# CONFIG_USB_GADGET_S3C2410 is not set
-# CONFIG_USB_GADGET_M66592 is not set
-# CONFIG_USB_GADGET_AMD5536UDC is not set
-# CONFIG_USB_GADGET_FSL_QE is not set
-# CONFIG_USB_GADGET_NET2272 is not set
-# CONFIG_USB_GADGET_NET2280 is not set
-# CONFIG_USB_GADGET_GOKU is not set
-# CONFIG_USB_GADGET_DUMMY_HCD is not set
-# CONFIG_USB_ZERO is not set
-# CONFIG_USB_AUDIO is not set
-# CONFIG_USB_ETH is not set
-# CONFIG_USB_GADGETFS is not set
-# CONFIG_USB_FILE_STORAGE is not set
-# CONFIG_USB_G_SERIAL is not set
-# CONFIG_USB_MIDI_GADGET is not set
-# CONFIG_USB_G_PRINTER is not set
-# CONFIG_USB_CDC_COMPOSITE is not set
 CONFIG_MMC=y
 # CONFIG_MMC_DEBUG is not set
 # CONFIG_MMC_UNSAFE_RESUME is not set
@@ -1414,13 +1416,8 @@ CONFIG_EXT2_FS=y
 CONFIG_EXT2_FS_XATTR=y
 # CONFIG_EXT2_FS_POSIX_ACL is not set
 # CONFIG_EXT2_FS_SECURITY is not set
-CONFIG_EXT3_FS=y
-CONFIG_EXT3_FS_XATTR=y
-# CONFIG_EXT3_FS_POSIX_ACL is not set
-# CONFIG_EXT3_FS_SECURITY is not set
+# CONFIG_EXT3_FS is not set
 # CONFIG_EXT4_FS is not set
-CONFIG_JBD=y
-# CONFIG_JBD_DEBUG is not set
 CONFIG_FS_MBCACHE=y
 # CONFIG_REISERFS_FS is not set
 # CONFIG_JFS_FS is not set
@@ -1476,16 +1473,6 @@ CONFIG_SYSFS=y
 # CONFIG_BEFS_FS is not set
 # CONFIG_BFS_FS is not set
 # CONFIG_EFS_FS is not set
-CONFIG_YAFFS_FS=m
-CONFIG_YAFFS_YAFFS1=y
-# CONFIG_YAFFS_9BYTE_TAGS is not set
-# CONFIG_YAFFS_DOES_ECC is not set
-CONFIG_YAFFS_YAFFS2=y
-CONFIG_YAFFS_AUTO_YAFFS2=y
-# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set
-# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set
-# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set
-CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y
 CONFIG_JFFS2_FS=m
 CONFIG_JFFS2_FS_DEBUG=0
 CONFIG_JFFS2_FS_WRITEBUFFER=y
@@ -1497,6 +1484,16 @@ CONFIG_JFFS2_ZLIB=y
 # CONFIG_JFFS2_LZO is not set
 CONFIG_JFFS2_RTIME=y
 # CONFIG_JFFS2_RUBIN is not set
+CONFIG_YAFFS_FS=m
+CONFIG_YAFFS_YAFFS1=y
+# CONFIG_YAFFS_9BYTE_TAGS is not set
+# CONFIG_YAFFS_DOES_ECC is not set
+CONFIG_YAFFS_YAFFS2=y
+CONFIG_YAFFS_AUTO_YAFFS2=y
+# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set
+# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set
+# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set
+CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y
 # CONFIG_CRAMFS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_MINIX_FS is not set
@@ -1539,63 +1536,47 @@ CONFIG_CIFS=y
 #
 # 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_PARTITION_ADVANCED is not set
 CONFIG_MSDOS_PARTITION=y
-# CONFIG_BSD_DISKLABEL is not set
-# CONFIG_MINIX_SUBPARTITION is not set
-# CONFIG_SOLARIS_X86_PARTITION is not set
-# CONFIG_UNIXWARE_DISKLABEL is not set
-# CONFIG_LDM_PARTITION is not set
-# CONFIG_SGI_PARTITION is not set
-# CONFIG_ULTRIX_PARTITION is not set
-# CONFIG_SUN_PARTITION is not set
-# CONFIG_KARMA_PARTITION is not set
-# CONFIG_EFI_PARTITION is not set
-# CONFIG_SYSV68_PARTITION is not set
 CONFIG_NLS=y
 CONFIG_NLS_DEFAULT="iso8859-1"
 CONFIG_NLS_CODEPAGE_437=m
-CONFIG_NLS_CODEPAGE_737=m
-CONFIG_NLS_CODEPAGE_775=m
-CONFIG_NLS_CODEPAGE_850=m
-CONFIG_NLS_CODEPAGE_852=m
-CONFIG_NLS_CODEPAGE_855=m
-CONFIG_NLS_CODEPAGE_857=m
-CONFIG_NLS_CODEPAGE_860=m
-CONFIG_NLS_CODEPAGE_861=m
-CONFIG_NLS_CODEPAGE_862=m
-CONFIG_NLS_CODEPAGE_863=m
-CONFIG_NLS_CODEPAGE_864=m
-CONFIG_NLS_CODEPAGE_865=m
-CONFIG_NLS_CODEPAGE_866=m
-CONFIG_NLS_CODEPAGE_869=m
+# CONFIG_NLS_CODEPAGE_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=m
-CONFIG_NLS_CODEPAGE_950=m
-CONFIG_NLS_CODEPAGE_932=m
-CONFIG_NLS_CODEPAGE_949=m
-CONFIG_NLS_CODEPAGE_874=m
-CONFIG_NLS_ISO8859_8=m
-CONFIG_NLS_CODEPAGE_1250=m
-CONFIG_NLS_CODEPAGE_1251=m
-CONFIG_NLS_ASCII=m
+# CONFIG_NLS_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=m
-CONFIG_NLS_ISO8859_2=m
-CONFIG_NLS_ISO8859_3=m
-CONFIG_NLS_ISO8859_4=m
-CONFIG_NLS_ISO8859_5=m
-CONFIG_NLS_ISO8859_6=m
-CONFIG_NLS_ISO8859_7=m
-CONFIG_NLS_ISO8859_9=m
-CONFIG_NLS_ISO8859_13=m
-CONFIG_NLS_ISO8859_14=m
-CONFIG_NLS_ISO8859_15=m
-CONFIG_NLS_KOI8_R=m
-CONFIG_NLS_KOI8_U=m
+# CONFIG_NLS_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=m
 # CONFIG_DLM is not set
 
@@ -1611,7 +1592,7 @@ CONFIG_FRAME_WARN=1024
 CONFIG_DEBUG_FS=y
 # CONFIG_HEADERS_CHECK is not set
 CONFIG_DEBUG_KERNEL=y
-# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_DEBUG_SHIRQ=y
 CONFIG_DETECT_SOFTLOCKUP=y
 # CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
 CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
@@ -1620,8 +1601,6 @@ CONFIG_SCHED_DEBUG=y
 # CONFIG_TIMER_STATS is not set
 # CONFIG_DEBUG_OBJECTS is not set
 # CONFIG_DEBUG_SLAB is not set
-# CONFIG_DEBUG_RT_MUTEXES is not set
-# CONFIG_RT_MUTEX_TESTER is not set
 # CONFIG_DEBUG_SPINLOCK is not set
 # CONFIG_DEBUG_MUTEXES is not set
 # CONFIG_DEBUG_SPINLOCK_SLEEP is not set
@@ -1641,7 +1620,6 @@ CONFIG_DEBUG_INFO=y
 # CONFIG_BACKTRACE_SELF_TEST is not set
 # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
 # CONFIG_FAULT_INJECTION is not set
-# CONFIG_SYSCTL_SYSCALL_CHECK is not set
 
 #
 # Tracers
@@ -1657,16 +1635,20 @@ CONFIG_HAVE_ARCH_KGDB=y
 # CONFIG_DEBUG_STACK_USAGE is not set
 CONFIG_DEBUG_VERBOSE=y
 CONFIG_DEBUG_MMRS=y
-# CONFIG_DEBUG_HWERR is not set
-# CONFIG_DEBUG_DOUBLEFAULT is not set
+CONFIG_DEBUG_HWERR=y
+CONFIG_EXACT_HWERR=y
+CONFIG_DEBUG_DOUBLEFAULT=y
+CONFIG_DEBUG_DOUBLEFAULT_PRINT=y
+# CONFIG_DEBUG_DOUBLEFAULT_RESET is not set
+# CONFIG_DEBUG_ICACHE_CHECK is not set
 CONFIG_DEBUG_HUNT_FOR_ZERO=y
 CONFIG_DEBUG_BFIN_HWTRACE_ON=y
-CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF=y
-# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_ONE is not set
+# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF is not set
+CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_ONE=y
 # CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_TWO is not set
-CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION=0
+CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION=1
 # CONFIG_DEBUG_BFIN_HWTRACE_EXPAND is not set
-# CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE is not set
+CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE=y
 CONFIG_EARLY_PRINTK=y
 CONFIG_CPLB_INFO=y
 CONFIG_ACCESS_CHECK=y
@@ -1780,7 +1762,6 @@ CONFIG_CRC32=y
 # CONFIG_LIBCRC32C is not set
 CONFIG_ZLIB_INFLATE=y
 CONFIG_ZLIB_DEFLATE=m
-CONFIG_PLIST=y
 CONFIG_HAS_IOMEM=y
 CONFIG_HAS_IOPORT=y
 CONFIG_HAS_DMA=y
index 4a6ea8e31df7443e4fdebd0a8cff92aadce18edf..dcfbe2e2931e57105a29a45314d436310360dbe6 100644 (file)
@@ -1,6 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.28.7
+# Linux kernel version: 2.6.28.10
+# Thu May 21 05:50:01 2009
 #
 # CONFIG_MMU is not set
 # CONFIG_FPU is not set
@@ -42,10 +43,11 @@ CONFIG_LOG_BUF_SHIFT=14
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_INITRAMFS_SOURCE=""
 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
-# CONFIG_SYSCTL is not set
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
 CONFIG_EMBEDDED=y
 CONFIG_UID16=y
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_ALL is not set
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
@@ -53,16 +55,15 @@ CONFIG_HOTPLUG=y
 CONFIG_PRINTK=y
 CONFIG_BUG=y
 # CONFIG_ELF_CORE is not set
-CONFIG_COMPAT_BRK=y
 CONFIG_BASE_FULL=y
 # CONFIG_FUTEX is not set
-CONFIG_ANON_INODES=y
 CONFIG_EPOLL=y
-CONFIG_SIGNALFD=y
-CONFIG_TIMERFD=y
-CONFIG_EVENTFD=y
+# CONFIG_SIGNALFD is not set
+# CONFIG_TIMERFD is not set
+# CONFIG_EVENTFD is not set
 # CONFIG_AIO is not set
 CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_COMPAT_BRK=y
 CONFIG_SLAB=y
 # CONFIG_SLUB is not set
 # CONFIG_SLOB is not set
@@ -71,7 +72,6 @@ CONFIG_SLAB=y
 CONFIG_HAVE_OPROFILE=y
 # CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
 CONFIG_SLABINFO=y
-CONFIG_RT_MUTEXES=y
 CONFIG_TINY_SHMEM=y
 CONFIG_BASE_SMALL=0
 CONFIG_MODULES=y
@@ -148,9 +148,9 @@ CONFIG_BF_REV_MAX=5
 # CONFIG_BF_REV_0_0 is not set
 # CONFIG_BF_REV_0_1 is not set
 # CONFIG_BF_REV_0_2 is not set
-CONFIG_BF_REV_0_3=y
+# CONFIG_BF_REV_0_3 is not set
 # CONFIG_BF_REV_0_4 is not set
-# CONFIG_BF_REV_0_5 is not set
+CONFIG_BF_REV_0_5=y
 # CONFIG_BF_REV_0_6 is not set
 # CONFIG_BF_REV_ANY is not set
 # CONFIG_BF_REV_NONE is not set
@@ -179,7 +179,6 @@ CONFIG_BFIN561_EZKIT=y
 # Core B Support
 #
 CONFIG_BF561_COREB=y
-CONFIG_BF561_COREB_RESET=y
 
 #
 # Interrupt Priority Assignment
@@ -264,7 +263,10 @@ CONFIG_HZ=250
 CONFIG_SCHED_HRTICK=y
 CONFIG_GENERIC_TIME=y
 CONFIG_GENERIC_CLOCKEVENTS=y
+# CONFIG_TICKSOURCE_GPTMR0 is not set
+CONFIG_TICKSOURCE_CORETMR=y
 # CONFIG_CYCLES_CLOCKSOURCE is not set
+# CONFIG_GPTMR0_CLOCKSOURCE is not set
 CONFIG_TICK_ONESHOT=y
 # CONFIG_NO_HZ is not set
 CONFIG_HIGH_RES_TIMERS=y
@@ -334,7 +336,9 @@ CONFIG_BFIN_DCACHE=y
 # CONFIG_BFIN_ICACHE_LOCK is not set
 CONFIG_BFIN_WB=y
 # CONFIG_BFIN_WT is not set
-# CONFIG_BFIN_L2_CACHEABLE is not set
+# CONFIG_BFIN_L2_WB is not set
+CONFIG_BFIN_L2_WT=y
+# CONFIG_BFIN_L2_NOT_CACHED is not set
 # CONFIG_MPU is not set
 
 #
@@ -415,7 +419,7 @@ CONFIG_IP_PNP=y
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_ARPD is not set
-CONFIG_SYN_COOKIES=y
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_AH is not set
 # CONFIG_INET_ESP is not set
 # CONFIG_INET_IPCOMP is not set
@@ -570,9 +574,7 @@ CONFIG_MTD_ROM=m
 #
 # CONFIG_MTD_COMPLEX_MAPPINGS is not set
 CONFIG_MTD_PHYSMAP=m
-CONFIG_MTD_PHYSMAP_START=0x20000000
-CONFIG_MTD_PHYSMAP_LEN=0x0
-CONFIG_MTD_PHYSMAP_BANKWIDTH=2
+# CONFIG_MTD_PHYSMAP_COMPAT is not set
 # CONFIG_MTD_UCLINUX is not set
 # CONFIG_MTD_PLATRAM is not set
 
@@ -649,9 +651,8 @@ CONFIG_SMC91X=y
 # CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
 # CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
 # CONFIG_B44 is not set
-CONFIG_NETDEV_1000=y
-# CONFIG_AX88180 is not set
-CONFIG_NETDEV_10000=y
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
 
 #
 # Wireless LAN
@@ -703,13 +704,13 @@ CONFIG_INPUT_EVDEV=m
 # Character devices
 #
 # CONFIG_AD9960 is not set
-# CONFIG_SPI_ADC_BF533 is not set
-# CONFIG_BF5xx_PPIFCD is not set
+CONFIG_BFIN_DMA_INTERFACE=m
+# CONFIG_BFIN_PPI is not set
+# CONFIG_BFIN_PPIFCD is not set
 # CONFIG_BFIN_SIMPLE_TIMER is not set
-# CONFIG_BF5xx_PPI is not set
+# CONFIG_BFIN_SPI_ADC is not set
 # CONFIG_BFIN_SPORT is not set
 # CONFIG_BFIN_TIMER_LATENCY is not set
-CONFIG_BFIN_DMA_INTERFACE=m
 CONFIG_SIMPLE_GPIO=m
 # CONFIG_VT is not set
 # CONFIG_DEVKMEM is not set
@@ -765,7 +766,30 @@ CONFIG_SPI_BFIN=y
 # CONFIG_SPI_SPIDEV is not set
 # CONFIG_SPI_TLE62X0 is not set
 CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
-# CONFIG_GPIOLIB is not set
+CONFIG_GPIOLIB=y
+# CONFIG_DEBUG_GPIO is not set
+CONFIG_GPIO_SYSFS=y
+
+#
+# Memory mapped GPIO expanders:
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
 # CONFIG_W1 is not set
 # CONFIG_POWER_SUPPLY is not set
 # CONFIG_HWMON is not set
@@ -897,16 +921,6 @@ CONFIG_SYSFS=y
 # CONFIG_BEFS_FS is not set
 # CONFIG_BFS_FS is not set
 # CONFIG_EFS_FS is not set
-CONFIG_YAFFS_FS=m
-CONFIG_YAFFS_YAFFS1=y
-# CONFIG_YAFFS_9BYTE_TAGS is not set
-# CONFIG_YAFFS_DOES_ECC is not set
-CONFIG_YAFFS_YAFFS2=y
-CONFIG_YAFFS_AUTO_YAFFS2=y
-# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set
-# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set
-# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set
-CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y
 CONFIG_JFFS2_FS=m
 CONFIG_JFFS2_FS_DEBUG=0
 CONFIG_JFFS2_FS_WRITEBUFFER=y
@@ -918,6 +932,16 @@ CONFIG_JFFS2_ZLIB=y
 # CONFIG_JFFS2_LZO is not set
 CONFIG_JFFS2_RTIME=y
 # CONFIG_JFFS2_RUBIN is not set
+CONFIG_YAFFS_FS=m
+CONFIG_YAFFS_YAFFS1=y
+# CONFIG_YAFFS_9BYTE_TAGS is not set
+# CONFIG_YAFFS_DOES_ECC is not set
+CONFIG_YAFFS_YAFFS2=y
+CONFIG_YAFFS_AUTO_YAFFS2=y
+# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set
+# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set
+# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set
+CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y
 # CONFIG_CRAMFS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_MINIX_FS is not set
@@ -1006,7 +1030,7 @@ CONFIG_FRAME_WARN=1024
 CONFIG_DEBUG_FS=y
 # CONFIG_HEADERS_CHECK is not set
 CONFIG_DEBUG_KERNEL=y
-# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_DEBUG_SHIRQ=y
 CONFIG_DETECT_SOFTLOCKUP=y
 # CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
 CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
@@ -1015,8 +1039,6 @@ CONFIG_SCHED_DEBUG=y
 # CONFIG_TIMER_STATS is not set
 # CONFIG_DEBUG_OBJECTS is not set
 # CONFIG_DEBUG_SLAB is not set
-# CONFIG_DEBUG_RT_MUTEXES is not set
-# CONFIG_RT_MUTEX_TESTER is not set
 # CONFIG_DEBUG_SPINLOCK is not set
 # CONFIG_DEBUG_MUTEXES is not set
 # CONFIG_DEBUG_SPINLOCK_SLEEP is not set
@@ -1036,7 +1058,6 @@ CONFIG_DEBUG_INFO=y
 # CONFIG_BACKTRACE_SELF_TEST is not set
 # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
 # CONFIG_FAULT_INJECTION is not set
-# CONFIG_SYSCTL_SYSCALL_CHECK is not set
 
 #
 # Tracers
@@ -1052,16 +1073,20 @@ CONFIG_HAVE_ARCH_KGDB=y
 # CONFIG_DEBUG_STACK_USAGE is not set
 CONFIG_DEBUG_VERBOSE=y
 CONFIG_DEBUG_MMRS=y
-# CONFIG_DEBUG_HWERR is not set
-# CONFIG_DEBUG_DOUBLEFAULT is not set
+CONFIG_DEBUG_HWERR=y
+CONFIG_EXACT_HWERR=y
+CONFIG_DEBUG_DOUBLEFAULT=y
+CONFIG_DEBUG_DOUBLEFAULT_PRINT=y
+# CONFIG_DEBUG_DOUBLEFAULT_RESET is not set
+# CONFIG_DEBUG_ICACHE_CHECK is not set
 CONFIG_DEBUG_HUNT_FOR_ZERO=y
 CONFIG_DEBUG_BFIN_HWTRACE_ON=y
-CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF=y
-# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_ONE is not set
+# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF is not set
+CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_ONE=y
 # CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_TWO is not set
-CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION=0
+CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION=1
 # CONFIG_DEBUG_BFIN_HWTRACE_EXPAND is not set
-# CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE is not set
+CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE=y
 CONFIG_EARLY_PRINTK=y
 CONFIG_CPLB_INFO=y
 CONFIG_ACCESS_CHECK=y
@@ -1174,7 +1199,6 @@ CONFIG_CRC32=y
 # CONFIG_LIBCRC32C is not set
 CONFIG_ZLIB_INFLATE=y
 CONFIG_ZLIB_DEFLATE=m
-CONFIG_PLIST=y
 CONFIG_HAS_IOMEM=y
 CONFIG_HAS_IOPORT=y
 CONFIG_HAS_DMA=y
index ef1a2c84ace1614c6f56443baf0cfeac3b024794..174c578b8ec4ffb5165a80fa5660cce933e20d50 100644 (file)
@@ -46,7 +46,7 @@ CONFIG_INITRAMFS_SOURCE=""
 # CONFIG_SYSCTL is not set
 CONFIG_EMBEDDED=y
 CONFIG_UID16=y
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_SYSCTL_SYSCALL_CHECK=y
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
@@ -381,7 +381,7 @@ CONFIG_IP_PNP=y
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_ARPD is not set
-CONFIG_SYN_COOKIES=y
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_AH is not set
 # CONFIG_INET_ESP is not set
 # CONFIG_INET_IPCOMP is not set
index e2fc588e43364702d3099a043d785c3bc11597cd..e17875e8abe8ff9df259717a015bd3e8a6c35cb7 100644 (file)
@@ -46,7 +46,7 @@ CONFIG_INITRAMFS_SOURCE=""
 # CONFIG_SYSCTL is not set
 CONFIG_EMBEDDED=y
 CONFIG_UID16=y
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_ALL is not set
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
@@ -411,7 +411,7 @@ CONFIG_IP_PNP=y
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_ARPD is not set
-CONFIG_SYN_COOKIES=y
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_AH is not set
 # CONFIG_INET_ESP is not set
 # CONFIG_INET_IPCOMP is not set
@@ -783,7 +783,30 @@ CONFIG_SPI_BFIN=y
 # CONFIG_SPI_SPIDEV is not set
 # CONFIG_SPI_TLE62X0 is not set
 CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
-# CONFIG_GPIOLIB is not set
+CONFIG_GPIOLIB=y
+# CONFIG_DEBUG_GPIO is not set
+CONFIG_GPIO_SYSFS=y
+
+#
+# Memory mapped GPIO expanders:
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
 # CONFIG_W1 is not set
 # CONFIG_POWER_SUPPLY is not set
 CONFIG_HWMON=y
index 65a8bbb8d647dc8128ce733dbd24ad05dd90118e..fafd95e84b28c78f95bfa732f04211302972c7ae 100644 (file)
@@ -49,7 +49,7 @@ CONFIG_LOG_BUF_SHIFT=14
 # CONFIG_SYSCTL is not set
 CONFIG_EMBEDDED=y
 # CONFIG_UID16 is not set
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
 # CONFIG_HOTPLUG is not set
@@ -347,7 +347,7 @@ CONFIG_IP_FIB_HASH=y
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_ARPD is not set
-CONFIG_SYN_COOKIES=y
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_AH is not set
 # CONFIG_INET_ESP is not set
 # CONFIG_INET_IPCOMP is not set
@@ -547,9 +547,9 @@ CONFIG_MII=y
 CONFIG_SMC91X=y
 # CONFIG_SMSC911X is not set
 # CONFIG_DM9000 is not set
-CONFIG_NETDEV_1000=y
+# CONFIG_NETDEV_1000 is not set
 # CONFIG_AX88180 is not set
-CONFIG_NETDEV_10000=y
+# CONFIG_NETDEV_10000 is not set
 
 #
 # Wireless LAN
@@ -641,6 +641,10 @@ CONFIG_UNIX98_PTYS=y
 # CONFIG_TCG_TPM is not set
 # CONFIG_I2C is not set
 
+CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_SYSFS=y
+
 #
 # SPI support
 #
index 9b7e9d781145a2aef42acdd202dd7a6e99acb5fe..e73aa5af58b9e50285e094cd97f0878c3cd84d51 100644 (file)
@@ -1,6 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.22.16
+# Linux kernel version: 2.6.28.10
+# Wed Jun  3 06:27:41 2009
 #
 # CONFIG_MMU is not set
 # CONFIG_FPU is not set
@@ -8,48 +9,44 @@ CONFIG_RWSEM_GENERIC_SPINLOCK=y
 # CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
 CONFIG_BLACKFIN=y
 CONFIG_ZONE_DMA=y
-CONFIG_SEMAPHORE_SLEEPERS=y
 CONFIG_GENERIC_FIND_NEXT_BIT=y
 CONFIG_GENERIC_HWEIGHT=y
 CONFIG_GENERIC_HARDIRQS=y
 CONFIG_GENERIC_IRQ_PROBE=y
-CONFIG_GENERIC_TIME=y
 CONFIG_GENERIC_GPIO=y
 CONFIG_FORCE_MAX_ZONEORDER=14
 CONFIG_GENERIC_CALIBRATE_DELAY=y
 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
 
 #
-# Code maturity level options
+# General setup
 #
 CONFIG_EXPERIMENTAL=y
 CONFIG_BROKEN_ON_SMP=y
 CONFIG_INIT_ENV_ARG_LIMIT=32
-
-#
-# General setup
-#
 CONFIG_LOCALVERSION=""
 CONFIG_LOCALVERSION_AUTO=y
 CONFIG_SYSVIPC=y
-# CONFIG_IPC_NS is not set
 CONFIG_SYSVIPC_SYSCTL=y
 # CONFIG_POSIX_MQUEUE is not set
 # CONFIG_BSD_PROCESS_ACCT is not set
 # CONFIG_TASKSTATS is not set
-# CONFIG_UTS_NS is not set
 # CONFIG_AUDIT is not set
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_LOG_BUF_SHIFT=14
-# CONFIG_SYSFS_DEPRECATED is not set
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED is not set
+# CONFIG_SYSFS_DEPRECATED_V2 is not set
 # CONFIG_RELAY is not set
+# CONFIG_NAMESPACES is not set
 # CONFIG_BLK_DEV_INITRD is not set
 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
-# CONFIG_SYSCTL is not set
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
 CONFIG_EMBEDDED=y
 # CONFIG_UID16 is not set
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
 # CONFIG_HOTPLUG is not set
@@ -58,37 +55,36 @@ CONFIG_BUG=y
 # CONFIG_ELF_CORE is not set
 CONFIG_BASE_FULL=y
 # CONFIG_FUTEX is not set
-CONFIG_ANON_INODES=y
 CONFIG_EPOLL=y
-CONFIG_SIGNALFD=y
-CONFIG_EVENTFD=y
+# CONFIG_SIGNALFD is not set
+# CONFIG_TIMERFD is not set
+# CONFIG_EVENTFD is not set
+# CONFIG_AIO is not set
 CONFIG_VM_EVENT_COUNTERS=y
-CONFIG_BIG_ORDER_ALLOC_NOFAIL_MAGIC=3
-# CONFIG_NP2 is not set
+CONFIG_COMPAT_BRK=y
 CONFIG_SLAB=y
 # CONFIG_SLUB is not set
 # CONFIG_SLOB is not set
-CONFIG_RT_MUTEXES=y
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
+CONFIG_SLABINFO=y
 CONFIG_TINY_SHMEM=y
 CONFIG_BASE_SMALL=0
-
-#
-# Loadable module support
-#
 CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
 CONFIG_MODULE_UNLOAD=y
 # CONFIG_MODULE_FORCE_UNLOAD is not set
 # CONFIG_MODVERSIONS is not set
 # CONFIG_MODULE_SRCVERSION_ALL is not set
 CONFIG_KMOD=y
-
-#
-# Block layer
-#
 CONFIG_BLOCK=y
 # CONFIG_LBD is not set
 # CONFIG_BLK_DEV_IO_TRACE is not set
 # CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
 
 #
 # IO Schedulers
@@ -102,9 +98,11 @@ CONFIG_IOSCHED_CFQ=y
 # CONFIG_DEFAULT_CFQ is not set
 CONFIG_DEFAULT_NOOP=y
 CONFIG_DEFAULT_IOSCHED="noop"
+CONFIG_CLASSIC_RCU=y
 CONFIG_PREEMPT_NONE=y
 # CONFIG_PREEMPT_VOLUNTARY is not set
 # CONFIG_PREEMPT is not set
+# CONFIG_FREEZER is not set
 
 #
 # Blackfin Processor Options
@@ -113,6 +111,10 @@ CONFIG_PREEMPT_NONE=y
 #
 # Processor and Board Settings
 #
+# CONFIG_BF512 is not set
+# CONFIG_BF514 is not set
+# CONFIG_BF516 is not set
+# CONFIG_BF518 is not set
 # CONFIG_BF522 is not set
 # CONFIG_BF523 is not set
 # CONFIG_BF524 is not set
@@ -125,22 +127,31 @@ CONFIG_PREEMPT_NONE=y
 # CONFIG_BF534 is not set
 # CONFIG_BF536 is not set
 CONFIG_BF537=y
+# CONFIG_BF538 is not set
+# CONFIG_BF539 is not set
 # CONFIG_BF542 is not set
+# CONFIG_BF542M is not set
 # CONFIG_BF544 is not set
+# CONFIG_BF544M is not set
 # CONFIG_BF547 is not set
+# CONFIG_BF547M is not set
 # CONFIG_BF548 is not set
+# CONFIG_BF548M is not set
 # CONFIG_BF549 is not set
+# CONFIG_BF549M is not set
 # CONFIG_BF561 is not set
+CONFIG_BF_REV_MIN=2
+CONFIG_BF_REV_MAX=3
 # CONFIG_BF_REV_0_0 is not set
 # CONFIG_BF_REV_0_1 is not set
 CONFIG_BF_REV_0_2=y
 # CONFIG_BF_REV_0_3 is not set
 # CONFIG_BF_REV_0_4 is not set
 # CONFIG_BF_REV_0_5 is not set
+# CONFIG_BF_REV_0_6 is not set
 # CONFIG_BF_REV_ANY is not set
 # CONFIG_BF_REV_NONE is not set
 CONFIG_BF53x=y
-CONFIG_BFIN_SINGLE_CORE=y
 CONFIG_MEM_MT48LC16M16A2TG_75=y
 CONFIG_IRQ_PLL_WAKEUP=7
 CONFIG_IRQ_RTC=8
@@ -150,7 +161,6 @@ CONFIG_IRQ_SPORT0_TX=9
 CONFIG_IRQ_SPORT1_RX=9
 CONFIG_IRQ_SPORT1_TX=9
 CONFIG_IRQ_TWI=10
-CONFIG_IRQ_SPI=10
 CONFIG_IRQ_UART0_RX=10
 CONFIG_IRQ_UART0_TX=10
 CONFIG_IRQ_UART1_RX=10
@@ -169,11 +179,12 @@ CONFIG_IRQ_PORTG_INTB=12
 CONFIG_IRQ_MEM_DMA0=13
 CONFIG_IRQ_MEM_DMA1=13
 CONFIG_IRQ_WATCH=13
+CONFIG_IRQ_SPI=10
 # CONFIG_BFIN537_STAMP is not set
 CONFIG_BFIN537_BLUETECHNIX_CM=y
+# CONFIG_BFIN537_BLUETECHNIX_TCM is not set
 # CONFIG_PNAV10 is not set
 # CONFIG_CAMSIG_MINOTAUR is not set
-# CONFIG_GENERIC_BF537_BOARD is not set
 
 #
 # BF537 Specific Configuration
@@ -196,6 +207,7 @@ CONFIG_IRQ_PROG_INTA=12
 # Board customizations
 #
 # CONFIG_CMDLINE_BOOL is not set
+CONFIG_BOOT_LOAD=0x1000
 
 #
 # Clock/PLL Setup
@@ -215,13 +227,20 @@ CONFIG_HZ_250=y
 # CONFIG_HZ_300 is not set
 # CONFIG_HZ_1000 is not set
 CONFIG_HZ=250
+# CONFIG_SCHED_HRTICK is not set
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+# CONFIG_TICKSOURCE_GPTMR0 is not set
+CONFIG_TICKSOURCE_CORETMR=y
+# CONFIG_CYCLES_CLOCKSOURCE is not set
+# CONFIG_GPTMR0_CLOCKSOURCE is not set
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
 
 #
-# Memory Setup
+# Misc
 #
-CONFIG_MAX_MEM_SIZE=32
-CONFIG_MEM_ADD_WIDTH=9
-CONFIG_BOOT_LOAD=0x1000
 CONFIG_BFIN_SCRATCH_REG_RETN=y
 # CONFIG_BFIN_SCRATCH_REG_RETE is not set
 # CONFIG_BFIN_SCRATCH_REG_CYCLES is not set
@@ -248,6 +267,12 @@ CONFIG_IP_CHECKSUM_L1=y
 CONFIG_CACHELINE_ALIGNED_L1=y
 CONFIG_SYSCALL_TAB_L1=y
 CONFIG_CPLB_SWITCH_TAB_L1=y
+CONFIG_APP_STACK_L1=y
+
+#
+# Speed Optimizations
+#
+CONFIG_BFIN_INS_LOWOVERHEAD=y
 CONFIG_RAMKERNEL=y
 # CONFIG_ROMKERNEL is not set
 CONFIG_SELECT_MEMORY_MODEL=y
@@ -256,12 +281,14 @@ CONFIG_FLATMEM_MANUAL=y
 # CONFIG_SPARSEMEM_MANUAL is not set
 CONFIG_FLATMEM=y
 CONFIG_FLAT_NODE_MEM_MAP=y
-# CONFIG_SPARSEMEM_STATIC is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
 CONFIG_SPLIT_PTLOCK_CPUS=4
 # CONFIG_RESOURCES_64BIT is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
 CONFIG_ZONE_DMA_FLAG=1
-CONFIG_LARGE_ALLOCS=y
+CONFIG_VIRT_TO_BUS=y
 # CONFIG_BFIN_GPTIMERS is not set
+# CONFIG_DMA_UNCACHED_4M is not set
 # CONFIG_DMA_UNCACHED_2M is not set
 CONFIG_DMA_UNCACHED_1M=y
 # CONFIG_DMA_UNCACHED_NONE is not set
@@ -275,7 +302,6 @@ CONFIG_BFIN_DCACHE=y
 # CONFIG_BFIN_ICACHE_LOCK is not set
 CONFIG_BFIN_WB=y
 # CONFIG_BFIN_WT is not set
-CONFIG_L1_MAX_PIECE=16
 # CONFIG_MPU is not set
 
 #
@@ -304,13 +330,8 @@ CONFIG_BANK_3=0xFFC2
 #
 # Bus options (PCI, PCMCIA, EISA, MCA, ISA)
 #
-# CONFIG_PCI is not set
 # CONFIG_ARCH_SUPPORTS_MSI is not set
 
-#
-# PCCARD (PCMCIA/CardBus) support
-#
-
 #
 # Executable file formats
 #
@@ -318,22 +339,19 @@ CONFIG_BINFMT_ELF_FDPIC=y
 CONFIG_BINFMT_FLAT=y
 CONFIG_BINFMT_ZFLAT=y
 CONFIG_BINFMT_SHARED_FLAT=y
+# CONFIG_HAVE_AOUT is not set
 # CONFIG_BINFMT_MISC is not set
 
 #
 # Power management options
 #
 # CONFIG_PM is not set
-# CONFIG_PM_WAKEUP_BY_GPIO is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
 
 #
 # CPU Frequency scaling
 #
 # CONFIG_CPU_FREQ is not set
-
-#
-# Networking
-#
 CONFIG_NET=y
 
 #
@@ -346,6 +364,7 @@ CONFIG_XFRM=y
 # CONFIG_XFRM_USER is not set
 # CONFIG_XFRM_SUB_POLICY is not set
 # CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
 # CONFIG_NET_KEY is not set
 CONFIG_INET=y
 # CONFIG_IP_MULTICAST is not set
@@ -358,7 +377,7 @@ CONFIG_IP_PNP=y
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_ARPD is not set
-CONFIG_SYN_COOKIES=y
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_AH is not set
 # CONFIG_INET_ESP is not set
 # CONFIG_INET_IPCOMP is not set
@@ -367,6 +386,7 @@ CONFIG_SYN_COOKIES=y
 CONFIG_INET_XFRM_MODE_TRANSPORT=y
 CONFIG_INET_XFRM_MODE_TUNNEL=y
 CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
 CONFIG_INET_DIAG=y
 CONFIG_INET_TCP_DIAG=y
 # CONFIG_TCP_CONG_ADVANCED is not set
@@ -374,8 +394,6 @@ CONFIG_TCP_CONG_CUBIC=y
 CONFIG_DEFAULT_TCP_CONG="cubic"
 # CONFIG_TCP_MD5SIG is not set
 # CONFIG_IPV6 is not set
-# CONFIG_INET6_XFRM_TUNNEL is not set
-# CONFIG_INET6_TUNNEL is not set
 # CONFIG_NETLABEL is not set
 # CONFIG_NETWORK_SECMARK is not set
 # CONFIG_NETFILTER is not set
@@ -384,6 +402,7 @@ CONFIG_DEFAULT_TCP_CONG="cubic"
 # CONFIG_TIPC is not set
 # CONFIG_ATM is not set
 # CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA is not set
 # CONFIG_VLAN_8021Q is not set
 # CONFIG_DECNET is not set
 # CONFIG_LLC2 is not set
@@ -393,10 +412,6 @@ CONFIG_DEFAULT_TCP_CONG="cubic"
 # CONFIG_LAPB 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
 
 #
@@ -404,18 +419,14 @@ CONFIG_DEFAULT_TCP_CONG="cubic"
 #
 # CONFIG_NET_PKTGEN is not set
 # CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
 # CONFIG_IRDA is not set
 # CONFIG_BT is not set
 # CONFIG_AF_RXRPC is not set
-
-#
-# Wireless
-#
-# CONFIG_CFG80211 is not set
-# CONFIG_WIRELESS_EXT is not set
-# CONFIG_MAC80211 is not set
-# CONFIG_IEEE80211 is not set
+# CONFIG_PHONET is not set
+# CONFIG_WIRELESS is not set
 # CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
 
 #
 # Device Drivers
@@ -427,10 +438,6 @@ CONFIG_DEFAULT_TCP_CONG="cubic"
 CONFIG_STANDALONE=y
 CONFIG_PREVENT_FIRMWARE_BUILD=y
 # CONFIG_SYS_HYPERVISOR is not set
-
-#
-# Connector - unified userspace <-> kernelspace linker
-#
 # CONFIG_CONNECTOR is not set
 CONFIG_MTD=y
 # CONFIG_MTD_DEBUG is not set
@@ -438,6 +445,7 @@ CONFIG_MTD=y
 CONFIG_MTD_PARTITIONS=y
 # CONFIG_MTD_REDBOOT_PARTS is not set
 # CONFIG_MTD_CMDLINE_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
 
 #
 # User Modules And Translation Layers
@@ -450,12 +458,15 @@ CONFIG_MTD_BLOCK=y
 # CONFIG_INFTL is not set
 # CONFIG_RFD_FTL is not set
 # CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
 
 #
 # RAM/ROM/Flash chip drivers
 #
-# CONFIG_MTD_CFI is not set
+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
@@ -466,6 +477,10 @@ CONFIG_MTD_CFI_I1=y
 CONFIG_MTD_CFI_I2=y
 # CONFIG_MTD_CFI_I4 is not set
 # CONFIG_MTD_CFI_I8 is not set
+CONFIG_MTD_CFI_INTELEXT=y
+# CONFIG_MTD_CFI_AMDSTD is not set
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
 CONFIG_MTD_RAM=y
 # CONFIG_MTD_ROM is not set
 # CONFIG_MTD_ABSENT is not set
@@ -473,7 +488,8 @@ CONFIG_MTD_RAM=y
 #
 # Mapping drivers for chip access
 #
-# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+CONFIG_MTD_GPIO_ADDR=y
 CONFIG_MTD_UCLINUX=y
 # CONFIG_MTD_PLATRAM is not set
 
@@ -498,33 +514,23 @@ CONFIG_MTD_UCLINUX=y
 # UBI - Unsorted block images
 #
 # CONFIG_MTD_UBI is not set
-
-#
-# Parallel port support
-#
 # CONFIG_PARPORT is not set
-
-#
-# Plug and Play support
-#
-# CONFIG_PNPACPI is not set
-
-#
-# Block devices
-#
+CONFIG_BLK_DEV=y
 # 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=y
 CONFIG_BLK_DEV_RAM_COUNT=16
 CONFIG_BLK_DEV_RAM_SIZE=4096
-CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
+# CONFIG_BLK_DEV_XIP is not set
 # CONFIG_CDROM_PKTCDVD is not set
 # CONFIG_ATA_OVER_ETH is not set
-
-#
-# Misc devices
-#
+# CONFIG_BLK_DEV_HD is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_C2PORT is not set
+CONFIG_HAVE_IDE=y
 # CONFIG_IDE is not set
 
 #
@@ -532,22 +538,17 @@ CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
 #
 # CONFIG_RAID_ATTRS is not set
 # CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
 # CONFIG_SCSI_NETLINK is not set
 # CONFIG_ATA is not set
-
-#
-# Multi-device support (RAID and LVM)
-#
 # CONFIG_MD is not set
-
-#
-# Network device support
-#
 CONFIG_NETDEVICES=y
 # CONFIG_DUMMY is not set
 # CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
 # CONFIG_EQUALIZER is not set
 # CONFIG_TUN is not set
+# CONFIG_VETH is not set
 CONFIG_PHYLIB=y
 
 #
@@ -561,46 +562,44 @@ CONFIG_PHYLIB=y
 # CONFIG_VITESSE_PHY is not set
 # CONFIG_SMSC_PHY is not set
 # CONFIG_BROADCOM_PHY is not set
+# CONFIG_ICPLUS_PHY is not set
+# CONFIG_REALTEK_PHY is not set
 # CONFIG_FIXED_PHY is not set
-
-#
-# Ethernet (10 or 100Mbit)
-#
+# CONFIG_MDIO_BITBANG is not set
 CONFIG_NET_ETHERNET=y
 CONFIG_MII=y
-# CONFIG_SMC91X is not set
 CONFIG_BFIN_MAC=y
 CONFIG_BFIN_MAC_USE_L1=y
 CONFIG_BFIN_TX_DESC_NUM=10
 CONFIG_BFIN_RX_DESC_NUM=20
 # CONFIG_BFIN_MAC_RMII is not set
+# CONFIG_SMC91X is not set
 # CONFIG_SMSC911X is not set
 # CONFIG_DM9000 is not set
-CONFIG_NETDEV_1000=y
-# CONFIG_AX88180 is not set
-CONFIG_NETDEV_10000=y
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
+# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
+# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
+# CONFIG_B44 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
 
 #
 # Wireless LAN
 #
 # CONFIG_WLAN_PRE80211 is not set
 # CONFIG_WLAN_80211 is not set
+# CONFIG_IWLWIFI_LEDS is not set
 # 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
 # CONFIG_NETPOLL is not set
 # CONFIG_NET_POLL_CONTROLLER is not set
-
-#
-# ISDN subsystem
-#
 # CONFIG_ISDN is not set
-
-#
-# Telephony Support
-#
 # CONFIG_PHONE is not set
 
 #
@@ -618,15 +617,17 @@ CONFIG_NETDEV_10000=y
 # Character devices
 #
 # CONFIG_AD9960 is not set
-# CONFIG_SPI_ADC_BF533 is not set
-# CONFIG_BF5xx_PFLAGS is not set
-# CONFIG_BF5xx_PPIFCD is not set
+CONFIG_BFIN_DMA_INTERFACE=m
+# CONFIG_BFIN_PPI is not set
+# CONFIG_BFIN_PPIFCD is not set
 # CONFIG_BFIN_SIMPLE_TIMER is not set
-# CONFIG_BF5xx_PPI is not set
+# CONFIG_BFIN_SPI_ADC is not set
 CONFIG_BFIN_SPORT=y
 # CONFIG_BFIN_TIMER_LATENCY is not set
+# CONFIG_SIMPLE_GPIO is not set
 # CONFIG_VT is not set
 # CONFIG_DEVKMEM is not set
+# CONFIG_BFIN_JTAG_COMM is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
 
 #
@@ -655,138 +656,119 @@ CONFIG_UNIX98_PTYS=y
 # CAN, the car bus and industrial fieldbus
 #
 # CONFIG_CAN4LINUX is not set
-
-#
-# IPMI
-#
 # CONFIG_IPMI_HANDLER is not set
-# CONFIG_WATCHDOG is not set
 # CONFIG_HW_RANDOM is not set
-# CONFIG_GEN_RTC is not set
 # CONFIG_R3964 is not set
 # CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_I2C is not set
+# CONFIG_SPI is not set
+CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_SYSFS=y
 
 #
-# TPM devices
+# Memory mapped GPIO expanders:
 #
-# CONFIG_TCG_TPM is not set
-# CONFIG_I2C is not set
 
 #
-# SPI support
+# I2C GPIO expanders:
+#
+
+#
+# PCI GPIO expanders:
 #
-# CONFIG_SPI is not set
-# CONFIG_SPI_MASTER is not set
 
 #
-# Dallas's 1-wire bus
+# SPI GPIO expanders:
 #
 # CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
 CONFIG_HWMON=y
 # CONFIG_HWMON_VID is not set
-# CONFIG_SENSORS_ABITUGURU is not set
 # CONFIG_SENSORS_F71805F is not set
+# CONFIG_SENSORS_F71882FG is not set
+# CONFIG_SENSORS_IT87 is not set
+# CONFIG_SENSORS_PC87360 is not set
 # CONFIG_SENSORS_PC87427 is not set
 # CONFIG_SENSORS_SMSC47M1 is not set
 # CONFIG_SENSORS_SMSC47B397 is not set
 # CONFIG_SENSORS_VT1211 is not set
 # CONFIG_SENSORS_W83627HF is not set
+# CONFIG_SENSORS_W83627EHF is not set
 # CONFIG_HWMON_DEBUG_CHIP is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
 
 #
 # Multifunction device drivers
 #
+# CONFIG_MFD_CORE is not set
 # CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_REGULATOR is not set
 
 #
 # Multimedia devices
 #
+
+#
+# Multimedia core support
+#
 # CONFIG_VIDEO_DEV is not set
 # CONFIG_DVB_CORE is not set
-# CONFIG_DAB is not set
+# CONFIG_VIDEO_MEDIA is not set
 
 #
-# Graphics support
+# Multimedia drivers
 #
-# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+# CONFIG_DAB is not set
 
 #
-# Display device support
+# Graphics support
 #
-# CONFIG_DISPLAY_SUPPORT is not set
 # CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
 # CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
 
 #
-# Sound
+# Display device support
 #
+# CONFIG_DISPLAY_SUPPORT is not set
 # CONFIG_SOUND is not set
-
-#
-# USB support
-#
+CONFIG_USB_SUPPORT=y
 CONFIG_USB_ARCH_HAS_HCD=y
 # CONFIG_USB_ARCH_HAS_OHCI is not set
 # CONFIG_USB_ARCH_HAS_EHCI is not set
 # CONFIG_USB is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
 
 #
 # Enable Host or Gadget support to see Inventra options
 #
 
 #
-# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
-#
-
-#
-# USB Gadget Support
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed;
 #
 # CONFIG_USB_GADGET is not set
 # CONFIG_MMC is not set
-
-#
-# LED devices
-#
+# CONFIG_MEMSTICK is not set
 # CONFIG_NEW_LEDS is not set
-
-#
-# LED drivers
-#
-
-#
-# LED Triggers
-#
-
-#
-# InfiniBand support
-#
-
-#
-# EDAC - error detection and reporting (RAS) (EXPERIMENTAL)
-#
-
-#
-# Real Time Clock
-#
+# CONFIG_ACCESSIBILITY is not set
 # CONFIG_RTC_CLASS is not set
-
-#
-# DMA Engine support
-#
-# CONFIG_DMA_ENGINE is not set
-
-#
-# DMA Clients
-#
-
-#
-# DMA Devices
-#
-
-#
-# PBX support
-#
-# CONFIG_PBX is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
 
 #
 # File systems
@@ -796,20 +778,18 @@ CONFIG_EXT2_FS_XATTR=y
 # CONFIG_EXT2_FS_POSIX_ACL is not set
 # CONFIG_EXT2_FS_SECURITY is not set
 # CONFIG_EXT3_FS is not set
-# CONFIG_EXT4DEV_FS is not set
+# CONFIG_EXT4_FS is not set
 CONFIG_FS_MBCACHE=y
 # CONFIG_REISERFS_FS is not set
 # CONFIG_JFS_FS is not set
 # CONFIG_FS_POSIX_ACL is not set
+CONFIG_FILE_LOCKING=y
 # CONFIG_XFS_FS is not set
-# CONFIG_GFS2_FS is not set
 # CONFIG_OCFS2_FS is not set
-# CONFIG_MINIX_FS is not set
-# CONFIG_ROMFS_FS is not set
+# CONFIG_DNOTIFY is not set
 CONFIG_INOTIFY=y
 CONFIG_INOTIFY_USER=y
 # CONFIG_QUOTA is not set
-# CONFIG_DNOTIFY is not set
 # CONFIG_AUTOFS_FS is not set
 # CONFIG_AUTOFS4_FS is not set
 # CONFIG_FUSE_FS is not set
@@ -835,7 +815,6 @@ CONFIG_PROC_SYSCTL=y
 CONFIG_SYSFS=y
 # CONFIG_TMPFS is not set
 # CONFIG_HUGETLB_PAGE is not set
-CONFIG_RAMFS=y
 # CONFIG_CONFIGFS_FS is not set
 
 #
@@ -848,60 +827,53 @@ CONFIG_RAMFS=y
 # CONFIG_BEFS_FS is not set
 # CONFIG_BFS_FS is not set
 # CONFIG_EFS_FS is not set
-# CONFIG_YAFFS_FS is not set
 # CONFIG_JFFS2_FS is not set
+# CONFIG_YAFFS_FS is not set
 # CONFIG_CRAMFS is not set
 # CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
 # CONFIG_HPFS_FS is not set
 # CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_UFS_FS is not set
-
-#
-# Network File Systems
-#
-# CONFIG_NFS_FS is not set
-# CONFIG_NFSD 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
-# CONFIG_9P_FS is not set
+# CONFIG_NETWORK_FILESYSTEMS is not set
 
 #
 # Partition Types
 #
 # CONFIG_PARTITION_ADVANCED is not set
 CONFIG_MSDOS_PARTITION=y
-
-#
-# Native Language Support
-#
 # CONFIG_NLS is not set
-
-#
-# Distributed Lock Manager
-#
 # CONFIG_DLM is not set
 
-#
-# Profiling support
-#
-# CONFIG_PROFILING is not set
-
 #
 # Kernel hacking
 #
 # CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
 CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
 # CONFIG_MAGIC_SYSRQ is not set
 # CONFIG_UNUSED_SYMBOLS is not set
 CONFIG_DEBUG_FS=y
 # CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_SECTION_MISMATCH=y
 # CONFIG_DEBUG_KERNEL is not set
 # CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+
+#
+# Tracers
+#
+# CONFIG_DYNAMIC_PRINTK_DEBUG is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+CONFIG_DEBUG_VERBOSE=y
 CONFIG_DEBUG_MMRS=y
+# CONFIG_DEBUG_DOUBLEFAULT is not set
 CONFIG_DEBUG_HUNT_FOR_ZERO=y
 CONFIG_DEBUG_BFIN_HWTRACE_ON=y
 CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF=y
@@ -919,13 +891,95 @@ CONFIG_ACCESS_CHECK=y
 #
 # CONFIG_KEYS is not set
 CONFIG_SECURITY=y
+# CONFIG_SECURITYFS is not set
 # CONFIG_SECURITY_NETWORK is not set
-CONFIG_SECURITY_CAPABILITIES=y
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_SECURITY_DEFAULT_MMAP_MIN_ADDR=0
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+# CONFIG_CRYPTO_FIPS is not set
+# CONFIG_CRYPTO_MANAGER is not set
+# CONFIG_CRYPTO_MANAGER2 is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+# CONFIG_CRYPTO_CBC is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+
+#
+# Compression
+#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_LZO is not set
 
 #
-# Cryptographic options
+# Random Number Generation
 #
-# CONFIG_CRYPTO is not set
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_HW=y
 
 #
 # Library routines
@@ -933,11 +987,12 @@ CONFIG_SECURITY_CAPABILITIES=y
 CONFIG_BITREVERSE=y
 CONFIG_CRC_CCITT=m
 # CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
 # CONFIG_CRC_ITU_T is not set
 CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
 # CONFIG_LIBCRC32C is not set
 CONFIG_ZLIB_INFLATE=y
-CONFIG_PLIST=y
 CONFIG_HAS_IOMEM=y
 CONFIG_HAS_IOPORT=y
 CONFIG_HAS_DMA=y
index 569523c1c034113f2c1991403e3d76e2a291f876..80211303f6b9572908aa423d36e11c8523ef0a5a 100644 (file)
@@ -49,7 +49,7 @@ CONFIG_LOG_BUF_SHIFT=14
 # CONFIG_SYSCTL is not set
 CONFIG_EMBEDDED=y
 # CONFIG_UID16 is not set
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
 # CONFIG_HOTPLUG is not set
@@ -355,7 +355,7 @@ CONFIG_IP_FIB_HASH=y
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_ARPD is not set
-CONFIG_SYN_COOKIES=y
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_AH is not set
 # CONFIG_INET_ESP is not set
 # CONFIG_INET_IPCOMP is not set
@@ -556,9 +556,9 @@ CONFIG_SMC91X=y
 # CONFIG_BFIN_MAC is not set
 # CONFIG_SMSC911X is not set
 # CONFIG_DM9000 is not set
-CONFIG_NETDEV_1000=y
+# CONFIG_NETDEV_1000 is not set
 # CONFIG_AX88180 is not set
-CONFIG_NETDEV_10000=y
+# CONFIG_NETDEV_10000 is not set
 
 #
 # Wireless LAN
@@ -652,6 +652,10 @@ CONFIG_UNIX98_PTYS=y
 # CONFIG_TCG_TPM is not set
 # CONFIG_I2C is not set
 
+CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_SYSFS=y
+
 #
 # SPI support
 #
index 035b635e599cbd69083ee67ddf94fddfb0c3ac32..dd815f0d15175e13c652b9efb20cb27690d4e949 100644 (file)
@@ -49,7 +49,7 @@ CONFIG_INITRAMFS_SOURCE=""
 # CONFIG_SYSCTL is not set
 CONFIG_EMBEDDED=y
 CONFIG_UID16=y
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
 CONFIG_HOTPLUG=y
@@ -125,9 +125,9 @@ CONFIG_PREEMPT_VOLUNTARY=y
 CONFIG_BF548=y
 # CONFIG_BF549 is not set
 # CONFIG_BF561 is not set
-CONFIG_BF_REV_0_0=y
+# CONFIG_BF_REV_0_0 is not set
 # CONFIG_BF_REV_0_1 is not set
-# CONFIG_BF_REV_0_2 is not set
+CONFIG_BF_REV_0_2=y
 # CONFIG_BF_REV_0_3 is not set
 # CONFIG_BF_REV_0_4 is not set
 # CONFIG_BF_REV_0_5 is not set
@@ -422,7 +422,7 @@ CONFIG_IP_PNP=y
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_ARPD is not set
-CONFIG_SYN_COOKIES=y
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_AH is not set
 # CONFIG_INET_ESP is not set
 # CONFIG_INET_IPCOMP is not set
@@ -811,6 +811,10 @@ CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ=100
 # CONFIG_I2C_DEBUG_BUS is not set
 # CONFIG_I2C_DEBUG_CHIP is not set
 
+CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_SYSFS=y
+
 #
 # SPI support
 #
index 7015e42ccce5baa5b2d0c7fc5798d2fb06a03ddc..16c198bd40c5347d443f570af4c2ac3bd22d3a11 100644 (file)
@@ -49,7 +49,7 @@ CONFIG_FAIR_USER_SCHED=y
 # CONFIG_SYSCTL is not set
 CONFIG_EMBEDDED=y
 # CONFIG_UID16 is not set
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
 # CONFIG_HOTPLUG is not set
@@ -389,7 +389,7 @@ CONFIG_IP_FIB_HASH=y
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_ARPD is not set
-CONFIG_SYN_COOKIES=y
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_AH is not set
 # CONFIG_INET_ESP is not set
 # CONFIG_INET_IPCOMP is not set
@@ -569,9 +569,9 @@ CONFIG_SMC91X=y
 # CONFIG_IBM_NEW_EMAC_TAH is not set
 # CONFIG_IBM_NEW_EMAC_EMAC4 is not set
 # CONFIG_B44 is not set
-CONFIG_NETDEV_1000=y
+# CONFIG_NETDEV_1000 is not set
 # CONFIG_AX88180 is not set
-CONFIG_NETDEV_10000=y
+# CONFIG_NETDEV_10000 is not set
 
 #
 # Wireless LAN
@@ -646,6 +646,10 @@ CONFIG_UNIX98_PTYS=y
 # CONFIG_TCG_TPM is not set
 # CONFIG_I2C is not set
 
+CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_SYSFS=y
+
 #
 # SPI support
 #
index dfc8e1ddd77a63a0aaccdf8d3dcb4ab66ee0fbe2..6b4c1a982383a8f6b0b83ceceabd5c652a05b612 100644 (file)
@@ -48,7 +48,7 @@ CONFIG_SYSFS_DEPRECATED=y
 # CONFIG_SYSCTL is not set
 CONFIG_EMBEDDED=y
 CONFIG_UID16=y
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
 CONFIG_HOTPLUG=y
@@ -347,7 +347,7 @@ CONFIG_IP_PNP=y
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_ARPD is not set
-CONFIG_SYN_COOKIES=y
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_AH is not set
 # CONFIG_INET_ESP is not set
 # CONFIG_INET_IPCOMP is not set
@@ -594,8 +594,8 @@ CONFIG_MII=y
 # CONFIG_SMC91X is not set
 # CONFIG_SMSC911X is not set
 CONFIG_DM9000=y
-CONFIG_NETDEV_1000=y
-CONFIG_NETDEV_10000=y
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
 # CONFIG_AX88180 is not set
 
 #
index 95a5f91aebaa1b3d544b96e50e73bcd21fd1c9f4..1ec9ae2e964b816f17905cac2e70512e6a31aa4f 100644 (file)
@@ -49,7 +49,7 @@ CONFIG_INITRAMFS_SOURCE=""
 # CONFIG_SYSCTL is not set
 CONFIG_EMBEDDED=y
 CONFIG_UID16=y
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
 # CONFIG_HOTPLUG is not set
@@ -355,7 +355,7 @@ CONFIG_IP_PNP=y
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_ARPD is not set
-CONFIG_SYN_COOKIES=y
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_AH is not set
 # CONFIG_INET_ESP is not set
 # CONFIG_INET_IPCOMP is not set
@@ -672,9 +672,9 @@ CONFIG_MII=y
 # CONFIG_SMC91X is not set
 # CONFIG_SMSC911X is not set
 CONFIG_DM9000=y
-CONFIG_NETDEV_1000=y
+# CONFIG_NETDEV_1000 is not set
 # CONFIG_AX88180 is not set
-CONFIG_NETDEV_10000=y
+# CONFIG_NETDEV_10000 is not set
 
 #
 # Wireless LAN
index 78e24080e7f1821cc96b7a8c254db4b901236690..09701f907e9bafe519eb214af72246738db22fb0 100644 (file)
@@ -1,6 +1,6 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.28.7
+# Linux kernel version: 2.6.28.10
 #
 # CONFIG_MMU is not set
 # CONFIG_FPU is not set
@@ -40,26 +40,26 @@ CONFIG_LOG_BUF_SHIFT=14
 # CONFIG_NAMESPACES is not set
 # CONFIG_BLK_DEV_INITRD is not set
 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
-# CONFIG_SYSCTL is not set
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
 CONFIG_EMBEDDED=y
 CONFIG_UID16=y
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
 CONFIG_HOTPLUG=y
 CONFIG_PRINTK=y
 CONFIG_BUG=y
 # CONFIG_ELF_CORE is not set
-CONFIG_COMPAT_BRK=y
 CONFIG_BASE_FULL=y
 # CONFIG_FUTEX is not set
-CONFIG_ANON_INODES=y
 CONFIG_EPOLL=y
 CONFIG_SIGNALFD=y
 CONFIG_TIMERFD=y
 CONFIG_EVENTFD=y
 # CONFIG_AIO is not set
 CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_COMPAT_BRK=y
 CONFIG_SLAB=y
 # CONFIG_SLUB is not set
 # CONFIG_SLOB is not set
@@ -68,7 +68,6 @@ CONFIG_SLAB=y
 CONFIG_HAVE_OPROFILE=y
 # CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
 CONFIG_SLABINFO=y
-CONFIG_RT_MUTEXES=y
 CONFIG_TINY_SHMEM=y
 CONFIG_BASE_SMALL=0
 CONFIG_MODULES=y
@@ -229,7 +228,10 @@ CONFIG_HZ=250
 # CONFIG_SCHED_HRTICK is not set
 CONFIG_GENERIC_TIME=y
 CONFIG_GENERIC_CLOCKEVENTS=y
+# CONFIG_TICKSOURCE_GPTMR0 is not set
+CONFIG_TICKSOURCE_CORETMR=y
 # CONFIG_CYCLES_CLOCKSOURCE is not set
+# CONFIG_GPTMR0_CLOCKSOURCE is not set
 # CONFIG_NO_HZ is not set
 # CONFIG_HIGH_RES_TIMERS is not set
 CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
@@ -374,7 +376,7 @@ CONFIG_IP_PNP=y
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_ARPD is not set
-CONFIG_SYN_COOKIES=y
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_AH is not set
 # CONFIG_INET_ESP is not set
 # CONFIG_INET_IPCOMP is not set
@@ -598,9 +600,8 @@ CONFIG_BFIN_MAC_RMII=y
 # CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
 # CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
 # CONFIG_B44 is not set
-CONFIG_NETDEV_1000=y
-# CONFIG_AX88180 is not set
-CONFIG_NETDEV_10000=y
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
 
 #
 # Wireless LAN
@@ -640,11 +641,11 @@ CONFIG_INPUT_EVDEV=y
 # CONFIG_INPUT_JOYSTICK is not set
 # CONFIG_INPUT_TABLET is not set
 CONFIG_INPUT_TOUCHSCREEN=y
-# CONFIG_TOUCHSCREEN_ADS7846 is not set
 CONFIG_TOUCHSCREEN_AD7877=y
 # CONFIG_TOUCHSCREEN_AD7879_I2C is not set
 # CONFIG_TOUCHSCREEN_AD7879_SPI is not set
 # CONFIG_TOUCHSCREEN_AD7879 is not set
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
 # CONFIG_TOUCHSCREEN_FUJITSU is not set
 # CONFIG_TOUCHSCREEN_GUNZE is not set
 # CONFIG_TOUCHSCREEN_ELO is not set
@@ -676,14 +677,14 @@ CONFIG_INPUT_UINPUT=y
 # Character devices
 #
 # CONFIG_AD9960 is not set
-# CONFIG_SPI_ADC_BF533 is not set
-# CONFIG_BF5xx_PPIFCD is not set
+CONFIG_BFIN_DMA_INTERFACE=m
+# CONFIG_BFIN_PPI is not set
+# CONFIG_BFIN_PPIFCD is not set
 # CONFIG_BFIN_SIMPLE_TIMER is not set
-# CONFIG_BF5xx_PPI is not set
+# CONFIG_BFIN_SPI_ADC is not set
 CONFIG_BFIN_SPORT=y
 # CONFIG_BFIN_TIMER_LATENCY is not set
-CONFIG_TWI_LCD=m
-CONFIG_BFIN_DMA_INTERFACE=m
+# CONFIG_BFIN_TWI_LCD is not set
 # CONFIG_SIMPLE_GPIO is not set
 # CONFIG_VT is not set
 CONFIG_DEVKMEM=y
@@ -796,6 +797,7 @@ CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
 # CONFIG_POWER_SUPPLY is not set
 CONFIG_HWMON=y
 # CONFIG_HWMON_VID is not set
+# CONFIG_SENSORS_AD5252 is not set
 # CONFIG_SENSORS_AD7414 is not set
 # CONFIG_SENSORS_AD7418 is not set
 # CONFIG_SENSORS_ADCXX is not set
@@ -867,6 +869,7 @@ CONFIG_SSB_POSSIBLE=y
 # CONFIG_HTC_PASIC3 is not set
 # CONFIG_MFD_TMIO is not set
 # CONFIG_PMIC_DA903X is not set
+# CONFIG_PMIC_ADP5520 is not set
 # CONFIG_MFD_WM8400 is not set
 # CONFIG_MFD_WM8350_I2C is not set
 # CONFIG_REGULATOR is not set
@@ -1111,6 +1114,7 @@ CONFIG_SYSFS=y
 # CONFIG_BEFS_FS is not set
 # CONFIG_BFS_FS is not set
 # CONFIG_EFS_FS is not set
+# CONFIG_JFFS2_FS is not set
 CONFIG_YAFFS_FS=y
 CONFIG_YAFFS_YAFFS1=y
 # CONFIG_YAFFS_9BYTE_TAGS is not set
@@ -1121,7 +1125,6 @@ CONFIG_YAFFS_AUTO_YAFFS2=y
 # CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set
 # CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set
 CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y
-# CONFIG_JFFS2_FS is not set
 # CONFIG_CRAMFS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_MINIX_FS is not set
@@ -1213,7 +1216,6 @@ CONFIG_FRAME_WARN=1024
 # CONFIG_DEBUG_BUGVERBOSE is not set
 # CONFIG_DEBUG_MEMORY_INIT is not set
 # CONFIG_RCU_CPU_STALL_DETECTOR is not set
-# CONFIG_SYSCTL_SYSCALL_CHECK is not set
 
 #
 # Tracers
@@ -1343,7 +1345,6 @@ CONFIG_CRC32=y
 # CONFIG_CRC7 is not set
 # CONFIG_LIBCRC32C is not set
 CONFIG_ZLIB_INFLATE=y
-CONFIG_PLIST=y
 CONFIG_HAS_IOMEM=y
 CONFIG_HAS_IOPORT=y
 CONFIG_HAS_DMA=y
index 2bc0779d22ea49eb48a3f65eb24d55690d17c52f..ec84a53daae928db75b2eab32f341151332e814a 100644 (file)
@@ -52,7 +52,7 @@ CONFIG_INITRAMFS_SOURCE=""
 # CONFIG_SYSCTL is not set
 CONFIG_EMBEDDED=y
 CONFIG_UID16=y
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_KALLSYMS=y
 CONFIG_KALLSYMS_ALL=y
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
@@ -373,7 +373,7 @@ CONFIG_IP_PNP=y
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_ARPD is not set
-CONFIG_SYN_COOKIES=y
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_INET_AH is not set
 # CONFIG_INET_ESP is not set
 # CONFIG_INET_IPCOMP is not set
index e65b3a49214f9ab7d54b40181e6df9899ff99e3b..6e2796240fdc50907d201fb03cf59f9af5d969e0 100644 (file)
@@ -42,7 +42,7 @@ CONFIG_LOG_BUF_SHIFT=14
 # CONFIG_SYSCTL is not set
 CONFIG_EMBEDDED=y
 # CONFIG_UID16 is not set
-CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_SYSCTL_SYSCALL is not set
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
 # CONFIG_HOTPLUG is not set
@@ -537,7 +537,30 @@ CONFIG_SPI_BFIN=y
 # CONFIG_SPI_SPIDEV is not set
 # CONFIG_SPI_TLE62X0 is not set
 CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
-# CONFIG_GPIOLIB is not set
+CONFIG_GPIOLIB=y
+# CONFIG_DEBUG_GPIO is not set
+CONFIG_GPIO_SYSFS=y
+
+#
+# Memory mapped GPIO expanders:
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
 # CONFIG_W1 is not set
 # CONFIG_POWER_SUPPLY is not set
 # CONFIG_HWMON is not set
index 94b2a9b194519a9fb16c50abd761c78de3db85dd..7bbf44e4ddf9642a264396d26eba88e7ce08f842 100644 (file)
@@ -208,6 +208,6 @@ static inline void atomic_set_mask(unsigned int mask, atomic_t *v)
 #define atomic_sub_and_test(i,v) (atomic_sub_return((i), (v)) == 0)
 #define atomic_dec_and_test(v) (atomic_sub_return(1, (v)) == 0)
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 
 #endif                         /* __ARCH_BLACKFIN_ATOMIC __ */
diff --git a/arch/blackfin/include/asm/bitsperlong.h b/arch/blackfin/include/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..6dc0bb0
--- /dev/null
@@ -0,0 +1 @@
+#include <asm-generic/bitsperlong.h>
index 1b040f5b4feb10e8fd0a07a5de8933a0bb1e7bf2..94697f0f6f402fc2bf772ca778b96dddf750344d 100644 (file)
@@ -30,7 +30,8 @@
 #ifndef _BLACKFIN_CACHEFLUSH_H
 #define _BLACKFIN_CACHEFLUSH_H
 
-extern void blackfin_icache_dcache_flush_range(unsigned long start_address, unsigned long end_address);
+#include <asm/blackfin.h>      /* for SSYNC() */
+
 extern void blackfin_icache_flush_range(unsigned long start_address, unsigned long end_address);
 extern void blackfin_dcache_flush_range(unsigned long start_address, unsigned long end_address);
 extern void blackfin_dcache_invalidate_range(unsigned long start_address, unsigned long end_address);
@@ -54,32 +55,28 @@ extern void blackfin_invalidate_entire_dcache(void);
 
 static inline void flush_icache_range(unsigned start, unsigned end)
 {
-#if defined(CONFIG_BFIN_DCACHE) && defined(CONFIG_BFIN_ICACHE)
-
-# if defined(CONFIG_BFIN_WT)
-       blackfin_icache_flush_range((start), (end));
-       flush_icache_range_others(start, end);
-# else
-       blackfin_icache_dcache_flush_range((start), (end));
-# endif
-
-#else
+#if defined(CONFIG_BFIN_WB)
+       blackfin_dcache_flush_range(start, end);
+#endif
 
-# if defined(CONFIG_BFIN_ICACHE)
-       blackfin_icache_flush_range((start), (end));
+       /* Make sure all write buffers in the data side of the core
+        * are flushed before trying to invalidate the icache.  This
+        * needs to be after the data flush and before the icache
+        * flush so that the SSYNC does the right thing in preventing
+        * the instruction prefetcher from hitting things in cached
+        * memory at the wrong time -- it runs much further ahead than
+        * the pipeline.
+        */
+       SSYNC();
+#if defined(CONFIG_BFIN_ICACHE)
+       blackfin_icache_flush_range(start, end);
        flush_icache_range_others(start, end);
-# endif
-# if defined(CONFIG_BFIN_DCACHE)
-       blackfin_dcache_flush_range((start), (end));
-# endif
-
 #endif
 }
 
 #define copy_to_user_page(vma, page, vaddr, dst, src, len)             \
 do { memcpy(dst, src, len);                                            \
      flush_icache_range((unsigned) (dst), (unsigned) (dst) + (len));   \
-     flush_icache_range_others((unsigned long) (dst), (unsigned long) (dst) + (len));\
 } while (0)
 
 #define copy_from_user_page(vma, page, vaddr, dst, src, len)   memcpy(dst, src, len)
@@ -111,6 +108,11 @@ static inline int bfin_addr_dcachable(unsigned long addr)
                addr >= _ramend && addr < physical_mem_end)
                return 1;
 
+#ifndef CONFIG_BFIN_L2_NOT_CACHED
+       if (addr >= L2_START && addr < L2_START + L2_LENGTH)
+               return 1;
+#endif
+
        return 0;
 }
 
index ad566ff9ad16fdcf13c9fc84be53a6f040346957..a75a6a9f0949d42e0d3eaf028a5d8f026708a321 100644 (file)
 #define SDRAM_DGENERIC   (CPLB_L1_CHBL | CPLB_WT | CPLB_L1_AOW  | CPLB_COMMON)
 #endif
 
+#define SDRAM_DNON_CHBL  (CPLB_COMMON)
+#define SDRAM_EBIU       (CPLB_COMMON)
+#define SDRAM_OOPS       (CPLB_VALID | ANOMALY_05000158_WORKAROUND | CPLB_LOCK | CPLB_DIRTY)
+
 #define L1_DMEMORY       (CPLB_LOCK | CPLB_COMMON)
 
 #ifdef CONFIG_SMP
-#define L2_ATTR           (INITIAL_T | I_CPLB | D_CPLB)
-#define L2_IMEMORY         (CPLB_COMMON | CPLB_LOCK)
-#define L2_DMEMORY         (CPLB_COMMON | CPLB_LOCK)
+#define L2_ATTR          (INITIAL_T | I_CPLB | D_CPLB)
+#define L2_IMEMORY       (CPLB_COMMON)
+#define L2_DMEMORY       (CPLB_LOCK | CPLB_COMMON)
 
 #else
-#ifdef CONFIG_BFIN_L2_CACHEABLE
-#define L2_IMEMORY        (SDRAM_IGENERIC)
-#define L2_DMEMORY        (SDRAM_DGENERIC)
-#else
-#define L2_IMEMORY        (CPLB_COMMON)
-#define L2_DMEMORY        (CPLB_COMMON)
-#endif /* CONFIG_BFIN_L2_CACHEABLE */
-
-#define L2_ATTR           (INITIAL_T | SWITCH_T | I_CPLB | D_CPLB)
+#define L2_ATTR          (INITIAL_T | SWITCH_T | I_CPLB | D_CPLB)
+#define L2_IMEMORY       (SDRAM_IGENERIC)
+
+# if defined(CONFIG_BFIN_L2_WB)
+# define L2_DMEMORY      (CPLB_L1_CHBL | CPLB_COMMON)
+# elif defined(CONFIG_BFIN_L2_WT)
+# define L2_DMEMORY      (CPLB_L1_CHBL | CPLB_WT | CPLB_L1_AOW  | CPLB_COMMON)
+# elif defined(CONFIG_BFIN_L2_NOT_CACHED)
+# define L2_DMEMORY      (CPLB_COMMON)
+# else
+# define L2_DMEMORY      (0)
+# endif
 #endif /* CONFIG_SMP */
 
-#define SDRAM_DNON_CHBL  (CPLB_COMMON)
-#define SDRAM_EBIU       (CPLB_COMMON)
-#define SDRAM_OOPS       (CPLB_VALID | ANOMALY_05000158_WORKAROUND | CPLB_LOCK | CPLB_DIRTY)
-
 #define SIZE_1K 0x00000400      /* 1K */
 #define SIZE_4K 0x00001000      /* 4K */
 #define SIZE_1M 0x00100000      /* 1M */
index e4f7b8043f025335d4acf2f2dc95c37e58d16ca4..c9a59622e23f65d490087c64f26921ddaf545313 100644 (file)
@@ -206,10 +206,16 @@ static inline unsigned long get_dma_curr_addr(unsigned int channel)
 
 static inline void set_dma_sg(unsigned int channel, struct dmasg *sg, int ndsize)
 {
+       /* Make sure the internal data buffers in the core are drained
+        * so that the DMA descriptors are completely written when the
+        * DMA engine goes to fetch them below.
+        */
+       SSYNC();
+
+       dma_ch[channel].regs->next_desc_ptr = sg;
        dma_ch[channel].regs->cfg =
                (dma_ch[channel].regs->cfg & ~(0xf << 8)) |
                ((ndsize & 0xf) << 8);
-       dma_ch[channel].regs->next_desc_ptr = sg;
 }
 
 static inline int dma_channel_active(unsigned int channel)
@@ -253,5 +259,7 @@ static inline void clear_dma_irqstat(unsigned int channel)
 void *dma_memcpy(void *dest, const void *src, size_t count);
 void *safe_dma_memcpy(void *dest, const void *src, size_t count);
 void blackfin_dma_early_init(void);
+void early_dma_memcpy(void *dest, const void *src, size_t count);
+void early_dma_memcpy_done(void);
 
 #endif
index cdbfcfc30f6a693c170d6db57e80a3af04c5cf50..230e1605d3fbcc60d481c19b7b23b0aed59ab71b 100644 (file)
@@ -55,50 +55,50 @@ do {                                                                                        \
 #define ELF_FDPIC_CORE_EFLAGS  EF_BFIN_FDPIC
 #define ELF_EXEC_PAGESIZE      4096
 
-#define        R_unused0       0       /* relocation type 0 is not defined */
-#define R_pcrel5m2     1       /*LSETUP part a */
-#define R_unused1      2       /* relocation type 2 is not defined */
-#define R_pcrel10      3       /* type 3, if cc jump <target>  */
-#define R_pcrel12_jump 4       /* type 4, jump <target> */
-#define R_rimm16       5       /* type 0x5, rN = <target> */
-#define R_luimm16      6       /* # 0x6, preg.l=<target> Load imm 16 to lower half */
-#define R_huimm16      7       /* # 0x7, preg.h=<target> Load imm 16 to upper half */
-#define R_pcrel12_jump_s 8     /* # 0x8 jump.s <target> */
-#define R_pcrel24_jump_x 9     /* # 0x9 jump.x <target> */
-#define R_pcrel24       10     /* # 0xa call <target> , not expandable */
-#define R_unusedb       11     /* # 0xb not generated */
-#define R_unusedc       12     /* # 0xc  not used */
-#define R_pcrel24_jump_l 13    /*0xd jump.l <target> */
-#define R_pcrel24_call_x 14    /* 0xE, call.x <target> if <target> is above 24 bit limit call through P1 */
-#define R_var_eq_symb    15    /* 0xf, linker should treat it same as 0x12 */
-#define R_byte_data      16    /* 0x10, .byte var = symbol */
-#define R_byte2_data     17    /* 0x11, .byte2 var = symbol */
-#define R_byte4_data     18    /* 0x12, .byte4 var = symbol and .var var=symbol */
-#define R_pcrel11        19    /* 0x13, lsetup part b */
-#define R_unused14      20     /* 0x14, undefined */
-#define R_unused15       21    /* not generated by VDSP 3.5 */
+#define R_BFIN_UNUSED0         0   /* relocation type 0 is not defined */
+#define R_BFIN_PCREL5M2        1   /* LSETUP part a */
+#define R_BFIN_UNUSED1         2   /* relocation type 2 is not defined */
+#define R_BFIN_PCREL10         3   /* type 3, if cc jump <target> */
+#define R_BFIN_PCREL12_JUMP    4   /* type 4, jump <target> */
+#define R_BFIN_RIMM16          5   /* type 0x5, rN = <target> */
+#define R_BFIN_LUIMM16         6   /* # 0x6, preg.l=<target> Load imm 16 to lower half */
+#define R_BFIN_HUIMM16         7   /* # 0x7, preg.h=<target> Load imm 16 to upper half */
+#define R_BFIN_PCREL12_JUMP_S  8   /* # 0x8 jump.s <target> */
+#define R_BFIN_PCREL24_JUMP_X  9   /* # 0x9 jump.x <target> */
+#define R_BFIN_PCREL24         10  /* # 0xa call <target> , not expandable */
+#define R_BFIN_UNUSEDB         11  /* # 0xb not generated */
+#define R_BFIN_UNUSEDC         12  /* # 0xc  not used */
+#define R_BFIN_PCREL24_JUMP_L  13  /* 0xd jump.l <target> */
+#define R_BFIN_PCREL24_CALL_X  14  /* 0xE, call.x <target> if <target> is above 24 bit limit call through P1 */
+#define R_BFIN_VAR_EQ_SYMB     15  /* 0xf, linker should treat it same as 0x12 */
+#define R_BFIN_BYTE_DATA       16  /* 0x10, .byte var = symbol */
+#define R_BFIN_BYTE2_DATA      17  /* 0x11, .byte2 var = symbol */
+#define R_BFIN_BYTE4_DATA      18  /* 0x12, .byte4 var = symbol and .var var=symbol */
+#define R_BFIN_PCREL11         19  /* 0x13, lsetup part b */
+#define R_BFIN_UNUSED14        20  /* 0x14, undefined */
+#define R_BFIN_UNUSED15        21  /* not generated by VDSP 3.5 */
 
 /* arithmetic relocations */
-#define R_push          0xE0
-#define R_const                 0xE1
-#define R_add           0xE2
-#define R_sub           0xE3
-#define R_mult          0xE4
-#define R_div           0xE5
-#define R_mod           0xE6
-#define R_lshift        0xE7
-#define R_rshift        0xE8
-#define R_and           0xE9
-#define R_or            0xEA
-#define R_xor           0xEB
-#define R_land          0xEC
-#define R_lor           0xED
-#define R_len           0xEE
-#define R_neg           0xEF
-#define R_comp          0xF0
-#define R_page          0xF1
-#define R_hwpage        0xF2
-#define R_addr          0xF3
+#define R_BFIN_PUSH            0xE0
+#define R_BFIN_CONST           0xE1
+#define R_BFIN_ADD             0xE2
+#define R_BFIN_SUB             0xE3
+#define R_BFIN_MULT            0xE4
+#define R_BFIN_DIV             0xE5
+#define R_BFIN_MOD             0xE6
+#define R_BFIN_LSHIFT          0xE7
+#define R_BFIN_RSHIFT          0xE8
+#define R_BFIN_AND             0xE9
+#define R_BFIN_OR              0xEA
+#define R_BFIN_XOR             0xEB
+#define R_BFIN_LAND            0xEC
+#define R_BFIN_LOR             0xED
+#define R_BFIN_LEN             0xEE
+#define R_BFIN_NEG             0xEF
+#define R_BFIN_COMP            0xF0
+#define R_BFIN_PAGE            0xF1
+#define R_BFIN_HWPAGE          0xF2
+#define R_BFIN_ADDR            0xF3
 
 /* This is the location that an ET_DYN program is loaded if exec'ed.  Typical
    use of this is to invoke "./ld.so someprog" to test out a new version of
index b30a2968e2741ab75356672581129945128e6525..ec58efc130e677cb5b688b691715e0d4d0a5761c 100644 (file)
 #else
 # define LOAD_IPIPE_IPEND
 #endif
+
+#ifndef CONFIG_EXACT_HWERR
+/* As a debugging aid - we save IPEND when DEBUG_KERNEL is on,
+ * otherwise it is a waste of cycles.
+ */
+# ifndef CONFIG_DEBUG_KERNEL
+#define INTERRUPT_ENTRY(N)                                             \
+    [--sp] = SYSCFG;                                                   \
+    [--sp] = P0;       /*orig_p0*/                                     \
+    [--sp] = R0;       /*orig_r0*/                                     \
+    [--sp] = (R7:0,P5:0);                                              \
+    R0 = (N);                                                          \
+    LOAD_IPIPE_IPEND                                                   \
+    jump __common_int_entry;
+# else /* CONFIG_DEBUG_KERNEL */
 #define INTERRUPT_ENTRY(N)                                             \
     [--sp] = SYSCFG;                                                   \
-                                                                       \
     [--sp] = P0;       /*orig_p0*/                                     \
     [--sp] = R0;       /*orig_r0*/                                     \
     [--sp] = (R7:0,P5:0);                                              \
+    p0.l = lo(IPEND);                                                  \
+    p0.h = hi(IPEND);                                                  \
+    r1 = [p0];                                                         \
     R0 = (N);                                                          \
     LOAD_IPIPE_IPEND                                                   \
     jump __common_int_entry;
+# endif /* CONFIG_DEBUG_KERNEL */
 
 /* For timer interrupts, we need to save IPEND, since the user_mode
-          macro accesses it to determine where to account time.  */
+ *macro accesses it to determine where to account time.
+ */
 #define TIMER_INTERRUPT_ENTRY(N)                                       \
     [--sp] = SYSCFG;                                                   \
-                                                                       \
     [--sp] = P0;       /*orig_p0*/                                     \
     [--sp] = R0;       /*orig_r0*/                                     \
     [--sp] = (R7:0,P5:0);                                              \
     r1 = [p0];                                                         \
     R0 = (N);                                                          \
     jump __common_int_entry;
+#else /* CONFIG_EXACT_HWERR is defined */
+
+/* if we want hardware error to be exact, we need to do a SSYNC (which forces
+ * read/writes to complete to the memory controllers), and check to see that
+ * caused a pending HW error condition. If so, we assume it was caused by user
+ * space, by setting the same interrupt that we are in (so it goes off again)
+ * and context restore, and a RTI (without servicing anything). This should
+ * cause the pending HWERR to fire, and when that is done, this interrupt will
+ * be re-serviced properly.
+ * As you can see by the code - we actually need to do two SSYNCS - one to
+ * make sure the read/writes complete, and another to make sure the hardware
+ * error is recognized by the core.
+ */
+#define INTERRUPT_ENTRY(N)                                             \
+    SSYNC;                                                             \
+    SSYNC;                                                             \
+    [--sp] = SYSCFG;                                                   \
+    [--sp] = P0;       /*orig_p0*/                                     \
+    [--sp] = R0;       /*orig_r0*/                                     \
+    [--sp] = (R7:0,P5:0);                                              \
+    R1 = ASTAT;                                                                \
+    P0.L = LO(ILAT);                                                   \
+    P0.H = HI(ILAT);                                                   \
+    R0 = [P0];                                                         \
+    CC = BITTST(R0, EVT_IVHW_P);                                       \
+    IF CC JUMP 1f;                                                     \
+    ASTAT = R1;                                                                \
+    p0.l = lo(IPEND);                                                  \
+    p0.h = hi(IPEND);                                                  \
+    r1 = [p0];                                                         \
+    R0 = (N);                                                          \
+    LOAD_IPIPE_IPEND                                                   \
+    jump __common_int_entry;                                           \
+1:  ASTAT = R1;                                                                \
+    RAISE N;                                                           \
+    (R7:0, P5:0) = [SP++];                                             \
+    SP += 0x8;                                                         \
+    SYSCFG = [SP++];                                                   \
+    CSYNC;                                                             \
+    RTI;
+
+#define TIMER_INTERRUPT_ENTRY(N)                                       \
+    SSYNC;                                                             \
+    SSYNC;                                                             \
+    [--sp] = SYSCFG;                                                   \
+    [--sp] = P0;       /*orig_p0*/                                     \
+    [--sp] = R0;       /*orig_r0*/                                     \
+    [--sp] = (R7:0,P5:0);                                              \
+    R1 = ASTAT;                                                                \
+    P0.L = LO(ILAT);                                                   \
+    P0.H = HI(ILAT);                                                   \
+    R0 = [P0];                                                         \
+    CC = BITTST(R0, EVT_IVHW_P);                                       \
+    IF CC JUMP 1f;                                                     \
+    ASTAT = R1;                                                                \
+    p0.l = lo(IPEND);                                                  \
+    p0.h = hi(IPEND);                                                  \
+    r1 = [p0];                                                         \
+    R0 = (N);                                                          \
+    jump __common_int_entry;                                           \
+1:  ASTAT = R1;                                                                \
+    RAISE N;                                                           \
+    (R7:0, P5:0) = [SP++];                                             \
+    SP += 0x8;                                                         \
+    SYSCFG = [SP++];                                                   \
+    CSYNC;                                                             \
+    RTI;
+#endif /* CONFIG_EXACT_HWERR */
 
 /* This one pushes RETI without using CLI.  Interrupts are enabled.  */
 #define SAVE_CONTEXT_SYSCALL   save_context_syscall
index b0f847ae4bf48b045a881eedd63b7ff4e30bba29..89f08decb8e0fb0b6e5bf62c3d664d36e83e043e 100644 (file)
@@ -30,6 +30,7 @@
 # else
 #  define MAX_BLACKFIN_GPTIMERS 11
 #  define TIMER8_GROUP_REG      TIMER_ENABLE1
+#  define TIMER_GROUP2          1
 # endif
 # define TIMER0_GROUP_REG       TIMER_ENABLE0
 #endif
 # define MAX_BLACKFIN_GPTIMERS 12
 # define TIMER0_GROUP_REG      TMRS8_ENABLE
 # define TIMER8_GROUP_REG      TMRS4_ENABLE
+# define TIMER_GROUP2          1
 #endif
 /*
  * All others: 3 timers:
  */
+#define TIMER_GROUP1           0
 #if !defined(MAX_BLACKFIN_GPTIMERS)
 # define MAX_BLACKFIN_GPTIMERS 3
 # define TIMER0_GROUP_REG      TIMER_ENABLE
 #define TIMER_ERR_PROG_PER  0x8000
 #define TIMER_ERR_PROG_PW   0xC000
 #define TIMER_EMU_RUN       0x0200
-#define        TIMER_TOGGLE_HI     0x0100
-#define        TIMER_CLK_SEL       0x0080
+#define TIMER_TOGGLE_HI     0x0100
+#define TIMER_CLK_SEL       0x0080
 #define TIMER_OUT_DIS       0x0040
 #define TIMER_TIN_SEL       0x0020
 #define TIMER_IRQ_ENA       0x0010
 
 /* The actual gptimer API */
 
-void     set_gptimer_pwidth    (int timer_id, uint32_t width);
-uint32_t get_gptimer_pwidth    (int timer_id);
-void     set_gptimer_period    (int timer_id, uint32_t period);
-uint32_t get_gptimer_period    (int timer_id);
-uint32_t get_gptimer_count     (int timer_id);
-uint16_t get_gptimer_intr      (int timer_id);
-void     clear_gptimer_intr    (int timer_id);
-uint16_t get_gptimer_over      (int timer_id);
-void     clear_gptimer_over    (int timer_id);
-void     set_gptimer_config    (int timer_id, uint16_t config);
-uint16_t get_gptimer_config    (int timer_id);
-void     set_gptimer_pulse_hi  (int timer_id);
+void     set_gptimer_pwidth(int timer_id, uint32_t width);
+uint32_t get_gptimer_pwidth(int timer_id);
+void     set_gptimer_period(int timer_id, uint32_t period);
+uint32_t get_gptimer_period(int timer_id);
+uint32_t get_gptimer_count(int timer_id);
+int      get_gptimer_intr(int timer_id);
+void     clear_gptimer_intr(int timer_id);
+int      get_gptimer_over(int timer_id);
+void     clear_gptimer_over(int timer_id);
+void     set_gptimer_config(int timer_id, uint16_t config);
+uint16_t get_gptimer_config(int timer_id);
+int      get_gptimer_run(int timer_id);
+void     set_gptimer_pulse_hi(int timer_id);
 void     clear_gptimer_pulse_hi(int timer_id);
-void     enable_gptimers       (uint16_t mask);
-void     disable_gptimers      (uint16_t mask);
-uint16_t get_enabled_gptimers  (void);
-uint32_t get_gptimer_status    (int group);
-void     set_gptimer_status    (int group, uint32_t value);
+void     enable_gptimers(uint16_t mask);
+void     disable_gptimers(uint16_t mask);
+void     disable_gptimers_sync(uint16_t mask);
+uint16_t get_enabled_gptimers(void);
+uint32_t get_gptimer_status(int group);
+void     set_gptimer_status(int group, uint32_t value);
 
 #endif
index 63b2d8c78570170eb0ca4bb5984ad92192fd8fd5..3022b5c96b3769841418ac7ef29e6088ff0a04bb 100644 (file)
@@ -80,19 +80,22 @@ static inline unsigned int readl(const volatile void __iomem *addr)
 #define memcpy_fromio(a,b,c)   memcpy((a),(void *)(b),(c))
 #define memcpy_toio(a,b,c)     memcpy((void *)(a),(b),(c))
 
-#define inb(addr)    readb(addr)
-#define inw(addr)    readw(addr)
-#define inl(addr)    readl(addr)
-#define outb(x,addr) ((void) writeb(x,addr))
-#define outw(x,addr) ((void) writew(x,addr))
-#define outl(x,addr) ((void) writel(x,addr))
-
-#define inb_p(addr)    inb(addr)
-#define inw_p(addr)    inw(addr)
-#define inl_p(addr)    inl(addr)
-#define outb_p(x,addr) outb(x,addr)
-#define outw_p(x,addr) outw(x,addr)
-#define outl_p(x,addr) outl(x,addr)
+/* Convert "I/O port addresses" to actual addresses.  i.e. ugly casts. */
+#define __io(port) ((void *)(unsigned long)(port))
+
+#define inb(port)    readb(__io(port))
+#define inw(port)    readw(__io(port))
+#define inl(port)    readl(__io(port))
+#define outb(x,port) writeb(x,__io(port))
+#define outw(x,port) writew(x,__io(port))
+#define outl(x,port) writel(x,__io(port))
+
+#define inb_p(port)    inb(__io(port))
+#define inw_p(port)    inw(__io(port))
+#define inl_p(port)    inl(__io(port))
+#define outb_p(x,port) outb(x,__io(port))
+#define outw_p(x,port) outw(x,__io(port))
+#define outl_p(x,port) outl(x,__io(port))
 
 #define ioread8_rep(a,d,c)     readsb(a,d,c)
 #define ioread16_rep(a,d,c)    readsw(a,d,c)
index 343b56361ec98db86252f75a3e0f01f07bbea7c1..51d0bf5e2899d4543810e344d842591057b210ee 100644 (file)
 #include <asm/atomic.h>
 #include <asm/traps.h>
 
-#define IPIPE_ARCH_STRING     "1.9-00"
+#define IPIPE_ARCH_STRING     "1.9-01"
 #define IPIPE_MAJOR_NUMBER    1
 #define IPIPE_MINOR_NUMBER    9
-#define IPIPE_PATCH_NUMBER    0
+#define IPIPE_PATCH_NUMBER    1
 
 #ifdef CONFIG_SMP
 #error "I-pipe/blackfin: SMP not implemented"
index 344f6a8c1f22a9cebe0a86f38c3955e30c0032a5..3ea2016a1d4a344e475d09c1154594e8007d0e94 100644 (file)
@@ -81,7 +81,7 @@ extern unsigned long memory_end;
 #define        virt_addr_valid(kaddr)  (((void *)(kaddr) >= (void *)PAGE_OFFSET) && \
                                ((void *)(kaddr) < (void *)memory_end))
 
-#include <asm-generic/page.h>
+#include <asm-generic/getorder.h>
 
 #endif                         /* __ASSEMBLY__ */
 
index a67142740df0b4fa9592ec8c95c8df93dd0d5617..b42555c1431cb5683605af10918389d151aeb708 100644 (file)
@@ -64,8 +64,6 @@ struct blackfin_pda {                 /* Per-processor Data Area */
 
 extern struct blackfin_pda cpu_pda[];
 
-void reserve_pda(void);
-
 #endif /* __ASSEMBLY__ */
 
 #endif /* _ASM_BLACKFIN_PDA_H */
index 0eece23b41c7c1ddaeb2f04908c20b5c9f16cd0f..3040415523b2120a338321beddb66b2cfdc8f3e1 100644 (file)
@@ -131,8 +131,8 @@ unsigned long get_wchan(struct task_struct *p);
 /* Get the Silicon Revision of the chip */
 static inline uint32_t __pure bfin_revid(void)
 {
-       /* stored in the upper 4 bits */
-       uint32_t revid = bfin_read_CHIPID() >> 28;
+       /* Always use CHIPID, to work around ANOMALY_05000234 */
+       uint32_t revid = (bfin_read_CHIPID() & CHIPID_VERSION) >> 28;
 
 #ifdef CONFIG_BF52x
        /* ANOMALY_05000357
index 87951d25145813e183459d03a15bf7000da69408..2eea907944545aad94b54cba586d2eec1c36ac32 100644 (file)
@@ -104,7 +104,7 @@ typedef unsigned long sigset_t;
 #define MINSIGSTKSZ    2048
 #define SIGSTKSZ       8192
 
-#include <asm-generic/signal.h>
+#include <asm-generic/signal-defs.h>
 
 #ifdef __KERNEL__
 struct old_sigaction {
index ddc43ce385338054a68a1c50a149c01b53185ce7..589e937ed1ebedfd4e7802ff68316fad0255cc9e 100644 (file)
@@ -37,4 +37,5 @@ extern unsigned long long __bfin_cycles_off;
 extern unsigned int __bfin_cycles_mod;
 #endif
 
+extern void __init setup_core_timer(void);
 #endif
index 3248033531e607b98d823351a3c05910aea5bfce..8894e9ffbb575b51d5e4857fa1e467574d0d12ba 100644 (file)
@@ -59,12 +59,8 @@ static inline int is_in_rom(unsigned long addr)
 #ifndef CONFIG_ACCESS_CHECK
 static inline int _access_ok(unsigned long addr, unsigned long size) { return 1; }
 #else
-#ifdef CONFIG_ACCESS_OK_L1
-extern int _access_ok(unsigned long addr, unsigned long size)__attribute__((l1_text));
-#else
 extern int _access_ok(unsigned long addr, unsigned long size);
 #endif
-#endif
 
 /*
  * The exception table consists of pairs of addresses: the first is the
@@ -83,9 +79,6 @@ struct exception_table_entry {
        unsigned long insn, fixup;
 };
 
-/* Returns 0 if exception not found and fixup otherwise.  */
-extern unsigned long search_exception_table(unsigned long);
-
 /*
  * These are the main single-value transfer routines.  They automatically
  * use the right size if we just have the right pointer type.
@@ -233,16 +226,29 @@ strncpy_from_user(char *dst, const char *src, long count)
 }
 
 /*
- * Return the size of a string (including the ending 0)
+ * Get the size of a string in user space.
+ *   src: The string to measure
+ *     n: The maximum valid length
  *
- * Return 0 on exception, a value greater than N if too long
+ * Get the size of a NUL-terminated string in user space.
+ *
+ * Returns the size of the string INCLUDING the terminating NUL.
+ * On exception, returns 0.
+ * If the string is too long, returns a value greater than n.
  */
-static inline long strnlen_user(const char *src, long n)
+static inline long __must_check strnlen_user(const char *src, long n)
 {
-       return (strlen(src) + 1);
+       if (!access_ok(VERIFY_READ, src, 1))
+               return 0;
+       return strnlen(src, n) + 1;
 }
 
-#define strlen_user(str) strnlen_user(str, 32767)
+static inline long __must_check strlen_user(const char *src)
+{
+       if (!access_ok(VERIFY_READ, src, 1))
+               return 0;
+       return strlen(src) + 1;
+}
 
 /*
  * Zero Userspace
@@ -251,6 +257,8 @@ static inline long strnlen_user(const char *src, long n)
 static inline unsigned long __must_check
 __clear_user(void *to, unsigned long n)
 {
+       if (!access_ok(VERIFY_WRITE, to, n))
+               return n;
        memset(to, 0, n);
        return 0;
 }
index 8531693fb48d8b5cddc10e60b13d3fc5f5b56275..763ed84ba459f3236fd7831075cdb002bd610d96 100644 (file)
 #include <asm/dma.h>
 #include <asm/uaccess.h>
 
+/*
+ * To make sure we work around 05000119 - we always check DMA_DONE bit,
+ * never the DMA_RUN bit
+ */
+
 struct dma_channel dma_ch[MAX_DMA_CHANNELS];
 EXPORT_SYMBOL(dma_ch);
 
@@ -232,6 +237,87 @@ void blackfin_dma_resume(void)
 void __init blackfin_dma_early_init(void)
 {
        bfin_write_MDMA_S0_CONFIG(0);
+       bfin_write_MDMA_S1_CONFIG(0);
+}
+
+void __init early_dma_memcpy(void *pdst, const void *psrc, size_t size)
+{
+       unsigned long dst = (unsigned long)pdst;
+       unsigned long src = (unsigned long)psrc;
+       struct dma_register *dst_ch, *src_ch;
+
+       /* We assume that everything is 4 byte aligned, so include
+        * a basic sanity check
+        */
+       BUG_ON(dst % 4);
+       BUG_ON(src % 4);
+       BUG_ON(size % 4);
+
+       /* Force a sync in case a previous config reset on this channel
+        * occurred.  This is needed so subsequent writes to DMA registers
+        * are not spuriously lost/corrupted.
+        */
+       __builtin_bfin_ssync();
+
+       src_ch = 0;
+       /* Find an avalible memDMA channel */
+       while (1) {
+               if (!src_ch || src_ch == (struct dma_register *)MDMA_S1_NEXT_DESC_PTR) {
+                       dst_ch = (struct dma_register *)MDMA_D0_NEXT_DESC_PTR;
+                       src_ch = (struct dma_register *)MDMA_S0_NEXT_DESC_PTR;
+               } else {
+                       dst_ch = (struct dma_register *)MDMA_D1_NEXT_DESC_PTR;
+                       src_ch = (struct dma_register *)MDMA_S1_NEXT_DESC_PTR;
+               }
+
+               if (!bfin_read16(&src_ch->cfg)) {
+                       break;
+               } else {
+                       if (bfin_read16(&src_ch->irq_status) & DMA_DONE)
+                               bfin_write16(&src_ch->cfg, 0);
+               }
+
+       }
+
+       /* Destination */
+       bfin_write32(&dst_ch->start_addr, dst);
+       bfin_write16(&dst_ch->x_count, size >> 2);
+       bfin_write16(&dst_ch->x_modify, 1 << 2);
+       bfin_write16(&dst_ch->irq_status, DMA_DONE | DMA_ERR);
+
+       /* Source */
+       bfin_write32(&src_ch->start_addr, src);
+       bfin_write16(&src_ch->x_count, size >> 2);
+       bfin_write16(&src_ch->x_modify, 1 << 2);
+       bfin_write16(&src_ch->irq_status, DMA_DONE | DMA_ERR);
+
+       /* Enable */
+       bfin_write16(&src_ch->cfg, DMAEN | WDSIZE_32);
+       bfin_write16(&dst_ch->cfg, WNR | DI_EN | DMAEN | WDSIZE_32);
+
+       /* Since we are atomic now, don't use the workaround ssync */
+       __builtin_bfin_ssync();
+}
+
+void __init early_dma_memcpy_done(void)
+{
+       while ((bfin_read_MDMA_S0_CONFIG() && !(bfin_read_MDMA_D0_IRQ_STATUS() & DMA_DONE)) ||
+              (bfin_read_MDMA_S1_CONFIG() && !(bfin_read_MDMA_D1_IRQ_STATUS() & DMA_DONE)))
+               continue;
+
+       bfin_write_MDMA_D0_IRQ_STATUS(DMA_DONE | DMA_ERR);
+       bfin_write_MDMA_D1_IRQ_STATUS(DMA_DONE | DMA_ERR);
+       /*
+        * Now that DMA is done, we would normally flush cache, but
+        * i/d cache isn't running this early, so we don't bother,
+        * and just clear out the DMA channel for next time
+        */
+       bfin_write_MDMA_S0_CONFIG(0);
+       bfin_write_MDMA_S1_CONFIG(0);
+       bfin_write_MDMA_D0_CONFIG(0);
+       bfin_write_MDMA_D1_CONFIG(0);
+
+       __builtin_bfin_ssync();
 }
 
 /**
index a0678da405328a3cbef29b65a95ac1f00d524f2a..beffa00a93c3a6e2d98f236e82f1653e232c40e8 100644 (file)
@@ -313,15 +313,6 @@ inline void portmux_setup(unsigned short per)
 # define portmux_setup(...)  do { } while (0)
 #endif
 
-static int __init bfin_gpio_init(void)
-{
-       printk(KERN_INFO "Blackfin GPIO Controller\n");
-
-       return 0;
-}
-arch_initcall(bfin_gpio_init);
-
-
 #ifndef CONFIG_BF54x
 /***********************************************************
 *
@@ -1021,15 +1012,6 @@ int bfin_gpio_irq_request(unsigned gpio, const char *label)
 
        local_irq_save_hw(flags);
 
-       if (unlikely(reserved_gpio_irq_map[gpio_bank(gpio)] & gpio_bit(gpio))) {
-               if (system_state == SYSTEM_BOOTING)
-                       dump_stack();
-               printk(KERN_ERR
-                      "bfin-gpio: GPIO %d is already reserved as gpio-irq !\n",
-                      gpio);
-               local_irq_restore_hw(flags);
-               return -EBUSY;
-       }
        if (unlikely(reserved_peri_map[gpio_bank(gpio)] & gpio_bit(gpio))) {
                if (system_state == SYSTEM_BOOTING)
                        dump_stack();
index 01f917d58b5908e65f3feeda75b89ec9c6fec407..53e893ff708aa547674aaab28c0974c54272f37c 100644 (file)
@@ -16,7 +16,6 @@ EXPORT_SYMBOL(bfin_return_from_exception);
 
 /* All the Blackfin cache functions: mach-common/cache.S */
 EXPORT_SYMBOL(blackfin_dcache_invalidate_range);
-EXPORT_SYMBOL(blackfin_icache_dcache_flush_range);
 EXPORT_SYMBOL(blackfin_icache_flush_range);
 EXPORT_SYMBOL(blackfin_dcache_flush_range);
 EXPORT_SYMBOL(blackfin_dflush_page);
index c6ff947f9d377ff30ec60a343deec485bc4390ae..d5a86c3017f7cfa8ca571f507224ac058881dbc7 100644 (file)
@@ -55,7 +55,14 @@ void __cpuinit bfin_dcache_init(struct cplb_entry *dcplb_tbl)
        }
 
        ctrl = bfin_read_DMEM_CONTROL();
-       ctrl |= DMEM_CNTR;
+
+       /*
+        *  Anomaly notes:
+        *  05000287 - We implement workaround #2 - Change the DMEM_CONTROL
+        *  register, so that the port preferences for DAG0 and DAG1 are set
+        *  to port B
+        */
+       ctrl |= DMEM_CNTR | PORT_PREF0 | (ANOMALY_05000287 ? PORT_PREF1 : 0);
        bfin_write_DMEM_CONTROL(ctrl);
        SSYNC();
 }
index 3e329a6ce041ad79e5429d0e0ea557ca98478569..c006a44527bf7a54907e87afe7ba7cea3201f199 100644 (file)
@@ -64,7 +64,7 @@ void __init generate_cplb_tables_cpu(unsigned int cpu)
        dcplb_tbl[cpu][i_d++].data = SDRAM_OOPS | PAGE_SIZE_1KB;
 
        icplb_tbl[cpu][i_i].addr = 0;
-       icplb_tbl[cpu][i_i++].data = i_cache | CPLB_USER_RD | PAGE_SIZE_1KB;
+       icplb_tbl[cpu][i_i++].data = CPLB_VALID | i_cache | CPLB_USER_RD | PAGE_SIZE_1KB;
 
        /* Cover kernel memory with 4M pages.  */
        addr = 0;
index c6ff947f9d377ff30ec60a343deec485bc4390ae..d5a86c3017f7cfa8ca571f507224ac058881dbc7 100644 (file)
@@ -55,7 +55,14 @@ void __cpuinit bfin_dcache_init(struct cplb_entry *dcplb_tbl)
        }
 
        ctrl = bfin_read_DMEM_CONTROL();
-       ctrl |= DMEM_CNTR;
+
+       /*
+        *  Anomaly notes:
+        *  05000287 - We implement workaround #2 - Change the DMEM_CONTROL
+        *  register, so that the port preferences for DAG0 and DAG1 are set
+        *  to port B
+        */
+       ctrl |= DMEM_CNTR | PORT_PREF0 | (ANOMALY_05000287 ? PORT_PREF1 : 0);
        bfin_write_DMEM_CONTROL(ctrl);
        SSYNC();
 }
index c8ad051742e256f8adae84d935b533582e634310..3302719173ca63ba39656ad5d8a5623c74994993 100644 (file)
@@ -178,25 +178,15 @@ int __init setup_early_printk(char *buf)
 
 asmlinkage void __init init_early_exception_vectors(void)
 {
+       u32 evt;
        SSYNC();
 
        /* cannot program in software:
         * evt0 - emulation (jtag)
         * evt1 - reset
         */
-       bfin_write_EVT2(early_trap);
-       bfin_write_EVT3(early_trap);
-       bfin_write_EVT5(early_trap);
-       bfin_write_EVT6(early_trap);
-       bfin_write_EVT7(early_trap);
-       bfin_write_EVT8(early_trap);
-       bfin_write_EVT9(early_trap);
-       bfin_write_EVT10(early_trap);
-       bfin_write_EVT11(early_trap);
-       bfin_write_EVT12(early_trap);
-       bfin_write_EVT13(early_trap);
-       bfin_write_EVT14(early_trap);
-       bfin_write_EVT15(early_trap);
+       for (evt = EVT2; evt <= EVT15; evt += 4)
+               bfin_write32(evt, early_trap);
        CSYNC();
 
        /* Set all the return from interrupt, exception, NMI to a known place
index 3a3e9615b0027a68c2c2d8e3ffc7386cd0c9dcd3..7281a91d26b5cfc2c328eef44ae327ac753bd6b4 100644 (file)
@@ -189,10 +189,10 @@ void set_gptimer_status(int group, uint32_t value)
 }
 EXPORT_SYMBOL(set_gptimer_status);
 
-uint16_t get_gptimer_intr(int timer_id)
+int get_gptimer_intr(int timer_id)
 {
        tassert(timer_id < MAX_BLACKFIN_GPTIMERS);
-       return (group_regs[BFIN_TIMER_OCTET(timer_id)]->status & timil_mask[timer_id]) ? 1 : 0;
+       return !!(group_regs[BFIN_TIMER_OCTET(timer_id)]->status & timil_mask[timer_id]);
 }
 EXPORT_SYMBOL(get_gptimer_intr);
 
@@ -203,10 +203,10 @@ void clear_gptimer_intr(int timer_id)
 }
 EXPORT_SYMBOL(clear_gptimer_intr);
 
-uint16_t get_gptimer_over(int timer_id)
+int get_gptimer_over(int timer_id)
 {
        tassert(timer_id < MAX_BLACKFIN_GPTIMERS);
-       return (group_regs[BFIN_TIMER_OCTET(timer_id)]->status & tovf_mask[timer_id]) ? 1 : 0;
+       return !!(group_regs[BFIN_TIMER_OCTET(timer_id)]->status & tovf_mask[timer_id]);
 }
 EXPORT_SYMBOL(get_gptimer_over);
 
@@ -217,6 +217,13 @@ void clear_gptimer_over(int timer_id)
 }
 EXPORT_SYMBOL(clear_gptimer_over);
 
+int get_gptimer_run(int timer_id)
+{
+       tassert(timer_id < MAX_BLACKFIN_GPTIMERS);
+       return !!(group_regs[BFIN_TIMER_OCTET(timer_id)]->status & trun_mask[timer_id]);
+}
+EXPORT_SYMBOL(get_gptimer_run);
+
 void set_gptimer_config(int timer_id, uint16_t config)
 {
        tassert(timer_id < MAX_BLACKFIN_GPTIMERS);
@@ -244,7 +251,7 @@ void enable_gptimers(uint16_t mask)
 }
 EXPORT_SYMBOL(enable_gptimers);
 
-void disable_gptimers(uint16_t mask)
+static void _disable_gptimers(uint16_t mask)
 {
        int i;
        uint16_t m = mask;
@@ -253,6 +260,12 @@ void disable_gptimers(uint16_t mask)
                group_regs[i]->disable = m & 0xFF;
                m >>= 8;
        }
+}
+
+void disable_gptimers(uint16_t mask)
+{
+       int i;
+       _disable_gptimers(mask);
        for (i = 0; i < MAX_BLACKFIN_GPTIMERS; ++i)
                if (mask & (1 << i))
                        group_regs[BFIN_TIMER_OCTET(i)]->status |= trun_mask[i];
@@ -260,6 +273,13 @@ void disable_gptimers(uint16_t mask)
 }
 EXPORT_SYMBOL(disable_gptimers);
 
+void disable_gptimers_sync(uint16_t mask)
+{
+       _disable_gptimers(mask);
+       SSYNC();
+}
+EXPORT_SYMBOL(disable_gptimers_sync);
+
 void set_gptimer_pulse_hi(int timer_id)
 {
        tassert(timer_id < MAX_BLACKFIN_GPTIMERS);
index a5de8d45424cda8b29f21d338e6224c94892cb1b..5fc424803a1788409a3b51d479c242d9ee16b6ae 100644 (file)
@@ -167,7 +167,7 @@ int __ipipe_check_root(void)
 void __ipipe_enable_irqdesc(struct ipipe_domain *ipd, unsigned irq)
 {
        struct irq_desc *desc = irq_to_desc(irq);
-       int prio = desc->ic_prio;
+       int prio = __ipipe_get_irq_priority(irq);
 
        desc->depth = 0;
        if (ipd != &ipipe_root &&
@@ -178,8 +178,7 @@ EXPORT_SYMBOL(__ipipe_enable_irqdesc);
 
 void __ipipe_disable_irqdesc(struct ipipe_domain *ipd, unsigned irq)
 {
-       struct irq_desc *desc = irq_to_desc(irq);
-       int prio = desc->ic_prio;
+       int prio = __ipipe_get_irq_priority(irq);
 
        if (ipd != &ipipe_root &&
            atomic_dec_and_test(&__ipipe_irq_lvdepth[prio]))
@@ -310,12 +309,16 @@ int ipipe_trigger_irq(unsigned irq)
 
 asmlinkage void __ipipe_sync_root(void)
 {
+       void (*irq_tail_hook)(void) = (void (*)(void))__ipipe_irq_tail_hook;
        unsigned long flags;
 
        BUG_ON(irqs_disabled());
 
        local_irq_save_hw(flags);
 
+       if (irq_tail_hook)
+               irq_tail_hook();
+
        clear_thread_flag(TIF_IRQ_SYNC);
 
        if (ipipe_root_cpudom_var(irqpend_himask) != 0)
index 401bd32aa499f10be2009a4a622b0e1a530994b4..6e31e935bb31a03fe1cadc78fd1787c0e87b1516 100644 (file)
@@ -59,12 +59,14 @@ static struct irq_chip bad_chip = {
        .unmask = dummy_mask_unmask_irq,
 };
 
+static int bad_stats;
 static struct irq_desc bad_irq_desc = {
        .status = IRQ_DISABLED,
        .chip = &bad_chip,
        .handle_irq = handle_bad_irq,
        .depth = 1,
        .lock = __SPIN_LOCK_UNLOCKED(irq_desc->lock),
+       .kstat_irqs = &bad_stats,
 #ifdef CONFIG_SMP
        .affinity = CPU_MASK_ALL
 #endif
index b163f6d3330d4e0a9317ee116171f9718b5fd45d..da28f796ad7889d43483eef8a3bd3c454ffd89d6 100644 (file)
@@ -466,7 +466,7 @@ static int validate_memory_access_address(unsigned long addr, int size)
        int cpu = raw_smp_processor_id();
 
        if (size < 0)
-               return EFAULT;
+               return -EFAULT;
        if (addr >= 0x1000 && (addr + size) <= physical_mem_end)
                return 0;
        if (addr >= SYSMMR_BASE)
@@ -498,7 +498,7 @@ static int validate_memory_access_address(unsigned long addr, int size)
        if (IN_MEM(addr, size, L2_START, L2_LENGTH))
                return 0;
 
-       return EFAULT;
+       return -EFAULT;
 }
 
 /*
@@ -508,14 +508,15 @@ static int validate_memory_access_address(unsigned long addr, int size)
 int kgdb_mem2hex(char *mem, char *buf, int count)
 {
        char *tmp;
-       int err = 0;
+       int err;
        unsigned char *pch;
        unsigned short mmr16;
        unsigned long mmr32;
        int cpu = raw_smp_processor_id();
 
-       if (validate_memory_access_address((unsigned long)mem, count))
-               return EFAULT;
+       err = validate_memory_access_address((unsigned long)mem, count);
+       if (err)
+               return err;
 
        /*
         * We use the upper half of buf as an intermediate buffer for the
@@ -533,7 +534,7 @@ int kgdb_mem2hex(char *mem, char *buf, int count)
                                *tmp++ = *pch++;
                                tmp -= 2;
                        } else
-                               err = EFAULT;
+                               err = -EFAULT;
                        break;
                case 4:
                        if ((unsigned int)mem % 4 == 0) {
@@ -545,10 +546,10 @@ int kgdb_mem2hex(char *mem, char *buf, int count)
                                *tmp++ = *pch++;
                                tmp -= 4;
                        } else
-                               err = EFAULT;
+                               err = -EFAULT;
                        break;
                default:
-                       err = EFAULT;
+                       err = -EFAULT;
                }
        } else if ((cpu == 0 && IN_MEM(mem, count, L1_CODE_START, L1_CODE_LENGTH))
 #ifdef CONFIG_SMP
@@ -557,7 +558,7 @@ int kgdb_mem2hex(char *mem, char *buf, int count)
                ) {
                /* access L1 instruction SRAM*/
                if (dma_memcpy(tmp, mem, count) == NULL)
-                       err = EFAULT;
+                       err = -EFAULT;
        } else
                err = probe_kernel_read(tmp, mem, count);
 
@@ -585,24 +586,24 @@ int kgdb_ebin2mem(char *buf, char *mem, int count)
        char *tmp_new;
        unsigned short *mmr16;
        unsigned long *mmr32;
-       int err = 0;
-       int size = 0;
+       int err;
+       int size;
        int cpu = raw_smp_processor_id();
 
        tmp_old = tmp_new = buf;
 
-       while (count-- > 0) {
+       for (size = 0; size < count; ++size) {
                if (*tmp_old == 0x7d)
                        *tmp_new = *(++tmp_old) ^ 0x20;
                else
                        *tmp_new = *tmp_old;
                tmp_new++;
                tmp_old++;
-               size++;
        }
 
-       if (validate_memory_access_address((unsigned long)mem, size))
-               return EFAULT;
+       err = validate_memory_access_address((unsigned long)mem, size);
+       if (err)
+               return err;
 
        if ((unsigned int)mem >= SYSMMR_BASE) { /*access MMR registers*/
                switch (size) {
@@ -611,17 +612,17 @@ int kgdb_ebin2mem(char *buf, char *mem, int count)
                                mmr16 = (unsigned short *)buf;
                                *(unsigned short *)mem = *mmr16;
                        } else
-                               return EFAULT;
+                               err = -EFAULT;
                        break;
                case 4:
                        if ((unsigned int)mem % 4 == 0) {
                                mmr32 = (unsigned long *)buf;
                                *(unsigned long *)mem = *mmr32;
                        } else
-                               return EFAULT;
+                               err = -EFAULT;
                        break;
                default:
-                       return EFAULT;
+                       err = -EFAULT;
                }
        } else if ((cpu == 0 && IN_MEM(mem, count, L1_CODE_START, L1_CODE_LENGTH))
 #ifdef CONFIG_SMP
@@ -630,7 +631,7 @@ int kgdb_ebin2mem(char *buf, char *mem, int count)
                ) {
                /* access L1 instruction SRAM */
                if (dma_memcpy(mem, buf, size) == NULL)
-                       err = EFAULT;
+                       err = -EFAULT;
        } else
                err = probe_kernel_write(mem, buf, size);
 
@@ -648,10 +649,12 @@ int kgdb_hex2mem(char *buf, char *mem, int count)
        char *tmp_hex;
        unsigned short *mmr16;
        unsigned long *mmr32;
+       int err;
        int cpu = raw_smp_processor_id();
 
-       if (validate_memory_access_address((unsigned long)mem, count))
-               return EFAULT;
+       err = validate_memory_access_address((unsigned long)mem, count);
+       if (err)
+               return err;
 
        /*
         * We use the upper half of buf as an intermediate buffer for the
@@ -673,17 +676,17 @@ int kgdb_hex2mem(char *buf, char *mem, int count)
                                mmr16 = (unsigned short *)tmp_raw;
                                *(unsigned short *)mem = *mmr16;
                        } else
-                               return EFAULT;
+                               err = -EFAULT;
                        break;
                case 4:
                        if ((unsigned int)mem % 4 == 0) {
                                mmr32 = (unsigned long *)tmp_raw;
                                *(unsigned long *)mem = *mmr32;
                        } else
-                               return EFAULT;
+                               err = -EFAULT;
                        break;
                default:
-                       return EFAULT;
+                       err = -EFAULT;
                }
        } else if ((cpu == 0 && IN_MEM(mem, count, L1_CODE_START, L1_CODE_LENGTH))
 #ifdef CONFIG_SMP
@@ -692,10 +695,11 @@ int kgdb_hex2mem(char *buf, char *mem, int count)
                ) {
                /* access L1 instruction SRAM */
                if (dma_memcpy(mem, tmp_raw, count) == NULL)
-                       return EFAULT;
+                       err = -EFAULT;
        } else
-               return probe_kernel_write(mem, tmp_raw, count);
-       return 0;
+               err = probe_kernel_write(mem, tmp_raw, count);
+
+       return err;
 }
 
 int kgdb_validate_break_address(unsigned long addr)
@@ -715,7 +719,7 @@ int kgdb_validate_break_address(unsigned long addr)
        if (IN_MEM(addr, BREAK_INSTR_SIZE, L2_START, L2_LENGTH))
                return 0;
 
-       return EFAULT;
+       return -EFAULT;
 }
 
 int kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr)
index 1bd7f2d018a85892c92ebcb8277e7547727c71fe..d5aee362668838f11d2f924ed47b05e34e4bb835 100644 (file)
@@ -201,8 +201,8 @@ apply_relocate(Elf_Shdr * sechdrs, const char *strtab,
 /*            Arithmetic relocations are handled.                        */
 /*            We do not expect LSETUP to be split and hence is not       */
 /*            handled.                                                   */
-/*            R_byte and R_byte2 are also not handled as the gas         */
-/*            does not generate it.                                      */
+/*            R_BFIN_BYTE and R_BFIN_BYTE2 are also not handled as the   */
+/*            gas does not generate it.                                  */
 /*************************************************************************/
 int
 apply_relocate_add(Elf_Shdr * sechdrs, const char *strtab,
@@ -243,8 +243,8 @@ apply_relocate_add(Elf_Shdr * sechdrs, const char *strtab,
 #endif
                switch (ELF32_R_TYPE(rel[i].r_info)) {
 
-               case R_pcrel24:
-               case R_pcrel24_jump_l:
+               case R_BFIN_PCREL24:
+               case R_BFIN_PCREL24_JUMP_L:
                        /* Add the value, subtract its postition */
                        location16 =
                            (uint16_t *) (sechdrs[sechdrs[relsec].sh_info].
@@ -266,18 +266,18 @@ apply_relocate_add(Elf_Shdr * sechdrs, const char *strtab,
                            (*location16 & 0xff00) | (value >> 16 & 0x00ff);
                        *(location16 + 1) = value & 0xffff;
                        break;
-               case R_pcrel12_jump:
-               case R_pcrel12_jump_s:
+               case R_BFIN_PCREL12_JUMP:
+               case R_BFIN_PCREL12_JUMP_S:
                        value -= (uint32_t) location32;
                        value >>= 1;
                        *location16 = (value & 0xfff);
                        break;
-               case R_pcrel10:
+               case R_BFIN_PCREL10:
                        value -= (uint32_t) location32;
                        value >>= 1;
                        *location16 = (value & 0x3ff);
                        break;
-               case R_luimm16:
+               case R_BFIN_LUIMM16:
                        pr_debug("before %x after %x\n", *location16,
                                       (value & 0xffff));
                        tmp = (value & 0xffff);
@@ -286,7 +286,7 @@ apply_relocate_add(Elf_Shdr * sechdrs, const char *strtab,
                        } else
                                *location16 = tmp;
                        break;
-               case R_huimm16:
+               case R_BFIN_HUIMM16:
                        pr_debug("before %x after %x\n", *location16,
                                       ((value >> 16) & 0xffff));
                        tmp = ((value >> 16) & 0xffff);
@@ -295,10 +295,10 @@ apply_relocate_add(Elf_Shdr * sechdrs, const char *strtab,
                        } else
                                *location16 = tmp;
                        break;
-               case R_rimm16:
+               case R_BFIN_RIMM16:
                        *location16 = (value & 0xffff);
                        break;
-               case R_byte4_data:
+               case R_BFIN_BYTE4_DATA:
                        pr_debug("before %x after %x\n", *location32, value);
                        *location32 = value;
                        break;
index e040e03335ea3c54792e44651424bbad51db5728..30d0843ed70192aa70fa49d9a3657260f235299d 100644 (file)
@@ -322,6 +322,9 @@ void finish_atomic_sections (struct pt_regs *regs)
 }
 
 #if defined(CONFIG_ACCESS_CHECK)
+#ifdef CONFIG_ACCESS_OK_L1
+__attribute__((l1_text))
+#endif
 /* Return 1 if access to memory range is OK, 0 otherwise */
 int _access_ok(unsigned long addr, unsigned long size)
 {
index a58687bdee6a6580031d6903526774c3f35e1625..80447f99c2b5f43cb0af2662e8e3717e214ab3e3 100644 (file)
 #include <linux/tty.h>
 #include <linux/pfn.h>
 
+#ifdef CONFIG_MTD_UCLINUX
+#include <linux/mtd/map.h>
 #include <linux/ext2_fs.h>
 #include <linux/cramfs_fs.h>
 #include <linux/romfs_fs.h>
+#endif
 
 #include <asm/cplb.h>
 #include <asm/cacheflush.h>
@@ -45,6 +48,7 @@ EXPORT_SYMBOL(_ramend);
 EXPORT_SYMBOL(reserved_mem_dcache_on);
 
 #ifdef CONFIG_MTD_UCLINUX
+extern struct map_info uclinux_ram_map;
 unsigned long memory_mtd_end, memory_mtd_start, mtd_size;
 unsigned long _ebss;
 EXPORT_SYMBOL(memory_mtd_end);
@@ -150,40 +154,45 @@ void __init bfin_relocate_l1_mem(void)
        unsigned long l1_data_b_length;
        unsigned long l2_length;
 
+       /*
+        * due to the ALIGN(4) in the arch/blackfin/kernel/vmlinux.lds.S
+        * we know that everything about l1 text/data is nice and aligned,
+        * so copy by 4 byte chunks, and don't worry about overlapping
+        * src/dest.
+        *
+        * We can't use the dma_memcpy functions, since they can call
+        * scheduler functions which might be in L1 :( and core writes
+        * into L1 instruction cause bad access errors, so we are stuck,
+        * we are required to use DMA, but can't use the common dma
+        * functions. We can't use memcpy either - since that might be
+        * going to be in the relocated L1
+        */
+
        blackfin_dma_early_init();
 
+       /* if necessary, copy _stext_l1 to _etext_l1 to L1 instruction SRAM */
        l1_code_length = _etext_l1 - _stext_l1;
-       if (l1_code_length > L1_CODE_LENGTH)
-               panic("L1 Instruction SRAM Overflow\n");
-       /* cannot complain as printk is not available as yet.
-        * But we can continue booting and complain later!
-        */
-
-       /* Copy _stext_l1 to _etext_l1 to L1 instruction SRAM */
-       dma_memcpy(_stext_l1, _l1_lma_start, l1_code_length);
+       if (l1_code_length)
+               early_dma_memcpy(_stext_l1, _l1_lma_start, l1_code_length);
 
+       /* if necessary, copy _sdata_l1 to _sbss_l1 to L1 data bank A SRAM */
        l1_data_a_length = _sbss_l1 - _sdata_l1;
-       if (l1_data_a_length > L1_DATA_A_LENGTH)
-               panic("L1 Data SRAM Bank A Overflow\n");
-
-       /* Copy _sdata_l1 to _sbss_l1 to L1 data bank A SRAM */
-       dma_memcpy(_sdata_l1, _l1_lma_start + l1_code_length, l1_data_a_length);
+       if (l1_data_a_length)
+               early_dma_memcpy(_sdata_l1, _l1_lma_start + l1_code_length, l1_data_a_length);
 
+       /* if necessary, copy _sdata_b_l1 to _sbss_b_l1 to L1 data bank B SRAM */
        l1_data_b_length = _sbss_b_l1 - _sdata_b_l1;
-       if (l1_data_b_length > L1_DATA_B_LENGTH)
-               panic("L1 Data SRAM Bank B Overflow\n");
-
-       /* Copy _sdata_b_l1 to _sbss_b_l1 to L1 data bank B SRAM */
-       dma_memcpy(_sdata_b_l1, _l1_lma_start + l1_code_length +
+       if (l1_data_b_length)
+               early_dma_memcpy(_sdata_b_l1, _l1_lma_start + l1_code_length +
                        l1_data_a_length, l1_data_b_length);
 
+       early_dma_memcpy_done();
+
+       /* if necessary, copy _stext_l2 to _edata_l2 to L2 SRAM */
        if (L2_LENGTH != 0) {
                l2_length = _sbss_l2 - _stext_l2;
-               if (l2_length > L2_LENGTH)
-                       panic("L2 SRAM Overflow\n");
-
-               /* Copy _stext_l2 to _edata_l2 to L2 SRAM */
-               dma_memcpy(_stext_l2, _l2_lma_start, l2_length);
+               if (l2_length)
+                       memcpy(_stext_l2, _l2_lma_start, l2_length);
        }
 }
 
@@ -472,7 +481,7 @@ static __init void memory_setup(void)
 
        if (DMA_UNCACHED_REGION > (_ramend - _ramstart)) {
                console_init();
-               panic("DMA region exceeds memory limit: %lu.\n",
+               panic("DMA region exceeds memory limit: %lu.",
                        _ramend - _ramstart);
        }
        memory_end = _ramend - DMA_UNCACHED_REGION;
@@ -526,14 +535,13 @@ static __init void memory_setup(void)
 
        if (mtd_size == 0) {
                console_init();
-               panic("Don't boot kernel without rootfs attached.\n");
+               panic("Don't boot kernel without rootfs attached.");
        }
 
        /* Relocate MTD image to the top of memory after the uncached memory area */
-       dma_memcpy((char *)memory_end, _end, mtd_size);
-
-       memory_mtd_start = memory_end;
-       _ebss = memory_mtd_start;       /* define _ebss for compatible */
+       uclinux_ram_map.phys = memory_mtd_start = memory_end;
+       uclinux_ram_map.size = mtd_size;
+       dma_memcpy((void *)uclinux_ram_map.phys, _end, uclinux_ram_map.size);
 #endif                         /* CONFIG_MTD_UCLINUX */
 
 #if (defined(CONFIG_BFIN_ICACHE) && ANOMALY_05000263)
@@ -796,10 +804,8 @@ void __init setup_arch(char **cmdline_p)
        cclk = get_cclk();
        sclk = get_sclk();
 
-#if !defined(CONFIG_BFIN_KERNEL_CLOCK)
-       if (ANOMALY_05000273 && cclk == sclk)
-               panic("ANOMALY 05000273, SCLK can not be same as CCLK");
-#endif
+       if ((ANOMALY_05000273 || ANOMALY_05000274) && (cclk >> 1) < sclk)
+               panic("ANOMALY 05000273 or 05000274: CCLK must be >= 2*SCLK");
 
 #ifdef BF561_FAMILY
        if (ANOMALY_05000266) {
@@ -881,7 +887,7 @@ void __init setup_arch(char **cmdline_p)
                                printk(KERN_ERR "Warning: Compiled for Rev %d, but running on Rev %d\n",
                                       bfin_compiled_revid(), bfin_revid());
                                if (bfin_compiled_revid() > bfin_revid())
-                                       panic("Error: you are missing anomaly workarounds for this rev\n");
+                                       panic("Error: you are missing anomaly workarounds for this rev");
                        }
                }
                if (bfin_revid() < CONFIG_BF_REV_MIN || bfin_revid() > CONFIG_BF_REV_MAX)
@@ -891,16 +897,13 @@ void __init setup_arch(char **cmdline_p)
 
        /* We can't run on BF548-0.1 due to ANOMALY 05000448 */
        if (bfin_cpuid() == 0x27de && bfin_revid() == 1)
-               panic("You can't run on this processor due to 05000448\n");
+               panic("You can't run on this processor due to 05000448");
 
        printk(KERN_INFO "Blackfin Linux support by http://blackfin.uclinux.org/\n");
 
        printk(KERN_INFO "Processor Speed: %lu MHz core clock and %lu MHz System Clock\n",
               cclk / 1000000, sclk / 1000000);
 
-       if (ANOMALY_05000273 && (cclk >> 1) <= sclk)
-               printk("\n\n\nANOMALY_05000273: CCLK must be >= 2*SCLK !!!\n\n\n");
-
        setup_bootmem_allocator();
 
        paging_init();
index fce49d7cf0017f0014304f8e715477cb29fa8f6b..a8f1329c15a48befeb62f31a7b80603b263dda2d 100644 (file)
@@ -78,11 +78,6 @@ asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
        return do_mmap2(addr, len, prot, flags, fd, pgoff);
 }
 
-asmlinkage int sys_getpagesize(void)
-{
-       return PAGE_SIZE;
-}
-
 asmlinkage void *sys_sram_alloc(size_t size, unsigned long flags)
 {
        return sram_alloc_with_lsl(size, flags);
index 27646121280a3f876295071084129148b1c0f250..0791eba40d9fdc479509682c990099299912f85a 100644 (file)
@@ -20,8 +20,9 @@
 
 #include <asm/blackfin.h>
 #include <asm/time.h>
+#include <asm/gptimers.h>
 
-#ifdef CONFIG_CYCLES_CLOCKSOURCE
+#if defined(CONFIG_CYCLES_CLOCKSOURCE)
 
 /* Accelerators for sched_clock()
  * convert from cycles(64bits) => nanoseconds (64bits)
@@ -58,15 +59,15 @@ static inline unsigned long long cycles_2_ns(cycle_t cyc)
        return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR;
 }
 
-static cycle_t read_cycles(struct clocksource *cs)
+static cycle_t bfin_read_cycles(struct clocksource *cs)
 {
        return __bfin_cycles_off + (get_cycles() << __bfin_cycles_mod);
 }
 
-static struct clocksource clocksource_bfin = {
-       .name           = "bfin_cycles",
+static struct clocksource bfin_cs_cycles = {
+       .name           = "bfin_cs_cycles",
        .rating         = 350,
-       .read           = read_cycles,
+       .read           = bfin_read_cycles,
        .mask           = CLOCKSOURCE_MASK(64),
        .shift          = 22,
        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
@@ -74,53 +75,198 @@ static struct clocksource clocksource_bfin = {
 
 unsigned long long sched_clock(void)
 {
-       return cycles_2_ns(read_cycles(&clocksource_bfin));
+       return cycles_2_ns(bfin_read_cycles(&bfin_cs_cycles));
 }
 
-static int __init bfin_clocksource_init(void)
+static int __init bfin_cs_cycles_init(void)
 {
        set_cyc2ns_scale(get_cclk() / 1000);
 
-       clocksource_bfin.mult = clocksource_hz2mult(get_cclk(), clocksource_bfin.shift);
+       bfin_cs_cycles.mult = \
+               clocksource_hz2mult(get_cclk(), bfin_cs_cycles.shift);
 
-       if (clocksource_register(&clocksource_bfin))
+       if (clocksource_register(&bfin_cs_cycles))
                panic("failed to register clocksource");
 
        return 0;
 }
+#else
+# define bfin_cs_cycles_init()
+#endif
+
+#ifdef CONFIG_GPTMR0_CLOCKSOURCE
+
+void __init setup_gptimer0(void)
+{
+       disable_gptimers(TIMER0bit);
+
+       set_gptimer_config(TIMER0_id, \
+               TIMER_OUT_DIS | TIMER_PERIOD_CNT | TIMER_MODE_PWM);
+       set_gptimer_period(TIMER0_id, -1);
+       set_gptimer_pwidth(TIMER0_id, -2);
+       SSYNC();
+       enable_gptimers(TIMER0bit);
+}
+
+static cycle_t bfin_read_gptimer0(void)
+{
+       return bfin_read_TIMER0_COUNTER();
+}
+
+static struct clocksource bfin_cs_gptimer0 = {
+       .name           = "bfin_cs_gptimer0",
+       .rating         = 400,
+       .read           = bfin_read_gptimer0,
+       .mask           = CLOCKSOURCE_MASK(32),
+       .shift          = 22,
+       .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static int __init bfin_cs_gptimer0_init(void)
+{
+       setup_gptimer0();
 
+       bfin_cs_gptimer0.mult = \
+               clocksource_hz2mult(get_sclk(), bfin_cs_gptimer0.shift);
+
+       if (clocksource_register(&bfin_cs_gptimer0))
+               panic("failed to register clocksource");
+
+       return 0;
+}
 #else
-# define bfin_clocksource_init()
+# define bfin_cs_gptimer0_init()
 #endif
 
+#ifdef CONFIG_CORE_TIMER_IRQ_L1
+__attribute__((l1_text))
+#endif
+irqreturn_t timer_interrupt(int irq, void *dev_id);
+
+static int bfin_timer_set_next_event(unsigned long, \
+               struct clock_event_device *);
+
+static void bfin_timer_set_mode(enum clock_event_mode, \
+               struct clock_event_device *);
+
+static struct clock_event_device clockevent_bfin = {
+#if defined(CONFIG_TICKSOURCE_GPTMR0)
+       .name           = "bfin_gptimer0",
+       .rating         = 300,
+       .irq            = IRQ_TIMER0,
+#else
+       .name           = "bfin_core_timer",
+       .rating         = 350,
+       .irq            = IRQ_CORETMR,
+#endif
+       .shift          = 32,
+       .features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+       .set_next_event = bfin_timer_set_next_event,
+       .set_mode       = bfin_timer_set_mode,
+};
+
+static struct irqaction bfin_timer_irq = {
+#if defined(CONFIG_TICKSOURCE_GPTMR0)
+       .name           = "Blackfin GPTimer0",
+#else
+       .name           = "Blackfin CoreTimer",
+#endif
+       .flags          = IRQF_DISABLED | IRQF_TIMER | \
+                         IRQF_IRQPOLL | IRQF_PERCPU,
+       .handler        = timer_interrupt,
+       .dev_id         = &clockevent_bfin,
+};
+
+#if defined(CONFIG_TICKSOURCE_GPTMR0)
 static int bfin_timer_set_next_event(unsigned long cycles,
                                      struct clock_event_device *evt)
 {
+       disable_gptimers(TIMER0bit);
+
+       /* it starts counting three SCLK cycles after the TIMENx bit is set */
+       set_gptimer_pwidth(TIMER0_id, cycles - 3);
+       enable_gptimers(TIMER0bit);
+       return 0;
+}
+
+static void bfin_timer_set_mode(enum clock_event_mode mode,
+                               struct clock_event_device *evt)
+{
+       switch (mode) {
+       case CLOCK_EVT_MODE_PERIODIC: {
+               set_gptimer_config(TIMER0_id, \
+                       TIMER_OUT_DIS | TIMER_IRQ_ENA | \
+                       TIMER_PERIOD_CNT | TIMER_MODE_PWM);
+               set_gptimer_period(TIMER0_id, get_sclk() / HZ);
+               set_gptimer_pwidth(TIMER0_id, get_sclk() / HZ - 1);
+               enable_gptimers(TIMER0bit);
+               break;
+       }
+       case CLOCK_EVT_MODE_ONESHOT:
+               disable_gptimers(TIMER0bit);
+               set_gptimer_config(TIMER0_id, \
+                       TIMER_OUT_DIS | TIMER_IRQ_ENA | TIMER_MODE_PWM);
+               set_gptimer_period(TIMER0_id, 0);
+               break;
+       case CLOCK_EVT_MODE_UNUSED:
+       case CLOCK_EVT_MODE_SHUTDOWN:
+               disable_gptimers(TIMER0bit);
+               break;
+       case CLOCK_EVT_MODE_RESUME:
+               break;
+       }
+}
+
+static void bfin_timer_ack(void)
+{
+       set_gptimer_status(TIMER_GROUP1, TIMER_STATUS_TIMIL0);
+}
+
+static void __init bfin_timer_init(void)
+{
+       disable_gptimers(TIMER0bit);
+}
+
+static unsigned long  __init bfin_clockevent_check(void)
+{
+       setup_irq(IRQ_TIMER0, &bfin_timer_irq);
+       return get_sclk();
+}
+
+#else /* CONFIG_TICKSOURCE_CORETMR */
+
+static int bfin_timer_set_next_event(unsigned long cycles,
+                               struct clock_event_device *evt)
+{
+       bfin_write_TCNTL(TMPWR);
+       CSYNC();
        bfin_write_TCOUNT(cycles);
        CSYNC();
+       bfin_write_TCNTL(TMPWR | TMREN);
        return 0;
 }
 
 static void bfin_timer_set_mode(enum clock_event_mode mode,
-                                struct clock_event_device *evt)
+                               struct clock_event_device *evt)
 {
        switch (mode) {
        case CLOCK_EVT_MODE_PERIODIC: {
                unsigned long tcount = ((get_cclk() / (HZ * TIME_SCALE)) - 1);
                bfin_write_TCNTL(TMPWR);
-               bfin_write_TSCALE(TIME_SCALE - 1);
                CSYNC();
+               bfin_write_TSCALE(TIME_SCALE - 1);
                bfin_write_TPERIOD(tcount);
                bfin_write_TCOUNT(tcount);
-               bfin_write_TCNTL(TMPWR | TMREN | TAUTORLD);
                CSYNC();
+               bfin_write_TCNTL(TMPWR | TMREN | TAUTORLD);
                break;
        }
        case CLOCK_EVT_MODE_ONESHOT:
+               bfin_write_TCNTL(TMPWR);
+               CSYNC();
                bfin_write_TSCALE(TIME_SCALE - 1);
+               bfin_write_TPERIOD(0);
                bfin_write_TCOUNT(0);
-               bfin_write_TCNTL(TMPWR | TMREN);
-               CSYNC();
                break;
        case CLOCK_EVT_MODE_UNUSED:
        case CLOCK_EVT_MODE_SHUTDOWN:
@@ -132,6 +278,10 @@ static void bfin_timer_set_mode(enum clock_event_mode mode,
        }
 }
 
+static void bfin_timer_ack(void)
+{
+}
+
 static void __init bfin_timer_init(void)
 {
        /* power up the timer, but don't enable it just yet */
@@ -145,38 +295,32 @@ static void __init bfin_timer_init(void)
        bfin_write_TPERIOD(0);
        bfin_write_TCOUNT(0);
 
-       /* now enable the timer */
        CSYNC();
 }
 
+static unsigned long  __init bfin_clockevent_check(void)
+{
+       setup_irq(IRQ_CORETMR, &bfin_timer_irq);
+       return get_cclk() / TIME_SCALE;
+}
+
+void __init setup_core_timer(void)
+{
+       bfin_timer_init();
+       bfin_timer_set_mode(CLOCK_EVT_MODE_PERIODIC, NULL);
+}
+#endif /* CONFIG_TICKSOURCE_GPTMR0 */
+
 /*
  * timer_interrupt() needs to keep up the real-time clock,
  * as well as call the "do_timer()" routine every clocktick
  */
-#ifdef CONFIG_CORE_TIMER_IRQ_L1
-__attribute__((l1_text))
-#endif
-irqreturn_t timer_interrupt(int irq, void *dev_id);
-
-static struct clock_event_device clockevent_bfin = {
-       .name           = "bfin_core_timer",
-       .features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
-       .shift          = 32,
-       .set_next_event = bfin_timer_set_next_event,
-       .set_mode       = bfin_timer_set_mode,
-};
-
-static struct irqaction bfin_timer_irq = {
-       .name           = "Blackfin Core Timer",
-       .flags          = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
-       .handler        = timer_interrupt,
-       .dev_id         = &clockevent_bfin,
-};
-
 irqreturn_t timer_interrupt(int irq, void *dev_id)
 {
        struct clock_event_device *evt = dev_id;
+       smp_mb();
        evt->event_handler(evt);
+       bfin_timer_ack();
        return IRQ_HANDLED;
 }
 
@@ -184,9 +328,8 @@ static int __init bfin_clockevent_init(void)
 {
        unsigned long timer_clk;
 
-       timer_clk = get_cclk() / TIME_SCALE;
+       timer_clk = bfin_clockevent_check();
 
-       setup_irq(IRQ_CORETMR, &bfin_timer_irq);
        bfin_timer_init();
 
        clockevent_bfin.mult = div_sc(timer_clk, NSEC_PER_SEC, clockevent_bfin.shift);
@@ -218,6 +361,7 @@ void __init time_init(void)
        xtime.tv_nsec = 0;
        set_normalized_timespec(&wall_to_monotonic, -xtime.tv_sec, -xtime.tv_nsec);
 
-       bfin_clocksource_init();
+       bfin_cs_cycles_init();
+       bfin_cs_gptimer0_init();
        bfin_clockevent_init();
 }
index 1bbacfbd4c5d7c6198496fa939a7920dc36319d4..adb54aa7d7c81a19eeeeb25f094179224a073a78 100644 (file)
 
 static struct irqaction bfin_timer_irq = {
        .name = "Blackfin Timer Tick",
-#ifdef CONFIG_IRQ_PER_CPU
-       .flags = IRQF_DISABLED | IRQF_PERCPU,
-#else
        .flags = IRQF_DISABLED
-#endif
 };
 
-#if defined(CONFIG_TICK_SOURCE_SYSTMR0) || defined(CONFIG_IPIPE)
+#if defined(CONFIG_IPIPE)
 void __init setup_system_timer0(void)
 {
        /* Power down the core timer, just to play safe. */
@@ -74,7 +70,7 @@ void __init setup_core_timer(void)
 static void __init
 time_sched_init(irqreturn_t(*timer_routine) (int, void *))
 {
-#if defined(CONFIG_TICK_SOURCE_SYSTMR0) || defined(CONFIG_IPIPE)
+#if defined(CONFIG_IPIPE)
        setup_system_timer0();
        bfin_timer_irq.handler = timer_routine;
        setup_irq(IRQ_TIMER0, &bfin_timer_irq);
@@ -94,7 +90,7 @@ static unsigned long gettimeoffset(void)
        unsigned long offset;
        unsigned long clocks_per_jiffy;
 
-#if defined(CONFIG_TICK_SOURCE_SYSTMR0) || defined(CONFIG_IPIPE)
+#if defined(CONFIG_IPIPE)
        clocks_per_jiffy = bfin_read_TIMER0_PERIOD();
        offset = bfin_read_TIMER0_COUNTER() / \
                (((clocks_per_jiffy + 1) * HZ) / USEC_PER_SEC);
@@ -133,36 +129,25 @@ irqreturn_t timer_interrupt(int irq, void *dummy)
        static long last_rtc_update;
 
        write_seqlock(&xtime_lock);
-#if defined(CONFIG_TICK_SOURCE_SYSTMR0) && !defined(CONFIG_IPIPE)
+       do_timer(1);
+
        /*
-        * TIMIL0 is latched in __ipipe_grab_irq() when the I-Pipe is
-        * enabled.
+        * If we have an externally synchronized Linux clock, then update
+        * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
+        * called as close as possible to 500 ms before the new second starts.
         */
-       if (get_gptimer_status(0) & TIMER_STATUS_TIMIL0) {
-#endif
-               do_timer(1);
-
-               /*
-                * If we have an externally synchronized Linux clock, then update
-                * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
-                * called as close as possible to 500 ms before the new second starts.
-                */
-               if (ntp_synced() &&
-                   xtime.tv_sec > last_rtc_update + 660 &&
-                   (xtime.tv_nsec / NSEC_PER_USEC) >=
-                   500000 - ((unsigned)TICK_SIZE) / 2
-                   && (xtime.tv_nsec / NSEC_PER_USEC) <=
-                   500000 + ((unsigned)TICK_SIZE) / 2) {
-                       if (set_rtc_mmss(xtime.tv_sec) == 0)
-                               last_rtc_update = xtime.tv_sec;
-                       else
-                               /* Do it again in 60s. */
-                               last_rtc_update = xtime.tv_sec - 600;
-               }
-#if defined(CONFIG_TICK_SOURCE_SYSTMR0) && !defined(CONFIG_IPIPE)
-               set_gptimer_status(0, TIMER_STATUS_TIMIL0);
+       if (ntp_synced() &&
+           xtime.tv_sec > last_rtc_update + 660 &&
+           (xtime.tv_nsec / NSEC_PER_USEC) >=
+           500000 - ((unsigned)TICK_SIZE) / 2
+           && (xtime.tv_nsec / NSEC_PER_USEC) <=
+           500000 + ((unsigned)TICK_SIZE) / 2) {
+               if (set_rtc_mmss(xtime.tv_sec) == 0)
+                       last_rtc_update = xtime.tv_sec;
+               else
+                       /* Do it again in 60s. */
+                       last_rtc_update = xtime.tv_sec - 600;
        }
-#endif
        write_sequnlock(&xtime_lock);
 
 #ifdef CONFIG_IPIPE
index ffe7fb53eccbdaccd344fbe27029fdc5bba12b14..aa76dfb0226ecd6dcf03136c463eb2969ac78f4b 100644 (file)
        ({ if (0) printk(fmt, ##arg); 0; })
 #endif
 
+#if defined(CONFIG_DEBUG_MMRS) || defined(CONFIG_DEBUG_MMRS_MODULE)
+u32 last_seqstat;
+#ifdef CONFIG_DEBUG_MMRS_MODULE
+EXPORT_SYMBOL(last_seqstat);
+#endif
+#endif
+
 /* Initiate the event table handler */
 void __init trap_init(void)
 {
@@ -79,7 +86,6 @@ void __init trap_init(void)
 static void decode_address(char *buf, unsigned long address)
 {
 #ifdef CONFIG_DEBUG_VERBOSE
-       struct vm_list_struct *vml;
        struct task_struct *p;
        struct mm_struct *mm;
        unsigned long flags, offset;
@@ -196,6 +202,11 @@ done:
 
 asmlinkage void double_fault_c(struct pt_regs *fp)
 {
+#ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON
+       int j;
+       trace_buffer_save(j);
+#endif
+
        console_verbose();
        oops_in_progress = 1;
 #ifdef CONFIG_DEBUG_VERBOSE
@@ -220,9 +231,10 @@ asmlinkage void double_fault_c(struct pt_regs *fp)
                dump_bfin_process(fp);
                dump_bfin_mem(fp);
                show_regs(fp);
+               dump_bfin_trace_buffer();
        }
 #endif
-       panic("Double Fault - unrecoverable event\n");
+       panic("Double Fault - unrecoverable event");
 
 }
 
@@ -239,6 +251,9 @@ asmlinkage void trap_c(struct pt_regs *fp)
        unsigned long trapnr = fp->seqstat & SEQSTAT_EXCAUSE;
 
        trace_buffer_save(j);
+#if defined(CONFIG_DEBUG_MMRS) || defined(CONFIG_DEBUG_MMRS_MODULE)
+       last_seqstat = (u32)fp->seqstat;
+#endif
 
        /* Important - be very careful dereferncing pointers - will lead to
         * double faults if the stack has become corrupt
@@ -588,6 +603,9 @@ asmlinkage void trap_c(struct pt_regs *fp)
                force_sig_info(sig, &info, current);
        }
 
+       if (ANOMALY_05000461 && trapnr == VEC_HWERR && !access_ok(VERIFY_READ, fp->pc, 8))
+               fp->pc = SAFE_USER_INSTRUCTION;
+
        trace_buffer_restore(j);
        return;
 }
@@ -832,6 +850,11 @@ void show_stack(struct task_struct *task, unsigned long *stack)
        decode_address(buf, (unsigned int)stack);
        printk(KERN_NOTICE " SP: [0x%p] %s\n", stack, buf);
 
+       if (!access_ok(VERIFY_READ, stack, (unsigned int)endstack - (unsigned int)stack)) {
+               printk(KERN_NOTICE "Invalid stack pointer\n");
+               return;
+       }
+
        /* First thing is to look for a frame pointer */
        for (addr = (unsigned int *)((unsigned int)stack & ~0xF); addr < endstack; addr++) {
                if (*addr & 0x1)
@@ -1066,6 +1089,29 @@ void show_regs(struct pt_regs *fp)
        unsigned int cpu = smp_processor_id();
        unsigned char in_atomic = (bfin_read_IPEND() & 0x10) || in_atomic();
 
+       verbose_printk(KERN_NOTICE "\n");
+       if (CPUID != bfin_cpuid())
+               verbose_printk(KERN_NOTICE "Compiled for cpu family 0x%04x (Rev %d), "
+                       "but running on:0x%04x (Rev %d)\n",
+                       CPUID, bfin_compiled_revid(), bfin_cpuid(), bfin_revid());
+
+       verbose_printk(KERN_NOTICE "ADSP-%s-0.%d",
+               CPU, bfin_compiled_revid());
+
+       if (bfin_compiled_revid() !=  bfin_revid())
+               verbose_printk("(Detected 0.%d)", bfin_revid());
+
+       verbose_printk(" %lu(MHz CCLK) %lu(MHz SCLK) (%s)\n",
+               get_cclk()/1000000, get_sclk()/1000000,
+#ifdef CONFIG_MPU
+               "mpu on"
+#else
+               "mpu off"
+#endif
+               );
+
+       verbose_printk(KERN_NOTICE "%s", linux_banner);
+
        verbose_printk(KERN_NOTICE "\n" KERN_NOTICE "SEQUENCER STATUS:\t\t%s\n", print_tainted());
        verbose_printk(KERN_NOTICE " SEQSTAT: %08lx  IPEND: %04lx  SYSCFG: %04lx\n",
                (long)fp->seqstat, fp->ipend, fp->syscfg);
@@ -1246,5 +1292,5 @@ void panic_cplb_error(int cplb_panic, struct pt_regs *fp)
        dump_bfin_mem(fp);
        show_regs(fp);
        dump_stack();
-       panic("Unrecoverable event\n");
+       panic("Unrecoverable event");
 }
index 27952ae047d883ab80b564eebdbba1e4836e2439..8b67167cb4f4cdeac39c465c0be0a5ba4f52711e 100644 (file)
@@ -50,7 +50,9 @@ SECTIONS
                _text = .;
                __stext = .;
                TEXT_TEXT
+#ifndef CONFIG_SCHEDULE_L1
                SCHED_TEXT
+#endif
                LOCK_TEXT
                KPROBES_TEXT
                *(.text.*)
@@ -180,6 +182,9 @@ SECTIONS
                . = ALIGN(4);
                __stext_l1 = .;
                *(.l1.text)
+#ifdef CONFIG_SCHEDULE_L1
+               SCHED_TEXT
+#endif
                . = ALIGN(4);
                __etext_l1 = .;
        }
index f397ede006bf8a6ebc36721a780a6a2e8d4c3ade..4c76fefb7a3b68c02006358622bffe0a401d6761 100644 (file)
@@ -156,6 +156,7 @@ config IRQ_PORTH_INTB
        default 11
 config IRQ_TIMER0
        int "IRQ_TIMER0"
+       default 7 if TICKSOURCE_GPTMR0
        default 8
 config IRQ_TIMER1
        int "IRQ_TIMER1"
index 41f2eacfef207339d53d7dbf1de90b78e7d3a8f2..62bba09bcce689dfe908b5b702e76fb20877921f 100644 (file)
@@ -82,7 +82,11 @@ static struct physmap_flash_data ezbrd_flash_data = {
 
 static struct resource ezbrd_flash_resource = {
        .start = 0x20000000,
+#if defined(CONFIG_SPI_BFIN) || defined(CONFIG_SPI_BFIN_MODULE)
+       .end   = 0x202fffff,
+#else
        .end   = 0x203fffff,
+#endif
        .flags = IORESOURCE_MEM,
 };
 
@@ -162,8 +166,8 @@ static struct bfin5xx_spi_chip spi_flash_chip_info = {
 };
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) \
-       || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) \
+       || defined(CONFIG_BFIN_SPI_ADC_MODULE)
 /* SPI ADC chip */
 static struct bfin5xx_spi_chip spi_adc_chip_info = {
        .enable_dma = 1,         /* use dma transfer with this chip*/
@@ -249,8 +253,8 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
        },
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) \
-       || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) \
+       || defined(CONFIG_BFIN_SPI_ADC_MODULE)
        {
                .modalias = "bfin_spi_adc", /* Name of spi_driver for this device */
                .max_speed_hz = 6250000,     /* max spi clock (SCK) speed in HZ */
@@ -514,7 +518,7 @@ static struct platform_device i2c_bfin_twi_device = {
 #endif
 
 static struct i2c_board_info __initdata bfin_i2c_board_info[] = {
-#if defined(CONFIG_TWI_LCD) || defined(CONFIG_TWI_LCD_MODULE)
+#if defined(CONFIG_BFIN_TWI_LCD) || defined(CONFIG_TWI_LCD_MODULE)
        {
                I2C_BOARD_INFO("pcf8574_lcd", 0x22),
        },
@@ -678,6 +682,11 @@ static int __init ezbrd_init(void)
                                ARRAY_SIZE(bfin_i2c_board_info));
        platform_add_devices(stamp_devices, ARRAY_SIZE(stamp_devices));
        spi_register_board_info(bfin_spi_board_info, ARRAY_SIZE(bfin_spi_board_info));
+       /* setup BF518-EZBRD GPIO pin PG11 to AMS2, PG15 to AMS3. */
+       peripheral_request(P_AMS2, "ParaFlash");
+#if !defined(CONFIG_SPI_BFIN) && !defined(CONFIG_SPI_BFIN_MODULE)
+       peripheral_request(P_AMS3, "ParaFlash");
+#endif
        return 0;
 }
 
index c847bb101076399cbb3bc510adf5946e42ddecff..b69bd9af38ddad9a7b218387fedf34704addf5c4 100644 (file)
@@ -6,14 +6,19 @@
  * Licensed under the GPL-2 or later.
  */
 
-/* This file shoule be up to date with:
+/* This file should be up to date with:
  *  - Revision B, 02/03/2009; ADSP-BF512/BF514/BF516/BF518 Blackfin Processor Anomaly List
  */
 
+/* We plan on not supporting 0.0 silicon, but 0.1 isn't out yet - sorry */
+#if __SILICON_REVISION__ < 0
+# error will not work on BF518 silicon version
+#endif
+
 #ifndef _MACH_ANOMALY_H_
 #define _MACH_ANOMALY_H_
 
-/* Multi-Issue Instruction with dsp32shiftimm in slot1 and P-reg Store in slot2 Not Supported */
+/* Multi-issue instruction with dsp32shiftimm in slot1 and P-reg store in slot 2 not supported */
 #define ANOMALY_05000074 (1)
 /* Rx.H Cannot Be Used to Access 16-bit System MMR Registers */
 #define ANOMALY_05000122 (1)
@@ -47,7 +52,7 @@
 #define ANOMALY_05000435 (1)
 /* PORTx_DRIVE and PORTx_HYSTERESIS Registers Read Back Incorrect Values */
 #define ANOMALY_05000438 (1)
-/* Preboot Cannot be Used to Program the PLL_DIV Register */
+/* Preboot Cannot be Used to Alter the PLL_DIV Register */
 #define ANOMALY_05000439 (1)
 /* bfrom_SysControl() Cannot be Used to Write the PLL_DIV Register */
 #define ANOMALY_05000440 (1)
 #define ANOMALY_05000453 (1)
 /* PPI_FS3 is Driven One Half Cycle Later Than PPI Data */
 #define ANOMALY_05000455 (1)
+/* False Hardware Error when RETI points to invalid memory */
+#define ANOMALY_05000461 (1)
 
 /* Anomalies that don't exist on this proc */
+#define ANOMALY_05000099 (0)
+#define ANOMALY_05000119 (0)
+#define ANOMALY_05000120 (0)
 #define ANOMALY_05000125 (0)
+#define ANOMALY_05000149 (0)
 #define ANOMALY_05000158 (0)
+#define ANOMALY_05000171 (0)
+#define ANOMALY_05000179 (0)
 #define ANOMALY_05000183 (0)
 #define ANOMALY_05000198 (0)
+#define ANOMALY_05000215 (0)
+#define ANOMALY_05000220 (0)
+#define ANOMALY_05000227 (0)
 #define ANOMALY_05000230 (0)
+#define ANOMALY_05000231 (0)
+#define ANOMALY_05000233 (0)
+#define ANOMALY_05000242 (0)
 #define ANOMALY_05000244 (0)
+#define ANOMALY_05000248 (0)
+#define ANOMALY_05000250 (0)
 #define ANOMALY_05000261 (0)
 #define ANOMALY_05000263 (0)
 #define ANOMALY_05000266 (0)
 #define ANOMALY_05000273 (0)
+#define ANOMALY_05000274 (0)
 #define ANOMALY_05000278 (0)
 #define ANOMALY_05000285 (0)
+#define ANOMALY_05000287 (0)
+#define ANOMALY_05000301 (0)
 #define ANOMALY_05000305 (0)
 #define ANOMALY_05000307 (0)
 #define ANOMALY_05000311 (0)
 #define ANOMALY_05000312 (0)
 #define ANOMALY_05000323 (0)
 #define ANOMALY_05000353 (0)
+#define ANOMALY_05000362 (1)
 #define ANOMALY_05000363 (0)
 #define ANOMALY_05000380 (0)
 #define ANOMALY_05000386 (0)
+#define ANOMALY_05000389 (0)
+#define ANOMALY_05000400 (0)
 #define ANOMALY_05000412 (0)
 #define ANOMALY_05000432 (0)
 #define ANOMALY_05000447 (0)
 #define ANOMALY_05000448 (0)
+#define ANOMALY_05000456 (0)
+#define ANOMALY_05000450 (0)
 
 #endif
index f618b487b2b0eabd96d03e1be423b282122770ca..a0fc77fd331518f48039f76dde51370581883bba 100644 (file)
 #define P_PTP_PPS              (P_DEFINED | P_IDENT(GPIO_PG12) | P_FUNCT(2))
 #define P_PTP_CLKOUT           (P_DEFINED | P_IDENT(GPIO_PG13) | P_FUNCT(2))
 
-#define P_HWAIT                (P_DEFINED | P_IDENT(GPIO_PG000000000) | P_FUNCT(1))
+/* AMS */
+#define P_AMS2                 (P_DEFINED | P_IDENT(GPIO_PG11) | P_FUNCT(1))
+#define P_AMS3                 (P_DEFINED | P_IDENT(GPIO_PG15) | P_FUNCT(2))
+
+#define P_HWAIT                        (P_DEFINED | P_IDENT(GPIO_PG000000000) | P_FUNCT(1))
 
 #endif                         /* _MACH_PORTMUX_H_ */
index 8438ec6d6679007fe115f8dd9fc4f477d9befeda..848ac6f868231d42044209a71c48a46c806d96e0 100644 (file)
@@ -170,6 +170,7 @@ config IRQ_PORTH_INTB
        default 11
 config IRQ_TIMER0
        int "IRQ_TIMER0"
+       default 7 if TICKSOURCE_GPTMR0
        default 8
 config IRQ_TIMER1
        int "IRQ_TIMER1"
index 48e69eecdba42b4e690b146b71866f1127c6d6e8..6d6f9effa0bb8e36f38f99ba9f7a8cfdb6798b16 100644 (file)
@@ -463,8 +463,8 @@ static struct bfin5xx_spi_chip spi_flash_chip_info = {
 };
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) \
-       || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) \
+       || defined(CONFIG_BFIN_SPI_ADC_MODULE)
 /* SPI ADC chip */
 static struct bfin5xx_spi_chip spi_adc_chip_info = {
        .enable_dma = 1,         /* use dma transfer with this chip*/
@@ -554,8 +554,8 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
        },
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) \
-       || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) \
+       || defined(CONFIG_BFIN_SPI_ADC_MODULE)
        {
                .modalias = "bfin_spi_adc", /* Name of spi_driver for this device */
                .max_speed_hz = 6250000,     /* max spi clock (SCK) speed in HZ */
@@ -789,7 +789,7 @@ static struct platform_device i2c_bfin_twi_device = {
 #endif
 
 static struct i2c_board_info __initdata bfin_i2c_board_info[] = {
-#if defined(CONFIG_TWI_LCD) || defined(CONFIG_TWI_LCD_MODULE)
+#if defined(CONFIG_BFIN_TWI_LCD) || defined(CONFIG_TWI_LCD_MODULE)
        {
                I2C_BOARD_INFO("pcf8574_lcd", 0x22),
                .type = "pcf8574_lcd",
index 7fe480e4ebe83c1022e275373cc1d77550091c20..1435c5d38cd515127d5f6b16f60fc084f8c96287 100644 (file)
@@ -247,8 +247,8 @@ static struct bfin5xx_spi_chip spi_flash_chip_info = {
 };
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) \
-       || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) \
+       || defined(CONFIG_BFIN_SPI_ADC_MODULE)
 /* SPI ADC chip */
 static struct bfin5xx_spi_chip spi_adc_chip_info = {
        .enable_dma = 1,         /* use dma transfer with this chip*/
@@ -354,8 +354,8 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
        },
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) \
-       || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) \
+       || defined(CONFIG_BFIN_SPI_ADC_MODULE)
        {
                .modalias = "bfin_spi_adc", /* Name of spi_driver for this device */
                .max_speed_hz = 6250000,     /* max spi clock (SCK) speed in HZ */
@@ -586,7 +586,7 @@ static struct platform_device i2c_bfin_twi_device = {
 #endif
 
 static struct i2c_board_info __initdata bfin_i2c_board_info[] = {
-#if defined(CONFIG_TWI_LCD) || defined(CONFIG_TWI_LCD_MODULE)
+#if defined(CONFIG_BFIN_TWI_LCD) || defined(CONFIG_TWI_LCD_MODULE)
        {
                I2C_BOARD_INFO("pcf8574_lcd", 0x22),
        },
index d0864111ef594f3b05ed613c31d18251c87961c3..147edd1eb1ad44f52850a8d9977a36dbbfd5aab0 100644 (file)
@@ -485,8 +485,8 @@ static struct bfin5xx_spi_chip spi_flash_chip_info = {
 };
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) \
-       || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) \
+       || defined(CONFIG_BFIN_SPI_ADC_MODULE)
 /* SPI ADC chip */
 static struct bfin5xx_spi_chip spi_adc_chip_info = {
        .enable_dma = 1,         /* use dma transfer with this chip*/
@@ -509,6 +509,13 @@ static struct bfin5xx_spi_chip ad9960_spi_chip_info = {
 };
 #endif
 
+#if defined(CONFIG_MMC_SPI) || defined(CONFIG_MMC_SPI_MODULE)
+static struct bfin5xx_spi_chip  mmc_spi_chip_info = {
+       .enable_dma = 0,
+       .bits_per_word = 8,
+};
+#endif
+
 #if defined(CONFIG_PBX)
 static struct bfin5xx_spi_chip spi_si3xxx_chip_info = {
        .ctl_reg        = 0x4, /* send zero */
@@ -593,8 +600,8 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
        },
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) \
-       || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) \
+       || defined(CONFIG_BFIN_SPI_ADC_MODULE)
        {
                .modalias = "bfin_spi_adc", /* Name of spi_driver for this device */
                .max_speed_hz = 6250000,     /* max spi clock (SCK) speed in HZ */
@@ -624,6 +631,17 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
                .controller_data = &ad9960_spi_chip_info,
        },
 #endif
+#if defined(CONFIG_MMC_SPI) || defined(CONFIG_MMC_SPI_MODULE)
+       {
+               .modalias = "mmc_spi",
+               .max_speed_hz = 20000000,     /* max spi clock (SCK) speed in HZ */
+               .bus_num = 0,
+               .chip_select = 3,
+               .controller_data = &mmc_spi_chip_info,
+               .mode = SPI_MODE_0,
+       },
+#endif
+
 #if defined(CONFIG_PBX)
        {
                .modalias = "fxs-spi",
@@ -836,7 +854,7 @@ static struct platform_device i2c_bfin_twi_device = {
 #endif
 
 static struct i2c_board_info __initdata bfin_i2c_board_info[] = {
-#if defined(CONFIG_TWI_LCD) || defined(CONFIG_TWI_LCD_MODULE)
+#if defined(CONFIG_BFIN_TWI_LCD) || defined(CONFIG_TWI_LCD_MODULE)
        {
                I2C_BOARD_INFO("pcf8574_lcd", 0x22),
        },
index df6808d8a6efce3f5317cfc849c9bbb969e7dcda..c84ddea95749c7c0b2a1be099cd22cb62f5b30cf 100644 (file)
@@ -6,14 +6,19 @@
  * Licensed under the GPL-2 or later.
  */
 
-/* This file shoule be up to date with:
- *  - Revision B, 08/12/2008; ADSP-BF526 Blackfin Processor Anomaly List
- *  - Revision E, 08/18/2008; ADSP-BF527 Blackfin Processor Anomaly List
+/* This file should be up to date with:
+ *  - Revision C, 03/13/2009; ADSP-BF526 Blackfin Processor Anomaly List
+ *  - Revision F, 03/03/2009; ADSP-BF527 Blackfin Processor Anomaly List
  */
 
 #ifndef _MACH_ANOMALY_H_
 #define _MACH_ANOMALY_H_
 
+/* We do not support old silicon - sorry */
+#if __SILICON_REVISION__ < 0
+# error will not work on BF526/BF527 silicon version
+#endif
+
 #if defined(__ADSPBF522__) || defined(__ADSPBF524__) || defined(__ADSPBF526__)
 # define ANOMALY_BF526 1
 #else
 # define ANOMALY_BF527 0
 #endif
 
-/* Multi-Issue Instruction with dsp32shiftimm in slot1 and P-reg Store in slot2 Not Supported */
+#define _ANOMALY_BF526(rev526) (ANOMALY_BF526 && __SILICON_REVISION__ rev526)
+#define _ANOMALY_BF527(rev527) (ANOMALY_BF527 && __SILICON_REVISION__ rev527)
+#define _ANOMALY_BF526_BF527(rev526, rev527) (_ANOMALY_BF526(rev526) || _ANOMALY_BF527(rev527))
+
+/* Multi-issue instruction with dsp32shiftimm in slot1 and P-reg store in slot 2 not supported */
 #define ANOMALY_05000074 (1)
 /* DMA_RUN Bit Is Not Valid after a Peripheral Receive Channel DMA Stops */
 #define ANOMALY_05000119 (1)   /* note: brokenness is noted in documentation, not anomaly sheet */
 /* Rx.H Cannot Be Used to Access 16-bit System MMR Registers */
 #define ANOMALY_05000122 (1)
-/* Spurious Hardware Error from an Access in the Shadow of a Conditional Branch */
+/* False Hardware Error from an Access in the Shadow of a Conditional Branch */
 #define ANOMALY_05000245 (1)
+/* Incorrect Timer Pulse Width in Single-Shot PWM_OUT Mode with External Clock */
+#define ANOMALY_05000254 (1)
 /* Sensitivity To Noise with Slow Input Edge Rates on External SPORT TX and RX Clocks */
 #define ANOMALY_05000265 (1)
 /* False Hardware Errors Caused by Fetches at the Boundary of Reserved Memory */
 #define ANOMALY_05000310 (1)
 /* PPI Is Level-Sensitive on First Transfer In Single Frame Sync Modes */
-#define ANOMALY_05000313 (__SILICON_REVISION__ < 2)
+#define ANOMALY_05000313 (_ANOMALY_BF526_BF527(< 1, < 2))
 /* Incorrect Access of OTP_STATUS During otp_write() Function */
-#define ANOMALY_05000328 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000328 (_ANOMALY_BF527(< 2))
+/* Host DMA Boot Modes Are Not Functional */
+#define ANOMALY_05000330 (__SILICON_REVISION__ < 2)
 /* Disallowed Configuration Prevents Subsequent Allowed Configuration on Host DMA Port */
-#define ANOMALY_05000337 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000337 (_ANOMALY_BF527(< 2))
 /* Ethernet MAC MDIO Reads Do Not Meet IEEE Specification */
-#define ANOMALY_05000341 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000341 (_ANOMALY_BF527(< 2))
 /* TWI May Not Operate Correctly Under Certain Signal Termination Conditions */
-#define ANOMALY_05000342 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000342 (_ANOMALY_BF527(< 2))
 /* USB Calibration Value Is Not Initialized */
-#define ANOMALY_05000346 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000346 (_ANOMALY_BF526_BF527(< 1, < 2))
 /* USB Calibration Value to use */
 #define ANOMALY_05000346_value 0xE510
 /* Preboot Routine Incorrectly Alters Reset Value of USB Register */
-#define ANOMALY_05000347 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000347 (_ANOMALY_BF527(< 2))
 /* Security Features Are Not Functional */
-#define ANOMALY_05000348 (ANOMALY_BF527 && __SILICON_REVISION__ < 1)
+#define ANOMALY_05000348 (_ANOMALY_BF527(< 1))
 /* bfrom_SysControl() Firmware Function Performs Improper System Reset */
-#define ANOMALY_05000353 (ANOMALY_BF526)
+#define ANOMALY_05000353 (_ANOMALY_BF526(< 1))
 /* Regulator Programming Blocked when Hibernate Wakeup Source Remains Active */
-#define ANOMALY_05000355 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000355 (_ANOMALY_BF527(< 2))
 /* Serial Port (SPORT) Multichannel Transmit Failure when Channel 0 Is Disabled */
-#define ANOMALY_05000357 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000357 (_ANOMALY_BF527(< 2))
 /* Incorrect Revision Number in DSPID Register */
-#define ANOMALY_05000364 (ANOMALY_BF527 && __SILICON_REVISION__ == 1)
+#define ANOMALY_05000364 (_ANOMALY_BF527(== 1))
 /* PPI Underflow Error Goes Undetected in ITU-R 656 Mode */
 #define ANOMALY_05000366 (1)
 /* Incorrect Default CSEL Value in PLL_DIV */
-#define ANOMALY_05000368 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000368 (_ANOMALY_BF527(< 2))
 /* Possible RETS Register Corruption when Subroutine Is under 5 Cycles in Duration */
-#define ANOMALY_05000371 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000371 (_ANOMALY_BF527(< 2))
 /* Authentication Fails To Initiate */
-#define ANOMALY_05000376 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000376 (_ANOMALY_BF527(< 2))
 /* Data Read From L3 Memory by USB DMA May be Corrupted */
-#define ANOMALY_05000380 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000380 (_ANOMALY_BF527(< 2))
 /* 8-Bit NAND Flash Boot Mode Not Functional */
-#define ANOMALY_05000382 (__SILICON_REVISION__ < 2)
-/* Host Must Not Read Back During Host DMA Boot */
-#define ANOMALY_05000384 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000382 (_ANOMALY_BF526_BF527(< 1, < 2))
 /* Boot from OTP Memory Not Functional */
-#define ANOMALY_05000385 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000385 (_ANOMALY_BF527(< 2))
 /* bfrom_SysControl() Firmware Routine Not Functional */
-#define ANOMALY_05000386 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000386 (_ANOMALY_BF527(< 2))
 /* Programmable Preboot Settings Not Functional */
-#define ANOMALY_05000387 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000387 (_ANOMALY_BF527(< 2))
 /* CRC32 Checksum Support Not Functional */
-#define ANOMALY_05000388 (__SILICON_REVISION__ < 2)
+#define ANOMALY_05000388 (_ANOMALY_BF526_BF527(< 1, < 2))
 /* Reset Vector Must Not Be in SDRAM Memory Space */
-#define ANOMALY_05000389 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000389 (_ANOMALY_BF527(< 2))
 /* pTempCurrent Not Present in ADI_BOOT_DATA Structure */
-#define ANOMALY_05000392 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000392 (_ANOMALY_BF527(< 2))
 /* Deprecated Value of dTempByteCount in ADI_BOOT_DATA Structure */
-#define ANOMALY_05000393 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000393 (_ANOMALY_BF527(< 2))
 /* Log Buffer Not Functional */
-#define ANOMALY_05000394 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000394 (_ANOMALY_BF527(< 2))
 /* Hook Routine Not Functional */
-#define ANOMALY_05000395 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000395 (_ANOMALY_BF527(< 2))
 /* Header Indirect Bit Not Functional */
-#define ANOMALY_05000396 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000396 (_ANOMALY_BF527(< 2))
 /* BK_ONES, BK_ZEROS, and BK_DATECODE Constants Not Functional */
-#define ANOMALY_05000397 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000397 (_ANOMALY_BF527(< 2))
 /* SWRESET, DFRESET and WDRESET Bits in the SYSCR Register Not Functional */
-#define ANOMALY_05000398 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000398 (_ANOMALY_BF527(< 2))
 /* BCODE_NOBOOT in BCODE Field of SYSCR Register Not Functional */
-#define ANOMALY_05000399 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000399 (_ANOMALY_BF527(< 2))
 /* PPI Data Signals D0 and D8 do not Tristate After Disabling PPI */
-#define ANOMALY_05000401 (__SILICON_REVISION__ < 2)
+#define ANOMALY_05000401 (_ANOMALY_BF526_BF527(< 1, < 2))
 /* Level-Sensitive External GPIO Wakeups May Cause Indefinite Stall */
-#define ANOMALY_05000403 (__SILICON_REVISION__ < 2)
+#define ANOMALY_05000403 (_ANOMALY_BF526_BF527(< 1, < 2))
 /* Lockbox SESR Disallows Certain User Interrupts */
-#define ANOMALY_05000404 (__SILICON_REVISION__ < 2)
+#define ANOMALY_05000404 (_ANOMALY_BF526_BF527(< 1, < 2))
 /* Lockbox SESR Firmware Does Not Save/Restore Full Context */
 #define ANOMALY_05000405 (1)
 /* Lockbox SESR Firmware Arguments Are Not Retained After First Initialization */
-#define ANOMALY_05000407 (__SILICON_REVISION__ < 2)
+#define ANOMALY_05000407 (_ANOMALY_BF526_BF527(< 1, < 2))
 /* Lockbox Firmware Memory Cleanup Routine Does not Clear Registers */
 #define ANOMALY_05000408 (1)
 /* Lockbox firmware leaves MDMA0 channel enabled */
-#define ANOMALY_05000409 (__SILICON_REVISION__ < 2)
+#define ANOMALY_05000409 (_ANOMALY_BF526_BF527(< 1, < 2))
 /* Incorrect Default Internal Voltage Regulator Setting */
-#define ANOMALY_05000410 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000410 (_ANOMALY_BF527(< 2))
 /* bfrom_SysControl() Firmware Function Cannot be Used to Enter Power Saving Modes */
-#define ANOMALY_05000411 (__SILICON_REVISION__ < 2)
+#define ANOMALY_05000411 (_ANOMALY_BF526_BF527(< 1, < 2))
 /* OTP_CHECK_FOR_PREV_WRITE Bit is Not Functional in bfrom_OtpWrite() API */
-#define ANOMALY_05000414 (__SILICON_REVISION__ < 2)
+#define ANOMALY_05000414 (_ANOMALY_BF526_BF527(< 1, < 2))
 /* DEB2_URGENT Bit Not Functional */
-#define ANOMALY_05000415 (__SILICON_REVISION__ < 2)
+#define ANOMALY_05000415 (_ANOMALY_BF526_BF527(< 1, < 2))
 /* Speculative Fetches Can Cause Undesired External FIFO Operations */
 #define ANOMALY_05000416 (1)
 /* SPORT0 Ignores External TSCLK0 on PG14 When TMR6 is an Output */
-#define ANOMALY_05000417 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
-/* tSFSPE and tHFSPE Do Not Meet Data Sheet Specifications */
-#define ANOMALY_05000418 (__SILICON_REVISION__ < 2)
+#define ANOMALY_05000417 (_ANOMALY_BF527(< 2))
+/* PPI Timing Requirements tSFSPE and tHFSPE Do Not Meet Data Sheet Specifications */
+#define ANOMALY_05000418 (_ANOMALY_BF526_BF527(< 1, < 2))
 /* USB PLL_STABLE Bit May Not Accurately Reflect the USB PLL's Status */
-#define ANOMALY_05000420 (__SILICON_REVISION__ < 2)
+#define ANOMALY_05000420 (_ANOMALY_BF526_BF527(< 1, < 2))
 /* TWI Fall Time (Tof) May Violate the Minimum I2C Specification */
 #define ANOMALY_05000421 (1)
 /* TWI Input Capacitance (Ci) May Violate the Maximum I2C Specification */
-#define ANOMALY_05000422 (ANOMALY_BF527 && __SILICON_REVISION__ > 1)
+#define ANOMALY_05000422 (_ANOMALY_BF526_BF527(> 0, > 1))
 /* Certain Ethernet Frames With Errors are Misclassified in RMII Mode */
-#define ANOMALY_05000423 (__SILICON_REVISION__ < 2)
+#define ANOMALY_05000423 (_ANOMALY_BF526_BF527(< 1, < 2))
 /* Internal Voltage Regulator Not Trimmed */
-#define ANOMALY_05000424 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+#define ANOMALY_05000424 (_ANOMALY_BF527(< 2))
 /* Multichannel SPORT Channel Misalignment Under Specific Configuration */
-#define ANOMALY_05000425 (__SILICON_REVISION__ < 2)
-/* Speculative Fetches of Indirect-Pointer Instructions Can Cause Spurious Hardware Errors */
+#define ANOMALY_05000425 (_ANOMALY_BF526_BF527(< 1, < 2))
+/* Speculative Fetches of Indirect-Pointer Instructions Can Cause False Hardware Errors */
 #define ANOMALY_05000426 (1)
 /* WB_EDGE Bit in NFC_IRQSTAT Incorrectly Reflects Buffer Status Instead of IRQ Status */
-#define ANOMALY_05000429 (__SILICON_REVISION__ < 2)
+#define ANOMALY_05000429 (_ANOMALY_BF526_BF527(< 1, < 2))
 /* Software System Reset Corrupts PLL_LOCKCNT Register */
-#define ANOMALY_05000430 (ANOMALY_BF527 && __SILICON_REVISION__ > 1)
+#define ANOMALY_05000430 (_ANOMALY_BF527(> 1))
+/* Incorrect Use of Stack in Lockbox Firmware During Authentication */
+#define ANOMALY_05000431 (1)
 /* bfrom_SysControl() Does Not Clear SIC_IWR1 Before Executing PLL Programming Sequence */
-#define ANOMALY_05000432 (ANOMALY_BF526)
+#define ANOMALY_05000432 (_ANOMALY_BF526(< 1))
 /* Certain SIC Registers are not Reset After Soft or Core Double Fault Reset */
-#define ANOMALY_05000435 ((ANOMALY_BF526 && __SILICON_REVISION__ < 1) || ANOMALY_BF527)
+#define ANOMALY_05000435 (_ANOMALY_BF526_BF527(< 1, >= 0))
+/* Preboot Cannot be Used to Alter the PLL_DIV Register */
+#define ANOMALY_05000439 (_ANOMALY_BF526_BF527(< 1, >= 0))
+/* bfrom_SysControl() Cannot be Used to Write the PLL_DIV Register */
+#define ANOMALY_05000440 (_ANOMALY_BF526_BF527(< 1, >= 0))
+/* OTP Write Accesses Not Supported */
+#define ANOMALY_05000442 (_ANOMALY_BF527(< 1))
 /* IFLUSH Instruction at End of Hardware Loop Causes Infinite Stall */
 #define ANOMALY_05000443 (1)
+/* The WURESET Bit in the SYSCR Register is not Functional */
+#define ANOMALY_05000445 (1)
+/* BCODE_QUICKBOOT, BCODE_ALLBOOT, and BCODE_FULLBOOT Settings in SYSCR Register Not Functional */
+#define ANOMALY_05000451 (1)
+/* Incorrect Default Hysteresis Setting for RESET, NMI, and BMODE Signals */
+#define ANOMALY_05000452 (_ANOMALY_BF526_BF527(< 1, >= 0))
+/* USB Receive Interrupt Is Not Generated in DMA Mode 1 */
+#define ANOMALY_05000456 (1)
+/* Host DMA Port Responds to Certain Bus Activity Without HOST_CE Assertion */
+#define ANOMALY_05000457 (1)
+/* False Hardware Error when RETI points to invalid memory */
+#define ANOMALY_05000461 (1)
 
 /* Anomalies that don't exist on this proc */
+#define ANOMALY_05000099 (0)
+#define ANOMALY_05000120 (0)
 #define ANOMALY_05000125 (0)
+#define ANOMALY_05000149 (0)
 #define ANOMALY_05000158 (0)
+#define ANOMALY_05000171 (0)
+#define ANOMALY_05000179 (0)
 #define ANOMALY_05000183 (0)
 #define ANOMALY_05000198 (0)
+#define ANOMALY_05000215 (0)
+#define ANOMALY_05000220 (0)
+#define ANOMALY_05000227 (0)
 #define ANOMALY_05000230 (0)
+#define ANOMALY_05000231 (0)
+#define ANOMALY_05000233 (0)
+#define ANOMALY_05000242 (0)
 #define ANOMALY_05000244 (0)
+#define ANOMALY_05000248 (0)
+#define ANOMALY_05000250 (0)
 #define ANOMALY_05000261 (0)
 #define ANOMALY_05000263 (0)
 #define ANOMALY_05000266 (0)
 #define ANOMALY_05000273 (0)
+#define ANOMALY_05000274 (0)
 #define ANOMALY_05000278 (0)
 #define ANOMALY_05000285 (0)
+#define ANOMALY_05000287 (0)
+#define ANOMALY_05000301 (0)
 #define ANOMALY_05000305 (0)
 #define ANOMALY_05000307 (0)
 #define ANOMALY_05000311 (0)
 #define ANOMALY_05000312 (0)
 #define ANOMALY_05000323 (0)
+#define ANOMALY_05000362 (1)
 #define ANOMALY_05000363 (0)
+#define ANOMALY_05000400 (0)
 #define ANOMALY_05000412 (0)
 #define ANOMALY_05000447 (0)
 #define ANOMALY_05000448 (0)
+#define ANOMALY_05000450 (0)
 
 #endif
index 14427de7d77f6ebf571ca2e9bf3f423778311138..4c572443147e62d0f32b81ca0cc9d1297ccb2625 100644 (file)
@@ -59,6 +59,7 @@ config DMA7_UARTTX
        default 10
 config TIMER0
        int "TIMER0"
+       default 7 if TICKSOURCE_GPTMR0
        default 8
 config TIMER1
        int "TIMER1"
index 0c66bf44cfab81629e22c4d619d294dd87ded2d1..895f213ea454d11038c536a5c0e9645093e6ea51 100644 (file)
@@ -173,7 +173,7 @@ static struct bfin5xx_spi_chip spi_flash_chip_info = {
 };
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) || defined(CONFIG_BFIN_SPI_ADC_MODULE)
 /* SPI ADC chip */
 static struct bfin5xx_spi_chip spi_adc_chip_info = {
        .ctl_reg = 0x1000,
@@ -216,7 +216,7 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
        },
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) || defined(CONFIG_BFIN_SPI_ADC_MODULE)
        {
                .modalias = "bfin_spi_adc", /* Name of spi_driver for this device */
                .max_speed_hz = 4,     /* actual baudrate is SCLK/(2xspeed_hz) */
index e8974878d8c2807dc34bb2baa4b5822959558a5c..a727e538fa2857e128baa6d9b61a73f0c06b8eb0 100644 (file)
@@ -82,7 +82,7 @@ static struct bfin5xx_spi_chip spi_flash_chip_info = {
 #endif
 
 /* SPI ADC chip */
-#if defined(CONFIG_SPI_ADC_BF533) || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) || defined(CONFIG_BFIN_SPI_ADC_MODULE)
 static struct bfin5xx_spi_chip spi_adc_chip_info = {
        .enable_dma = 1,         /* use dma transfer with this chip*/
        .bits_per_word = 16,
@@ -117,7 +117,7 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
        },
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) || defined(CONFIG_BFIN_SPI_ADC_MODULE)
        {
                .modalias = "bfin_spi_adc", /* Name of spi_driver for this device */
                .max_speed_hz = 6250000,     /* max spi clock (SCK) speed in HZ */
index 08cd0969de475a98b6b82670b80825fab8e0a891..842f1c9c239336e684bc2f7fd7ad0d733a27ffb7 100644 (file)
@@ -118,7 +118,7 @@ static struct bfin5xx_spi_chip spi_flash_chip_info = {
 };
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) || defined(CONFIG_BFIN_SPI_ADC_MODULE)
 /* SPI ADC chip */
 static struct bfin5xx_spi_chip spi_adc_chip_info = {
        .enable_dma = 1,         /* use dma transfer with this chip*/
@@ -154,7 +154,7 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
        },
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) || defined(CONFIG_BFIN_SPI_ADC_MODULE)
        {
                .modalias = "bfin_spi_adc", /* Name of spi_driver for this device */
                .max_speed_hz = 6250000,     /* max spi clock (SCK) speed in HZ */
index db96f33f72e238ee5d4c64178232859a692645cf..e19c565ade160e4639c4f17bddf4c4e13c17c225 100644 (file)
@@ -192,7 +192,7 @@ static struct bfin5xx_spi_chip spi_flash_chip_info = {
 };
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) || defined(CONFIG_BFIN_SPI_ADC_MODULE)
 /* SPI ADC chip */
 static struct bfin5xx_spi_chip spi_adc_chip_info = {
        .enable_dma = 1,         /* use dma transfer with this chip*/
@@ -237,7 +237,7 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
        },
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) || defined(CONFIG_BFIN_SPI_ADC_MODULE)
        {
                .modalias = "bfin_spi_adc", /* Name of spi_driver for this device */
                .max_speed_hz = 6250000,     /* max spi clock (SCK) speed in HZ */
@@ -448,7 +448,7 @@ static struct i2c_board_info __initdata bfin_i2c_board_info[] = {
                .irq = 39,
        },
 #endif
-#if defined(CONFIG_TWI_LCD) || defined(CONFIG_TWI_LCD_MODULE)
+#if defined(CONFIG_BFIN_TWI_LCD) || defined(CONFIG_TWI_LCD_MODULE)
        {
                I2C_BOARD_INFO("pcf8574_lcd", 0x22),
        },
index 1cf893e2e55baffa27cf4aa8aa2d2757505f76f1..31145b509e20c1ae2054a1006c9f4a9165a958da 100644 (file)
@@ -6,7 +6,7 @@
  * Licensed under the GPL-2 or later.
  */
 
-/* This file shoule be up to date with:
+/* This file should be up to date with:
  *  - Revision E, 09/18/2008; ADSP-BF531/BF532/BF533 Blackfin Processor Anomaly List
  */
 
 # define ANOMALY_BF533 0
 #endif
 
-/* Multi-Issue Instruction with dsp32shiftimm in slot1 and P-reg Store in slot 2 Not Supported */
+/* Multi-issue instruction with dsp32shiftimm in slot1 and P-reg store in slot 2 not supported */
 #define ANOMALY_05000074 (1)
 /* UART Line Status Register (UART_LSR) Bits Are Not Updated at the Same Time */
 #define ANOMALY_05000099 (__SILICON_REVISION__ < 5)
 /* Watchpoint Status Register (WPSTAT) Bits Are Set on Every Corresponding Match */
-#define ANOMALY_05000105 (1)
+#define ANOMALY_05000105 (__SILICON_REVISION__ > 2)
 /* DMA_RUN Bit Is Not Valid after a Peripheral Receive Channel DMA Stops */
 #define ANOMALY_05000119 (1)
 /* Rx.H Cannot Be Used to Access 16-bit System MMR Registers */
@@ -48,7 +48,7 @@
 #define ANOMALY_05000158 (__SILICON_REVISION__ < 5)
 /* PPI Data Lengths Between 8 and 16 Do Not Zero Out Upper Bits */
 #define ANOMALY_05000166 (1)
-/* Turning Serial Ports on with External Frame Syncs */
+/* Turning SPORTs on while External Frame Sync Is Active May Corrupt Data */
 #define ANOMALY_05000167 (1)
 /* PPI_COUNT Cannot Be Programmed to 0 in General Purpose TX or RX Modes */
 #define ANOMALY_05000179 (__SILICON_REVISION__ < 5)
@@ -67,9 +67,9 @@
 /* Current DMA Address Shows Wrong Value During Carry Fix */
 #define ANOMALY_05000199 (__SILICON_REVISION__ < 4)
 /* SPORT TFS and DT Are Incorrectly Driven During Inactive Channels in Certain Conditions */
-#define ANOMALY_05000200 (__SILICON_REVISION__ < 5)
+#define ANOMALY_05000200 (__SILICON_REVISION__ == 3 || __SILICON_REVISION__ == 4)
 /* Receive Frame Sync Not Ignored During Active Frames in SPORT Multi-Channel Mode */
-#define ANOMALY_05000201 (__SILICON_REVISION__ < 4)
+#define ANOMALY_05000201 (__SILICON_REVISION__ == 3)
 /* Possible Infinite Stall with Specific Dual-DAG Situation */
 #define ANOMALY_05000202 (__SILICON_REVISION__ < 5)
 /* Specific Sequence That Can Cause DMA Error or DMA Stopping */
 #define ANOMALY_05000242 (__SILICON_REVISION__ < 5)
 /* If I-Cache Is On, CSYNC/SSYNC/IDLE Around Change of Control Causes Failures */
 #define ANOMALY_05000244 (__SILICON_REVISION__ < 5)
-/* Spurious Hardware Error from an Access in the Shadow of a Conditional Branch */
+/* False Hardware Error from an Access in the Shadow of a Conditional Branch */
 #define ANOMALY_05000245 (1)
 /* Data CPLBs Should Prevent Spurious Hardware Errors */
 #define ANOMALY_05000246 (__SILICON_REVISION__ < 5)
 /* High I/O Activity Causes Output Voltage of Internal Voltage Regulator (Vddint) to Decrease */
 #define ANOMALY_05000270 (__SILICON_REVISION__ < 5)
 /* Spontaneous Reset of Internal Voltage Regulator */
-#define ANOMALY_05000271 (__SILICON_REVISION__ < 4)
+#define ANOMALY_05000271 (__SILICON_REVISION__ == 3)
 /* Certain Data Cache Writethrough Modes Fail for Vddint <= 0.9V */
 #define ANOMALY_05000272 (1)
 /* Writes to Synchronous SDRAM Memory May Be Lost */
 /* New Feature: Additional PPI Frame Sync Sampling Options (Not Available On Older Silicon) */
 #define ANOMALY_05000306 (__SILICON_REVISION__ < 5)
 /* SCKELOW Bit Does Not Maintain State Through Hibernate */
-#define ANOMALY_05000307 (1)
+#define ANOMALY_05000307 (1)   /* note: brokenness is noted in documentation, not anomaly sheet */
 /* False Hardware Errors Caused by Fetches at the Boundary of Reserved Memory */
 #define ANOMALY_05000310 (1)
 /* Erroneous Flag (GPIO) Pin Operations under Specific Sequences */
 #define ANOMALY_05000311 (__SILICON_REVISION__ < 6)
 /* Errors When SSYNC, CSYNC, or Loads to LT, LB and LC Registers Are Interrupted */
 #define ANOMALY_05000312 (__SILICON_REVISION__ < 6)
-/* PPI Is Level-Sensitive on First Transfer */
+/* PPI Is Level-Sensitive on First Transfer In Single Frame Sync Modes */
 #define ANOMALY_05000313 (__SILICON_REVISION__ < 6)
 /* Killed System MMR Write Completes Erroneously On Next System MMR Access */
 #define ANOMALY_05000315 (__SILICON_REVISION__ < 6)
 #define ANOMALY_05000426 (1)
 /* IFLUSH Instruction at End of Hardware Loop Causes Infinite Stall */
 #define ANOMALY_05000443 (1)
+/* False Hardware Error when RETI points to invalid memory */
+#define ANOMALY_05000461 (1)
 
 /* These anomalies have been "phased" out of analog.com anomaly sheets and are
  * here to show running on older silicon just isn't feasible.
  */
 
+/* Internal voltage regulator can't be modified via register writes */
+#define ANOMALY_05000066 (__SILICON_REVISION__ < 2)
 /* Watchpoints (Hardware Breakpoints) are not supported */
 #define ANOMALY_05000067 (__SILICON_REVISION__ < 3)
+/* SDRAM PSSE bit cannot be set again after SDRAM Powerup */
+#define ANOMALY_05000070 (__SILICON_REVISION__ < 2)
+/* Writing FIO_DIR can corrupt a programmable flag's data */
+#define ANOMALY_05000079 (__SILICON_REVISION__ < 2)
+/* Timer Auto-Baud Mode requires the UART clock to be enabled */
+#define ANOMALY_05000086 (__SILICON_REVISION__ < 2)
+/* Internal Clocking Modes on SPORT0 not supported */
+#define ANOMALY_05000088 (__SILICON_REVISION__ < 2)
+/* Internal voltage regulator does not wake up from an RTC wakeup */
+#define ANOMALY_05000092 (__SILICON_REVISION__ < 2)
+/* The IFLUSH instruction must be preceded by a CSYNC instruction */
+#define ANOMALY_05000093 (__SILICON_REVISION__ < 2)
+/* Vectoring to an instruction that is presently being filled into the instruction cache may cause erroneous behavior */
+#define ANOMALY_05000095 (__SILICON_REVISION__ < 2)
+/* PREFETCH, FLUSH, and FLUSHINV must be followed by a CSYNC */
+#define ANOMALY_05000096 (__SILICON_REVISION__ < 2)
+/* Performance Monitor 0 and 1 are swapped when monitoring memory events */
+#define ANOMALY_05000097 (__SILICON_REVISION__ < 2)
+/* 32-bit SPORT DMA will be word reversed */
+#define ANOMALY_05000098 (__SILICON_REVISION__ < 2)
+/* Incorrect status in the UART_IIR register */
+#define ANOMALY_05000100 (__SILICON_REVISION__ < 2)
+/* Reading X_MODIFY or Y_MODIFY while DMA channel is active */
+#define ANOMALY_05000101 (__SILICON_REVISION__ < 2)
+/* Descriptor-based MemDMA may lock up with 32-bit transfers or if transfers span 64KB buffers */
+#define ANOMALY_05000102 (__SILICON_REVISION__ < 2)
+/* Incorrect value written to the cycle counters */
+#define ANOMALY_05000103 (__SILICON_REVISION__ < 2)
+/* Stores to L1 Data memory incorrect when a specific sequence is followed */
+#define ANOMALY_05000104 (__SILICON_REVISION__ < 2)
+/* Programmable Flag (PF3) functionality not supported in all PPI modes */
+#define ANOMALY_05000106 (__SILICON_REVISION__ < 2)
+/* Data store can be lost when targeting a cache line fill */
+#define ANOMALY_05000107 (__SILICON_REVISION__ < 2)
 /* Reserved bits in SYSCFG register not set at power on */
 #define ANOMALY_05000109 (__SILICON_REVISION__ < 3)
+/* Infinite Core Stall */
+#define ANOMALY_05000114 (__SILICON_REVISION__ < 2)
+/* PPI_FSx may glitch when generated by the on chip Timers */
+#define ANOMALY_05000115 (__SILICON_REVISION__ < 2)
 /* Trace Buffers may record discontinuities into emulation mode and/or exception, NMI, reset handlers */
 #define ANOMALY_05000116 (__SILICON_REVISION__ < 3)
+/* DTEST registers allow access to Data Cache when DTEST_COMMAND< 14 >= 0 */
+#define ANOMALY_05000117 (__SILICON_REVISION__ < 2)
+/* Booting from an 8-bit or 24-bit Addressable SPI device is not supported */
+#define ANOMALY_05000118 (__SILICON_REVISION__ < 2)
 /* DTEST_COMMAND initiated memory access may be incorrect if data cache or DMA is active */
 #define ANOMALY_05000123 (__SILICON_REVISION__ < 3)
 /* DMA Lock-up at CCLK to SCLK ratios of 4:1, 2:1, or 1:1 */
 /* DMEM_CONTROL is not set on Reset */
 #define ANOMALY_05000137 (__SILICON_REVISION__ < 3)
 /* SPI boot will not complete if there is a zero fill block in the loader file */
-#define ANOMALY_05000138 (__SILICON_REVISION__ < 3)
+#define ANOMALY_05000138 (__SILICON_REVISION__ == 2)
+/* Timerx_Config must be set for using the PPI in GP output mode with internal Frame Syncs */
+#define ANOMALY_05000139 (__SILICON_REVISION__ < 2)
 /* Allowing the SPORT RX FIFO to fill will cause an overflow */
 #define ANOMALY_05000140 (__SILICON_REVISION__ < 3)
 /* An Infinite Stall occurs with a particular sequence of consecutive dual dag events */
 #define ANOMALY_05000145 (__SILICON_REVISION__ < 3)
 /* MDMA may lose the first few words of a descriptor chain */
 #define ANOMALY_05000146 (__SILICON_REVISION__ < 3)
-/* The source MDMA descriptor may stop with a DMA Error */
+/* Source MDMA descriptor may stop with a DMA Error near beginning of descriptor fetch */
 #define ANOMALY_05000147 (__SILICON_REVISION__ < 3)
 /* When booting from a 16-bit asynchronous memory device, the upper 8-bits of each word must be 0x00 */
 #define ANOMALY_05000148 (__SILICON_REVISION__ < 3)
 /* Frame Delay in SPORT Multichannel Mode */
 #define ANOMALY_05000153 (__SILICON_REVISION__ < 3)
-/* SPORT TFS signal is active in Multi-channel mode outside of valid channels */
+/* SPORT TFS signal stays active in multichannel mode outside of valid channels */
 #define ANOMALY_05000154 (__SILICON_REVISION__ < 3)
 /* Timer1 can not be used for PWMOUT mode when a certain PPI mode is in use */
 #define ANOMALY_05000155 (__SILICON_REVISION__ < 3)
-/* A killed 32-bit System MMR write will lead to the next system MMR access thinking it should be 32-bit. */
+/* Killed 32-bit MMR write leads to next system MMR access thinking it should be 32-bit */
 #define ANOMALY_05000157 (__SILICON_REVISION__ < 3)
 /* SPORT transmit data is not gated by external frame sync in certain conditions */
 #define ANOMALY_05000163 (__SILICON_REVISION__ < 3)
 #define ANOMALY_05000206 (__SILICON_REVISION__ < 3)
 
 /* Anomalies that don't exist on this proc */
+#define ANOMALY_05000120 (0)
+#define ANOMALY_05000149 (0)
+#define ANOMALY_05000171 (0)
+#define ANOMALY_05000220 (0)
+#define ANOMALY_05000248 (0)
 #define ANOMALY_05000266 (0)
+#define ANOMALY_05000274 (0)
+#define ANOMALY_05000287 (0)
 #define ANOMALY_05000323 (0)
 #define ANOMALY_05000353 (1)
+#define ANOMALY_05000362 (1)
 #define ANOMALY_05000380 (0)
 #define ANOMALY_05000386 (1)
+#define ANOMALY_05000389 (0)
 #define ANOMALY_05000412 (0)
+#define ANOMALY_05000430 (0)
 #define ANOMALY_05000432 (0)
 #define ANOMALY_05000435 (0)
 #define ANOMALY_05000447 (0)
 #define ANOMALY_05000448 (0)
+#define ANOMALY_05000456 (0)
+#define ANOMALY_05000450 (0)
 
 #endif
index bbc08fd4f122ae2ad71cc8ed661c50886703d8b0..d81224f9d723d0f8e3b27ea8d558ec1389e77f1f 100644 (file)
@@ -66,6 +66,7 @@ config IRQ_MAC_TX
        default 11
 config IRQ_TIMER0
        int "IRQ_TIMER0"
+       default 7 if TICKSOURCE_GPTMR0
        default 8
 config IRQ_TIMER1
        int "IRQ_TIMER1"
index 41c75b9bfac03dde78f1269738fd750193efe4a2..4fee196731279f5eb86a794be4894d6eefb114db 100644 (file)
@@ -86,7 +86,7 @@ static struct bfin5xx_spi_chip spi_flash_chip_info = {
 };
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) || defined(CONFIG_BFIN_SPI_ADC_MODULE)
 /* SPI ADC chip */
 static struct bfin5xx_spi_chip spi_adc_chip_info = {
        .enable_dma = 1,         /* use dma transfer with this chip*/
@@ -129,7 +129,7 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
        },
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) || defined(CONFIG_BFIN_SPI_ADC_MODULE)
        {
                .modalias = "bfin_spi_adc", /* Name of spi_driver for this device */
                .max_speed_hz = 6250000,     /* max spi clock (SCK) speed in HZ */
index 4e1de1e53f89f396a38ff5faf71fa04300434381..26707ce39f29b98666593b011deb89c8912e5ede 100644 (file)
@@ -265,8 +265,8 @@ static struct bfin5xx_spi_chip spi_flash_chip_info = {
 };
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) \
-       || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) \
+       || defined(CONFIG_BFIN_SPI_ADC_MODULE)
 /* SPI ADC chip */
 static struct bfin5xx_spi_chip spi_adc_chip_info = {
        .enable_dma = 1,         /* use dma transfer with this chip*/
@@ -333,8 +333,8 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
        },
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) \
-       || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) \
+       || defined(CONFIG_BFIN_SPI_ADC_MODULE)
        {
                .modalias = "bfin_spi_adc", /* Name of spi_driver for this device */
                .max_speed_hz = 6250000,     /* max spi clock (SCK) speed in HZ */
index 0572926da23f4ef81a2648b70a7f233c1a3b72fb..dfb5036f8a6b2d8dacdb414227f010196b830e39 100644 (file)
@@ -508,8 +508,8 @@ static struct bfin5xx_spi_chip spi_flash_chip_info = {
 };
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) \
-       || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) \
+       || defined(CONFIG_BFIN_SPI_ADC_MODULE)
 /* SPI ADC chip */
 static struct bfin5xx_spi_chip spi_adc_chip_info = {
        .enable_dma = 1,         /* use dma transfer with this chip*/
@@ -607,6 +607,43 @@ static const struct ad7879_platform_data bfin_ad7879_ts_info = {
 };
 #endif
 
+#if defined(CONFIG_INPUT_ADXL34X) || defined(CONFIG_INPUT_ADXL34X_MODULE)
+#include <linux/input.h>
+#include <linux/spi/adxl34x.h>
+static const struct adxl34x_platform_data adxl34x_info = {
+       .x_axis_offset = 0,
+       .y_axis_offset = 0,
+       .z_axis_offset = 0,
+       .tap_threshold = 0x31,
+       .tap_duration = 0x10,
+       .tap_latency = 0x60,
+       .tap_window = 0xF0,
+       .tap_axis_control = ADXL_TAP_X_EN | ADXL_TAP_Y_EN | ADXL_TAP_Z_EN,
+       .act_axis_control = 0xFF,
+       .activity_threshold = 5,
+       .inactivity_threshold = 3,
+       .inactivity_time = 4,
+       .free_fall_threshold = 0x7,
+       .free_fall_time = 0x20,
+       .data_rate = 0x8,
+       .data_range = ADXL_FULL_RES,
+
+       .ev_type = EV_ABS,
+       .ev_code_x = ABS_X,             /* EV_REL */
+       .ev_code_y = ABS_Y,             /* EV_REL */
+       .ev_code_z = ABS_Z,             /* EV_REL */
+
+       .ev_code_tap_x = BTN_TOUCH,             /* EV_KEY */
+       .ev_code_tap_y = BTN_TOUCH,             /* EV_KEY */
+       .ev_code_tap_z = BTN_TOUCH,             /* EV_KEY */
+
+/*     .ev_code_ff = KEY_F,*/          /* EV_KEY */
+/*     .ev_code_act_inactivity = KEY_A,*/      /* EV_KEY */
+       .power_mode = ADXL_AUTO_SLEEP | ADXL_LINK,
+       .fifo_mode = ADXL_FIFO_STREAM,
+};
+#endif
+
 #if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE)
 static struct bfin5xx_spi_chip spi_ad7879_chip_info = {
        .enable_dma = 0,
@@ -695,8 +732,8 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
                .mode = SPI_MODE_3,
        },
 #endif
-#if defined(CONFIG_SPI_ADC_BF533) \
-       || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) \
+       || defined(CONFIG_BFIN_SPI_ADC_MODULE)
        {
                .modalias = "bfin_spi_adc", /* Name of spi_driver for this device */
                .max_speed_hz = 6250000,     /* max spi clock (SCK) speed in HZ */
@@ -1280,7 +1317,7 @@ static struct i2c_board_info __initdata bfin_i2c_board_info[] = {
                .irq = IRQ_PF5,
        },
 #endif
-#if defined(CONFIG_TWI_LCD) || defined(CONFIG_TWI_LCD_MODULE)
+#if defined(CONFIG_BFIN_TWI_LCD) || defined(CONFIG_TWI_LCD_MODULE)
        {
                I2C_BOARD_INFO("pcf8574_lcd", 0x22),
        },
@@ -1312,6 +1349,13 @@ static struct i2c_board_info __initdata bfin_i2c_board_info[] = {
                .platform_data = (void *)&adp5520_pdev_data,
        },
 #endif
+#if defined(CONFIG_INPUT_ADXL34X_I2C) || defined(CONFIG_INPUT_ADXL34X_I2C_MODULE)
+       {
+               I2C_BOARD_INFO("adxl34x", 0x53),
+               .irq = IRQ_PG3,
+               .platform_data = (void *)&adxl34x_info,
+       },
+#endif
 };
 
 #if defined(CONFIG_SERIAL_BFIN_SPORT) || defined(CONFIG_SERIAL_BFIN_SPORT_MODULE)
@@ -1358,16 +1402,18 @@ static struct resource bfin_pata_resources[] = {
 static struct pata_platform_info bfin_pata_platform_data = {
        .ioport_shift = 0,
 };
-
+/* CompactFlash Storage Card Memory Mapped Adressing
+ * /REG = A11 = 1
+ */
 static struct resource bfin_pata_resources[] = {
        {
-               .start = 0x20211820,
-               .end = 0x2021183F,
+               .start = 0x20211800,
+               .end = 0x20211807,
                .flags = IORESOURCE_MEM,
        },
        {
-               .start = 0x2021181C,
-               .end = 0x2021181F,
+               .start = 0x2021180E,    /* Device Ctl */
+               .end = 0x2021180E,
                .flags = IORESOURCE_MEM,
        },
 };
@@ -1527,7 +1573,8 @@ static int __init stamp_init(void)
        platform_add_devices(stamp_devices, ARRAY_SIZE(stamp_devices));
        spi_register_board_info(bfin_spi_board_info, ARRAY_SIZE(bfin_spi_board_info));
 
-#if defined(CONFIG_PATA_PLATFORM) || defined(CONFIG_PATA_PLATFORM_MODULE)
+#if (defined(CONFIG_PATA_PLATFORM) || defined(CONFIG_PATA_PLATFORM_MODULE)) \
+        && defined(PATA_INT)
        irq_desc[PATA_INT].status |= IRQ_NOAUTOEN;
 #endif
 
index 53ad10f3cd76fb5a7fe945f032e80a172dc0f7fd..280574591201e54b98b61d65513d6d92d79098a3 100644 (file)
@@ -86,7 +86,7 @@ static struct bfin5xx_spi_chip spi_flash_chip_info = {
 };
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) || defined(CONFIG_BFIN_SPI_ADC_MODULE)
 /* SPI ADC chip */
 static struct bfin5xx_spi_chip spi_adc_chip_info = {
        .enable_dma = 1,         /* use dma transfer with this chip*/
@@ -129,7 +129,7 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
        },
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) || defined(CONFIG_BFIN_SPI_ADC_MODULE)
        {
                .modalias = "bfin_spi_adc", /* Name of spi_driver for this device */
                .max_speed_hz = 6250000,     /* max spi clock (SCK) speed in HZ */
index 1bfd80c26c90c35fe0921585a177d4e215eb3bdd..fc9663425465cf5f751f31082f6025766c41cd23 100644 (file)
@@ -6,7 +6,7 @@
  * Licensed under the GPL-2 or later.
  */
 
-/* This file shoule be up to date with:
+/* This file should be up to date with:
  *  - Revision D, 09/18/2008; ADSP-BF534/ADSP-BF536/ADSP-BF537 Blackfin Processor Anomaly List
  */
 
 
 /* Multi-issue instruction with dsp32shiftimm in slot1 and P-reg store in slot 2 not supported */
 #define ANOMALY_05000074 (1)
-/* DMA_RUN bit is not valid after a Peripheral Receive Channel DMA stops */
+/* DMA_RUN Bit Is Not Valid after a Peripheral Receive Channel DMA Stops */
 #define ANOMALY_05000119 (1)
-/* Rx.H cannot be used to access 16-bit System MMR registers */
+/* Rx.H Cannot Be Used to Access 16-bit System MMR Registers */
 #define ANOMALY_05000122 (1)
 /* Killed 32-bit MMR write leads to next system MMR access thinking it should be 32-bit */
 #define ANOMALY_05000157 (__SILICON_REVISION__ < 2)
-/* Turning SPORTs on while External Frame Sync Is Active May Corrupt Data */
-#define ANOMALY_05000167 (1)
-/* PPI_DELAY not functional in PPI modes with 0 frame syncs */
+/* PPI_DELAY Not Functional in PPI Modes with 0 Frame Syncs */
 #define ANOMALY_05000180 (1)
 /* Instruction Cache Is Not Functional */
 #define ANOMALY_05000237 (__SILICON_REVISION__ < 2)
-/* If i-cache is on, CSYNC/SSYNC/IDLE around Change of Control causes failures */
+/* If I-Cache Is On, CSYNC/SSYNC/IDLE Around Change of Control Causes Failures */
 #define ANOMALY_05000244 (__SILICON_REVISION__ < 3)
-/* Spurious Hardware Error from an access in the shadow of a conditional branch */
+/* False Hardware Error from an Access in the Shadow of a Conditional Branch */
 #define ANOMALY_05000245 (1)
 /* CLKIN Buffer Output Enable Reset Behavior Is Changed */
 #define ANOMALY_05000247 (1)
-/* Incorrect Bit-Shift of Data Word in Multichannel (TDM) mode in certain conditions */
+/* Incorrect Bit Shift of Data Word in Multichannel (TDM) Mode in Certain Conditions */
 #define ANOMALY_05000250 (__SILICON_REVISION__ < 3)
 /* EMAC Tx DMA error after an early frame abort */
 #define ANOMALY_05000252 (__SILICON_REVISION__ < 3)
-/* Maximum external clock speed for Timers */
+/* Maximum External Clock Speed for Timers */
 #define ANOMALY_05000253 (__SILICON_REVISION__ < 3)
-/* Incorrect Timer Pulse Width in Single-Shot PWM_OUT mode with external clock */
+/* Incorrect Timer Pulse Width in Single-Shot PWM_OUT Mode with External Clock */
 #define ANOMALY_05000254 (__SILICON_REVISION__ > 2)
-/* Entering Hibernate Mode with RTC Seconds event interrupt not functional */
+/* Entering Hibernate State with RTC Seconds Interrupt Not Functional */
 #define ANOMALY_05000255 (__SILICON_REVISION__ < 3)
 /* EMAC MDIO input latched on wrong MDC edge */
 #define ANOMALY_05000256 (__SILICON_REVISION__ < 3)
-/* Interrupt/Exception during short hardware loop may cause bad instruction fetches */
+/* Interrupt/Exception During Short Hardware Loop May Cause Bad Instruction Fetches */
 #define ANOMALY_05000257 (__SILICON_REVISION__ < 3)
-/* Instruction Cache is corrupted when bits 9 and 12 of the ICPLB Data registers differ */
+/* Instruction Cache Is Corrupted When Bits 9 and 12 of the ICPLB Data Registers Differ */
 #define ANOMALY_05000258 (((ANOMALY_BF536 || ANOMALY_BF537) && __SILICON_REVISION__ == 1) || __SILICON_REVISION__ == 2)
-/* ICPLB_STATUS MMR register may be corrupted */
+/* ICPLB_STATUS MMR Register May Be Corrupted */
 #define ANOMALY_05000260 (__SILICON_REVISION__ == 2)
-/* DCPLB_FAULT_ADDR MMR register may be corrupted */
+/* DCPLB_FAULT_ADDR MMR Register May Be Corrupted */
 #define ANOMALY_05000261 (__SILICON_REVISION__ < 3)
-/* Stores to data cache may be lost */
+/* Stores To Data Cache May Be Lost */
 #define ANOMALY_05000262 (__SILICON_REVISION__ < 3)
-/* Hardware loop corrupted when taking an ICPLB exception */
+/* Hardware Loop Corrupted When Taking an ICPLB Exception */
 #define ANOMALY_05000263 (__SILICON_REVISION__ == 2)
-/* CSYNC/SSYNC/IDLE causes infinite stall in second to last instruction in hardware loop */
+/* CSYNC/SSYNC/IDLE Causes Infinite Stall in Penultimate Instruction in Hardware Loop */
 #define ANOMALY_05000264 (__SILICON_REVISION__ < 3)
-/* Sensitivity to noise with slow input edge rates on external SPORT TX and RX clocks */
+/* Sensitivity To Noise with Slow Input Edge Rates on External SPORT TX and RX Clocks */
 #define ANOMALY_05000265 (1)
 /* Memory DMA error when peripheral DMA is running with non-zero DEB_TRAFFIC_PERIOD */
 #define ANOMALY_05000268 (__SILICON_REVISION__ < 3)
-/* High I/O activity causes output voltage of internal voltage regulator (VDDint) to decrease */
+/* High I/O Activity Causes Output Voltage of Internal Voltage Regulator (Vddint) to Decrease */
 #define ANOMALY_05000270 (__SILICON_REVISION__ < 3)
-/* Certain data cache write through modes fail for VDDint <=0.9V */
+/* Certain Data Cache Writethrough Modes Fail for Vddint <= 0.9V */
 #define ANOMALY_05000272 (1)
-/* Writes to Synchronous SDRAM memory may be lost */
+/* Writes to Synchronous SDRAM Memory May Be Lost */
 #define ANOMALY_05000273 (__SILICON_REVISION__ < 3)
-/* Writes to an I/O data register one SCLK cycle after an edge is detected may clear interrupt */
+/* Writes to an I/O Data Register One SCLK Cycle after an Edge Is Detected May Clear Interrupt */
 #define ANOMALY_05000277 (__SILICON_REVISION__ < 3)
-/* Disabling Peripherals with DMA running may cause DMA system instability */
+/* Disabling Peripherals with DMA Running May Cause DMA System Instability */
 #define ANOMALY_05000278 (((ANOMALY_BF536 || ANOMALY_BF537) && __SILICON_REVISION__ < 3) || (ANOMALY_BF534 && __SILICON_REVISION__ < 2))
 /* SPI Master boot mode does not work well with Atmel Data flash devices */
 #define ANOMALY_05000280 (1)
-/* False Hardware Error Exception when ISR context is not restored */
+/* False Hardware Error Exception When ISR Context Is Not Restored */
 #define ANOMALY_05000281 (__SILICON_REVISION__ < 3)
-/* Memory DMA corruption with 32-bit data and traffic control */
+/* Memory DMA Corruption with 32-Bit Data and Traffic Control */
 #define ANOMALY_05000282 (__SILICON_REVISION__ < 3)
 /* System MMR Write Is Stalled Indefinitely When Killed in a Particular Stage */
 #define ANOMALY_05000283 (__SILICON_REVISION__ < 3)
 /* New Feature: EMAC TX DMA Word Alignment (Not Available On Older Silicon) */
 #define ANOMALY_05000285 (__SILICON_REVISION__ < 3)
-/* SPORTs may receive bad data if FIFOs fill up */
+/* SPORTs May Receive Bad Data If FIFOs Fill Up */
 #define ANOMALY_05000288 (__SILICON_REVISION__ < 3)
-/* Memory to memory DMA source/destination descriptors must be in same memory space */
+/* Memory-To-Memory DMA Source/Destination Descriptors Must Be in Same Memory Space */
 #define ANOMALY_05000301 (1)
 /* SSYNCs After Writes To CAN/DMA MMR Registers Are Not Always Handled Correctly */
 #define ANOMALY_05000304 (__SILICON_REVISION__ < 3)
 #define ANOMALY_05000307 (__SILICON_REVISION__ < 3)
 /* Writing UART_THR while UART clock is disabled sends erroneous start bit */
 #define ANOMALY_05000309 (__SILICON_REVISION__ < 3)
-/* False hardware errors caused by fetches at the boundary of reserved memory */
+/* False Hardware Errors Caused by Fetches at the Boundary of Reserved Memory */
 #define ANOMALY_05000310 (1)
-/* Errors when SSYNC, CSYNC, or loads to LT, LB and LC registers are interrupted */
+/* Errors When SSYNC, CSYNC, or Loads to LT, LB and LC Registers Are Interrupted */
 #define ANOMALY_05000312 (1)
-/* PPI is level sensitive on first transfer */
+/* PPI Is Level-Sensitive on First Transfer In Single Frame Sync Modes */
 #define ANOMALY_05000313 (1)
 /* Killed System MMR Write Completes Erroneously On Next System MMR Access */
 #define ANOMALY_05000315 (__SILICON_REVISION__ < 3)
 #define ANOMALY_05000426 (1)
 /* IFLUSH Instruction at End of Hardware Loop Causes Infinite Stall */
 #define ANOMALY_05000443 (1)
+/* False Hardware Error when RETI points to invalid memory */
+#define ANOMALY_05000461 (1)
 
 /* Anomalies that don't exist on this proc */
+#define ANOMALY_05000099 (0)
+#define ANOMALY_05000120 (0)
 #define ANOMALY_05000125 (0)
+#define ANOMALY_05000149 (0)
 #define ANOMALY_05000158 (0)
+#define ANOMALY_05000171 (0)
+#define ANOMALY_05000179 (0)
 #define ANOMALY_05000183 (0)
 #define ANOMALY_05000198 (0)
+#define ANOMALY_05000215 (0)
+#define ANOMALY_05000220 (0)
+#define ANOMALY_05000227 (0)
 #define ANOMALY_05000230 (0)
+#define ANOMALY_05000231 (0)
+#define ANOMALY_05000233 (0)
+#define ANOMALY_05000242 (0)
+#define ANOMALY_05000248 (0)
 #define ANOMALY_05000266 (0)
+#define ANOMALY_05000274 (0)
+#define ANOMALY_05000287 (0)
 #define ANOMALY_05000311 (0)
 #define ANOMALY_05000323 (0)
 #define ANOMALY_05000353 (1)
+#define ANOMALY_05000362 (1)
 #define ANOMALY_05000363 (0)
 #define ANOMALY_05000380 (0)
 #define ANOMALY_05000386 (1)
+#define ANOMALY_05000389 (0)
+#define ANOMALY_05000400 (0)
 #define ANOMALY_05000412 (0)
+#define ANOMALY_05000430 (0)
 #define ANOMALY_05000432 (0)
 #define ANOMALY_05000435 (0)
 #define ANOMALY_05000447 (0)
 #define ANOMALY_05000448 (0)
+#define ANOMALY_05000456 (0)
+#define ANOMALY_05000450 (0)
 
 #endif
index f068c3523cdc716ab04806f2d2dd4fafbcbd5afc..2d280f504ab0a829178366118cb116b07248e42f 100644 (file)
@@ -57,6 +57,7 @@ config IRQ_UART0_TX
        default 10
 config IRQ_TIMER0
        int "IRQ_TIMER0"
+       default 7 if TICKSOURCE_GPTMR0
        default 8
 config IRQ_TIMER1
        int "IRQ_TIMER1"
index 3a5699827363b9e68c547c2c7628a5852660ab48..175ca9ef7232bca986d412901dbe639b6d4b20e6 100644 (file)
@@ -6,7 +6,7 @@
  * Licensed under the GPL-2 or later.
  */
 
-/* This file shoule be up to date with:
+/* This file should be up to date with:
  *  - Revision G, 09/18/2008; ADSP-BF538/BF538F Blackfin Processor Anomaly List
  *  - Revision L, 09/18/2008; ADSP-BF539/BF539F Blackfin Processor Anomaly List
  */
 #ifndef _MACH_ANOMALY_H_
 #define _MACH_ANOMALY_H_
 
+/* We do not support old silicon - sorry */
 #if __SILICON_REVISION__ < 4
-# error will not work on BF538 silicon version 0.0, 0.1, 0.2, or 0.3
+# error will not work on BF538/BF539 silicon version 0.0, 0.1, 0.2, or 0.3
 #endif
 
-/* Multi-Issue Instruction with dsp32shiftimm in slot1 and P-reg Store in slot2 Not Supported */
+#if defined(__ADSPBF538__)
+# define ANOMALY_BF538 1
+#else
+# define ANOMALY_BF538 0
+#endif
+#if defined(__ADSPBF539__)
+# define ANOMALY_BF539 1
+#else
+# define ANOMALY_BF539 0
+#endif
+
+/* Multi-issue instruction with dsp32shiftimm in slot1 and P-reg store in slot 2 not supported */
 #define ANOMALY_05000074 (1)
 /* DMA_RUN Bit Is Not Valid after a Peripheral Receive Channel DMA Stops */
 #define ANOMALY_05000119 (1)
 /* Rx.H Cannot Be Used to Access 16-bit System MMR Registers */
 #define ANOMALY_05000122 (1)
-/* PPI Data Lengths between 8 and 16 Do Not Zero Out Upper Bits */
+/* PPI Data Lengths Between 8 and 16 Do Not Zero Out Upper Bits */
 #define ANOMALY_05000166 (1)
 /* PPI_COUNT Cannot Be Programmed to 0 in General Purpose TX or RX Modes */
 #define ANOMALY_05000179 (1)
 #define ANOMALY_05000229 (1)
 /* PPI_FS3 Is Not Driven in 2 or 3 Internal Frame Sync Transmit Modes */
 #define ANOMALY_05000233 (1)
-/* If i-cache is on, CSYNC/SSYNC/IDLE around Change of Control causes failures */
+/* If I-Cache Is On, CSYNC/SSYNC/IDLE Around Change of Control Causes Failures */
 #define ANOMALY_05000244 (__SILICON_REVISION__ < 3)
-/* Spurious Hardware Error from an Access in the Shadow of a Conditional Branch */
+/* False Hardware Error from an Access in the Shadow of a Conditional Branch */
 #define ANOMALY_05000245 (1)
 /* Maximum External Clock Speed for Timers */
 #define ANOMALY_05000253 (1)
-/* DCPLB_FAULT_ADDR MMR register may be corrupted */
+/* DCPLB_FAULT_ADDR MMR Register May Be Corrupted */
 #define ANOMALY_05000261 (__SILICON_REVISION__ < 3)
 /* High I/O Activity Causes Output Voltage of Internal Voltage Regulator (Vddint) to Decrease */
 #define ANOMALY_05000270 (__SILICON_REVISION__ < 4)
 #define ANOMALY_05000277 (__SILICON_REVISION__ < 4)
 /* Disabling Peripherals with DMA Running May Cause DMA System Instability */
 #define ANOMALY_05000278 (__SILICON_REVISION__ < 4)
-/* False Hardware Error Exception when ISR Context Is Not Restored */
+/* False Hardware Error Exception When ISR Context Is Not Restored */
 #define ANOMALY_05000281 (__SILICON_REVISION__ < 4)
 /* Memory DMA Corruption with 32-Bit Data and Traffic Control */
 #define ANOMALY_05000282 (__SILICON_REVISION__ < 4)
-/* System MMR Write Is Stalled Indefinitely when Killed in a Particular Stage */
+/* System MMR Write Is Stalled Indefinitely When Killed in a Particular Stage */
 #define ANOMALY_05000283 (__SILICON_REVISION__ < 4)
 /* SPORTs May Receive Bad Data If FIFOs Fill Up */
 #define ANOMALY_05000288 (__SILICON_REVISION__ < 4)
 #define ANOMALY_05000307 (__SILICON_REVISION__ < 4)
 /* False Hardware Errors Caused by Fetches at the Boundary of Reserved Memory */
 #define ANOMALY_05000310 (1)
-/* Errors when SSYNC, CSYNC, or Loads to LT, LB and LC Registers Are Interrupted */
+/* Errors When SSYNC, CSYNC, or Loads to LT, LB and LC Registers Are Interrupted */
 #define ANOMALY_05000312 (__SILICON_REVISION__ < 5)
-/* PPI Is Level-Sensitive on First Transfer */
+/* PPI Is Level-Sensitive on First Transfer In Single Frame Sync Modes */
 #define ANOMALY_05000313 (__SILICON_REVISION__ < 4)
-/* Killed System MMR Write Completes Erroneously on Next System MMR Access */
+/* Killed System MMR Write Completes Erroneously On Next System MMR Access */
 #define ANOMALY_05000315 (__SILICON_REVISION__ < 4)
 /* PFx Glitch on Write to FIO_FLAG_D or FIO_FLAG_T */
-#define ANOMALY_05000318 (__SILICON_REVISION__ < 4)
+#define ANOMALY_05000318 (ANOMALY_BF539 && __SILICON_REVISION__ < 4)
 /* Regulator Programming Blocked when Hibernate Wakeup Source Remains Active */
 #define ANOMALY_05000355 (__SILICON_REVISION__ < 5)
 /* Serial Port (SPORT) Multichannel Transmit Failure when Channel 0 Is Disabled */
 #define ANOMALY_05000436 (__SILICON_REVISION__ > 3)
 /* IFLUSH Instruction at End of Hardware Loop Causes Infinite Stall */
 #define ANOMALY_05000443 (1)
+/* False Hardware Error when RETI points to invalid memory */
+#define ANOMALY_05000461 (1)
 
 /* Anomalies that don't exist on this proc */
+#define ANOMALY_05000099 (0)
+#define ANOMALY_05000120 (0)
+#define ANOMALY_05000149 (0)
 #define ANOMALY_05000158 (0)
+#define ANOMALY_05000171 (0)
 #define ANOMALY_05000198 (0)
+#define ANOMALY_05000215 (0)
+#define ANOMALY_05000220 (0)
+#define ANOMALY_05000227 (0)
 #define ANOMALY_05000230 (0)
+#define ANOMALY_05000231 (0)
+#define ANOMALY_05000242 (0)
+#define ANOMALY_05000248 (0)
+#define ANOMALY_05000250 (0)
+#define ANOMALY_05000254 (0)
 #define ANOMALY_05000263 (0)
+#define ANOMALY_05000274 (0)
+#define ANOMALY_05000287 (0)
 #define ANOMALY_05000305 (0)
 #define ANOMALY_05000311 (0)
 #define ANOMALY_05000323 (0)
 #define ANOMALY_05000353 (1)
+#define ANOMALY_05000362 (1)
 #define ANOMALY_05000363 (0)
 #define ANOMALY_05000380 (0)
 #define ANOMALY_05000386 (1)
+#define ANOMALY_05000389 (0)
+#define ANOMALY_05000400 (0)
 #define ANOMALY_05000412 (0)
+#define ANOMALY_05000430 (0)
 #define ANOMALY_05000432 (0)
 #define ANOMALY_05000435 (0)
 #define ANOMALY_05000447 (0)
 #define ANOMALY_05000448 (0)
+#define ANOMALY_05000456 (0)
+#define ANOMALY_05000450 (0)
 
 #endif
index ea25371a922b3a0c92e19b4c6e0072b2a418fff9..6f628353dde30b1f9b98666d640bcf5830635078 100644 (file)
 #define OFFSET_SCR              0x1C   /* SCR Scratch Register                 */
 #define OFFSET_GCTL             0x24   /* Global Control Register              */
 
-
-#define bfin_write_MDMA_D0_IRQ_STATUS  bfin_write_MDMA0_D0_IRQ_STATUS
-#define bfin_write_MDMA_D0_START_ADDR   bfin_write_MDMA0_D0_START_ADDR
-#define bfin_write_MDMA_S0_START_ADDR   bfin_write_MDMA0_S0_START_ADDR
-#define bfin_write_MDMA_D0_X_COUNT      bfin_write_MDMA0_D0_X_COUNT
-#define bfin_write_MDMA_S0_X_COUNT      bfin_write_MDMA0_S0_X_COUNT
-#define bfin_write_MDMA_D0_Y_COUNT      bfin_write_MDMA0_D0_Y_COUNT
-#define bfin_write_MDMA_S0_Y_COUNT      bfin_write_MDMA0_S0_Y_COUNT
-#define bfin_write_MDMA_D0_X_MODIFY     bfin_write_MDMA0_D0_X_MODIFY
-#define bfin_write_MDMA_S0_X_MODIFY     bfin_write_MDMA0_S0_X_MODIFY
-#define bfin_write_MDMA_D0_Y_MODIFY     bfin_write_MDMA0_D0_Y_MODIFY
-#define bfin_write_MDMA_S0_Y_MODIFY     bfin_write_MDMA0_S0_Y_MODIFY
-#define bfin_write_MDMA_S0_CONFIG       bfin_write_MDMA0_S0_CONFIG
-#define bfin_write_MDMA_D0_CONFIG       bfin_write_MDMA0_D0_CONFIG
-#define bfin_read_MDMA_S0_CONFIG        bfin_read_MDMA0_S0_CONFIG
-#define bfin_read_MDMA_D0_IRQ_STATUS    bfin_read_MDMA0_D0_IRQ_STATUS
-#define bfin_write_MDMA_S0_IRQ_STATUS   bfin_write_MDMA0_S0_IRQ_STATUS
-
-
 /* DPMC*/
 #define bfin_read_STOPCK_OFF() bfin_read_STOPCK()
 #define bfin_write_STOPCK_OFF(val) bfin_write_STOPCK(val)
index 241725bc69881c0703549ce16ddac62389a41754..99ca3f4305e2fd233a86a4cf34e5e9a89492d610 100644 (file)
 #define bfin_write_SIC_ISR0(val)       bfin_write32(SIC_ISR0, val)
 #define bfin_read_SIC_ISR1()           bfin_read32(SIC_ISR1)
 #define bfin_write_SIC_ISR1(val)       bfin_write32(SIC_ISR1, val)
-#define bfin_read_SIC_ISR(x)          bfin_read32(SIC_ISR0 + x * (SIC_ISR1 - SIC_ISR0))
+#define bfin_read_SIC_ISR(x)           bfin_read32(SIC_ISR0 + x * (SIC_ISR1 - SIC_ISR0))
 #define bfin_write_SIC_ISR(x, val)     bfin_write32(SIC_ISR0 + x * (SIC_ISR1 - SIC_ISR0), val)
 #define bfin_read_SIC_IWR0()           bfin_read32(SIC_IWR0)
 #define bfin_write_SIC_IWR0(val)       bfin_write32(SIC_IWR0, val)
 #define bfin_read_SIC_IWR1()           bfin_read32(SIC_IWR1)
 #define bfin_write_SIC_IWR1(val)       bfin_write32(SIC_IWR1, val)
-#define bfin_read_SIC_IWR(x)          bfin_read32(SIC_IWR0 + x * (SIC_IWR1 - SIC_IWR0))
-#define bfin_write_SIC_IWR(x, val)     bfin_write32((SIC_IWR0 + x * (SIC_IWR1 - SIC_IWR0), val)
+#define bfin_read_SIC_IWR(x)           bfin_read32(SIC_IWR0 + x * (SIC_IWR1 - SIC_IWR0))
+#define bfin_write_SIC_IWR(x, val)     bfin_write32(SIC_IWR0 + x * (SIC_IWR1 - SIC_IWR0), val)
 #define bfin_read_SIC_IAR0()           bfin_read32(SIC_IAR0)
 #define bfin_write_SIC_IAR0(val)       bfin_write32(SIC_IAR0, val)
 #define bfin_read_SIC_IAR1()           bfin_read32(SIC_IAR1)
 #define bfin_write_MDMA1_S1_CURR_X_COUNT(val) bfin_write16(MDMA1_S1_CURR_X_COUNT, val)
 #define bfin_read_MDMA1_S1_CURR_Y_COUNT() bfin_read16(MDMA1_S1_CURR_Y_COUNT)
 #define bfin_write_MDMA1_S1_CURR_Y_COUNT(val) bfin_write16(MDMA1_S1_CURR_Y_COUNT, val)
+
+#define bfin_read_MDMA_S0_CONFIG()  bfin_read_MDMA0_S0_CONFIG()
+#define bfin_write_MDMA_S0_CONFIG(val) bfin_write_MDMA0_S0_CONFIG(val)
+#define bfin_read_MDMA_S0_IRQ_STATUS()  bfin_read_MDMA0_S0_IRQ_STATUS()
+#define bfin_write_MDMA_S0_IRQ_STATUS(val) bfin_write_MDMA0_S0_IRQ_STATUS(val)
+#define bfin_read_MDMA_S0_X_MODIFY()  bfin_read_MDMA0_S0_X_MODIFY()
+#define bfin_write_MDMA_S0_X_MODIFY(val) bfin_write_MDMA0_S0_X_MODIFY(val)
+#define bfin_read_MDMA_S0_Y_MODIFY()  bfin_read_MDMA0_S0_Y_MODIFY()
+#define bfin_write_MDMA_S0_Y_MODIFY(val) bfin_write_MDMA0_S0_Y_MODIFY(val)
+#define bfin_read_MDMA_S0_X_COUNT()  bfin_read_MDMA0_S0_X_COUNT()
+#define bfin_write_MDMA_S0_X_COUNT(val) bfin_write_MDMA0_S0_X_COUNT(val)
+#define bfin_read_MDMA_S0_Y_COUNT()  bfin_read_MDMA0_S0_Y_COUNT()
+#define bfin_write_MDMA_S0_Y_COUNT(val) bfin_write_MDMA0_S0_Y_COUNT(val)
+#define bfin_read_MDMA_S0_START_ADDR()  bfin_read_MDMA0_S0_START_ADDR()
+#define bfin_write_MDMA_S0_START_ADDR(val) bfin_write_MDMA0_S0_START_ADDR(val)
+#define bfin_read_MDMA_D0_CONFIG()  bfin_read_MDMA0_D0_CONFIG()
+#define bfin_write_MDMA_D0_CONFIG(val) bfin_write_MDMA0_D0_CONFIG(val)
+#define bfin_read_MDMA_D0_IRQ_STATUS()  bfin_read_MDMA0_D0_IRQ_STATUS()
+#define bfin_write_MDMA_D0_IRQ_STATUS(val) bfin_write_MDMA0_D0_IRQ_STATUS(val)
+#define bfin_read_MDMA_D0_X_MODIFY()  bfin_read_MDMA0_D0_X_MODIFY()
+#define bfin_write_MDMA_D0_X_MODIFY(val) bfin_write_MDMA0_D0_X_MODIFY(val)
+#define bfin_read_MDMA_D0_Y_MODIFY()  bfin_read_MDMA0_D0_Y_MODIFY()
+#define bfin_write_MDMA_D0_Y_MODIFY(val) bfin_write_MDMA0_D0_Y_MODIFY(val)
+#define bfin_read_MDMA_D0_X_COUNT()  bfin_read_MDMA0_D0_X_COUNT()
+#define bfin_write_MDMA_D0_X_COUNT(val) bfin_write_MDMA0_D0_X_COUNT(val)
+#define bfin_read_MDMA_D0_Y_COUNT()  bfin_read_MDMA0_D0_Y_COUNT()
+#define bfin_write_MDMA_D0_Y_COUNT(val) bfin_write_MDMA0_D0_Y_COUNT(val)
+#define bfin_read_MDMA_D0_START_ADDR()  bfin_read_MDMA0_D0_START_ADDR()
+#define bfin_write_MDMA_D0_START_ADDR(val) bfin_write_MDMA0_D0_START_ADDR(val)
+
+#define bfin_read_MDMA_S1_CONFIG()  bfin_read_MDMA0_S1_CONFIG()
+#define bfin_write_MDMA_S1_CONFIG(val) bfin_write_MDMA0_S1_CONFIG(val)
+#define bfin_read_MDMA_S1_IRQ_STATUS()  bfin_read_MDMA0_S1_IRQ_STATUS()
+#define bfin_write_MDMA_S1_IRQ_STATUS(val) bfin_write_MDMA0_S1_IRQ_STATUS(val)
+#define bfin_read_MDMA_S1_X_MODIFY()  bfin_read_MDMA0_S1_X_MODIFY()
+#define bfin_write_MDMA_S1_X_MODIFY(val) bfin_write_MDMA0_S1_X_MODIFY(val)
+#define bfin_read_MDMA_S1_Y_MODIFY()  bfin_read_MDMA0_S1_Y_MODIFY()
+#define bfin_write_MDMA_S1_Y_MODIFY(val) bfin_write_MDMA0_S1_Y_MODIFY(val)
+#define bfin_read_MDMA_S1_X_COUNT()  bfin_read_MDMA0_S1_X_COUNT()
+#define bfin_write_MDMA_S1_X_COUNT(val) bfin_write_MDMA0_S1_X_COUNT(val)
+#define bfin_read_MDMA_S1_Y_COUNT()  bfin_read_MDMA0_S1_Y_COUNT()
+#define bfin_write_MDMA_S1_Y_COUNT(val) bfin_write_MDMA0_S1_Y_COUNT(val)
+#define bfin_read_MDMA_S1_START_ADDR()  bfin_read_MDMA0_S1_START_ADDR()
+#define bfin_write_MDMA_S1_START_ADDR(val) bfin_write_MDMA0_S1_START_ADDR(val)
+#define bfin_read_MDMA_D1_CONFIG()  bfin_read_MDMA0_D1_CONFIG()
+#define bfin_write_MDMA_D1_CONFIG(val) bfin_write_MDMA0_D1_CONFIG(val)
+#define bfin_read_MDMA_D1_IRQ_STATUS()  bfin_read_MDMA0_D1_IRQ_STATUS()
+#define bfin_write_MDMA_D1_IRQ_STATUS(val) bfin_write_MDMA0_D1_IRQ_STATUS(val)
+#define bfin_read_MDMA_D1_X_MODIFY()  bfin_read_MDMA0_D1_X_MODIFY()
+#define bfin_write_MDMA_D1_X_MODIFY(val) bfin_write_MDMA0_D1_X_MODIFY(val)
+#define bfin_read_MDMA_D1_Y_MODIFY()  bfin_read_MDMA0_D1_Y_MODIFY()
+#define bfin_write_MDMA_D1_Y_MODIFY(val) bfin_write_MDMA0_D1_Y_MODIFY(val)
+#define bfin_read_MDMA_D1_X_COUNT()  bfin_read_MDMA0_D1_X_COUNT()
+#define bfin_write_MDMA_D1_X_COUNT(val) bfin_write_MDMA0_D1_X_COUNT(val)
+#define bfin_read_MDMA_D1_Y_COUNT()  bfin_read_MDMA0_D1_Y_COUNT()
+#define bfin_write_MDMA_D1_Y_COUNT(val) bfin_write_MDMA0_D1_Y_COUNT(val)
+#define bfin_read_MDMA_D1_START_ADDR()  bfin_read_MDMA0_D1_START_ADDR()
+#define bfin_write_MDMA_D1_START_ADDR(val) bfin_write_MDMA0_D1_START_ADDR(val)
+
 #define bfin_read_PPI_CONTROL()        bfin_read16(PPI_CONTROL)
 #define bfin_write_PPI_CONTROL(val)    bfin_write16(PPI_CONTROL, val)
 #define bfin_read_PPI_STATUS()         bfin_read16(PPI_STATUS)
index 6adbfcc65a3527ebde33be0e3092102c324112f6..bdc330cd0e1c3e41354433f2a556627058e0bceb 100644 (file)
 #define        MDMA0_S1_CURR_X_COUNT   0xFFC00EF0      /* MemDMA0 Stream 1 Source Current X Count Register */
 #define        MDMA0_S1_CURR_Y_COUNT   0xFFC00EF8      /* MemDMA0 Stream 1 Source Current Y Count Register */
 
+#define MDMA_D0_NEXT_DESC_PTR MDMA0_D0_NEXT_DESC_PTR
+#define MDMA_D0_START_ADDR MDMA0_D0_START_ADDR
+#define MDMA_D0_CONFIG MDMA0_D0_CONFIG
+#define MDMA_D0_X_COUNT MDMA0_D0_X_COUNT
+#define MDMA_D0_X_MODIFY MDMA0_D0_X_MODIFY
+#define MDMA_D0_Y_COUNT MDMA0_D0_Y_COUNT
+#define MDMA_D0_Y_MODIFY MDMA0_D0_Y_MODIFY
+#define MDMA_D0_CURR_DESC_PTR MDMA0_D0_CURR_DESC_PTR
+#define MDMA_D0_CURR_ADDR MDMA0_D0_CURR_ADDR
+#define MDMA_D0_IRQ_STATUS MDMA0_D0_IRQ_STATUS
+#define MDMA_D0_PERIPHERAL_MAP MDMA0_D0_PERIPHERAL_MAP
+#define MDMA_D0_CURR_X_COUNT MDMA0_D0_CURR_X_COUNT
+#define MDMA_D0_CURR_Y_COUNT MDMA0_D0_CURR_Y_COUNT
+
+#define MDMA_S0_NEXT_DESC_PTR MDMA0_S0_NEXT_DESC_PTR
+#define MDMA_S0_START_ADDR MDMA0_S0_START_ADDR
+#define MDMA_S0_CONFIG MDMA0_S0_CONFIG
+#define MDMA_S0_X_COUNT MDMA0_S0_X_COUNT
+#define MDMA_S0_X_MODIFY MDMA0_S0_X_MODIFY
+#define MDMA_S0_Y_COUNT MDMA0_S0_Y_COUNT
+#define MDMA_S0_Y_MODIFY MDMA0_S0_Y_MODIFY
+#define MDMA_S0_CURR_DESC_PTR MDMA0_S0_CURR_DESC_PTR
+#define MDMA_S0_CURR_ADDR MDMA0_S0_CURR_ADDR
+#define MDMA_S0_IRQ_STATUS MDMA0_S0_IRQ_STATUS
+#define MDMA_S0_PERIPHERAL_MAP MDMA0_S0_PERIPHERAL_MAP
+#define MDMA_S0_CURR_X_COUNT MDMA0_S0_CURR_X_COUNT
+#define MDMA_S0_CURR_Y_COUNT MDMA0_S0_CURR_Y_COUNT
+
+#define MDMA_D1_NEXT_DESC_PTR MDMA0_D1_NEXT_DESC_PTR
+#define MDMA_D1_START_ADDR MDMA0_D1_START_ADDR
+#define MDMA_D1_CONFIG MDMA0_D1_CONFIG
+#define MDMA_D1_X_COUNT MDMA0_D1_X_COUNT
+#define MDMA_D1_X_MODIFY MDMA0_D1_X_MODIFY
+#define MDMA_D1_Y_COUNT MDMA0_D1_Y_COUNT
+#define MDMA_D1_Y_MODIFY MDMA0_D1_Y_MODIFY
+#define MDMA_D1_CURR_DESC_PTR MDMA0_D1_CURR_DESC_PTR
+#define MDMA_D1_CURR_ADDR MDMA0_D1_CURR_ADDR
+#define MDMA_D1_IRQ_STATUS MDMA0_D1_IRQ_STATUS
+#define MDMA_D1_PERIPHERAL_MAP MDMA0_D1_PERIPHERAL_MAP
+#define MDMA_D1_CURR_X_COUNT MDMA0_D1_CURR_X_COUNT
+#define MDMA_D1_CURR_Y_COUNT MDMA0_D1_CURR_Y_COUNT
+
+#define MDMA_S1_NEXT_DESC_PTR MDMA0_S1_NEXT_DESC_PTR
+#define MDMA_S1_START_ADDR MDMA0_S1_START_ADDR
+#define MDMA_S1_CONFIG MDMA0_S1_CONFIG
+#define MDMA_S1_X_COUNT MDMA0_S1_X_COUNT
+#define MDMA_S1_X_MODIFY MDMA0_S1_X_MODIFY
+#define MDMA_S1_Y_COUNT MDMA0_S1_Y_COUNT
+#define MDMA_S1_Y_MODIFY MDMA0_S1_Y_MODIFY
+#define MDMA_S1_CURR_DESC_PTR MDMA0_S1_CURR_DESC_PTR
+#define MDMA_S1_CURR_ADDR MDMA0_S1_CURR_ADDR
+#define MDMA_S1_IRQ_STATUS MDMA0_S1_IRQ_STATUS
+#define MDMA_S1_PERIPHERAL_MAP MDMA0_S1_PERIPHERAL_MAP
+#define MDMA_S1_CURR_X_COUNT MDMA0_S1_CURR_X_COUNT
+#define MDMA_S1_CURR_Y_COUNT MDMA0_S1_CURR_Y_COUNT
+
 
 /* Parallel Peripheral Interface (PPI) (0xFFC01000 - 0xFFC010FF) */
 #define        PPI_CONTROL                     0xFFC01000      /* PPI Control Register */
index dcf657159051cfd8a1fb34714ea3586ba6bff5c4..a09623dfd5504b063199c72b9f76d4df43ffaee7 100644 (file)
@@ -11,6 +11,13 @@ config DEB_DMA_URGENT
        help
          Treat any DEB1, DEB2 and DEB3 request as Urgent
 
+config BF548_ATAPI_ALTERNATIVE_PORT
+       bool "BF548 ATAPI alternative port via GPIO"
+       help
+         BF548 ATAPI data and address PINs can be routed through
+         async address or GPIO port F and G. Select y to route it
+         to GPIO.
+
 comment "Interrupt Priority Assignment"
 menu "Priority"
 
@@ -250,6 +257,7 @@ config IRQ_OTPSEC
        default 11
 config IRQ_TIMER0
        int "IRQ_TIMER0"
+       default 7 if TICKSOURCE_GPTMR0
        default 8
 config IRQ_TIMER1
        int "IRQ_TIMER1"
index 096e661700a7d666ab4534edbdb847a83975100f..add5a17452cec8010bf3386de428a6d47ce3af1e 100644 (file)
@@ -208,6 +208,43 @@ static struct platform_device bfin_rotary_device = {
 };
 #endif
 
+#if defined(CONFIG_INPUT_ADXL34X) || defined(CONFIG_INPUT_ADXL34X_MODULE)
+#include <linux/input.h>
+#include <linux/spi/adxl34x.h>
+static const struct adxl34x_platform_data adxl34x_info = {
+       .x_axis_offset = 0,
+       .y_axis_offset = 0,
+       .z_axis_offset = 0,
+       .tap_threshold = 0x31,
+       .tap_duration = 0x10,
+       .tap_latency = 0x60,
+       .tap_window = 0xF0,
+       .tap_axis_control = ADXL_TAP_X_EN | ADXL_TAP_Y_EN | ADXL_TAP_Z_EN,
+       .act_axis_control = 0xFF,
+       .activity_threshold = 5,
+       .inactivity_threshold = 3,
+       .inactivity_time = 4,
+       .free_fall_threshold = 0x7,
+       .free_fall_time = 0x20,
+       .data_rate = 0x8,
+       .data_range = ADXL_FULL_RES,
+
+       .ev_type = EV_ABS,
+       .ev_code_x = ABS_X,             /* EV_REL */
+       .ev_code_y = ABS_Y,             /* EV_REL */
+       .ev_code_z = ABS_Z,             /* EV_REL */
+
+       .ev_code_tap_x = BTN_TOUCH,             /* EV_KEY */
+       .ev_code_tap_y = BTN_TOUCH,             /* EV_KEY */
+       .ev_code_tap_z = BTN_TOUCH,             /* EV_KEY */
+
+/*     .ev_code_ff = KEY_F,*/          /* EV_KEY */
+/*     .ev_code_act_inactivity = KEY_A,*/      /* EV_KEY */
+       .power_mode = ADXL_AUTO_SLEEP | ADXL_LINK,
+       .fifo_mode = ADXL_FIFO_STREAM,
+};
+#endif
+
 #if defined(CONFIG_RTC_DRV_BFIN) || defined(CONFIG_RTC_DRV_BFIN_MODULE)
 static struct platform_device rtc_device = {
        .name = "rtc-bfin",
@@ -628,6 +665,14 @@ static struct bfin5xx_spi_chip spidev_chip_info = {
 };
 #endif
 
+#if defined(CONFIG_INPUT_ADXL34X_SPI) || defined(CONFIG_INPUT_ADXL34X_SPI_MODULE)
+static struct bfin5xx_spi_chip spi_adxl34x_chip_info = {
+       .enable_dma = 0,         /* use dma transfer with this chip*/
+       .bits_per_word = 8,
+       .cs_change_per_word = 0,
+};
+#endif
+
 static struct spi_board_info bfin_spi_board_info[] __initdata = {
 #if defined(CONFIG_MTD_M25P80) \
        || defined(CONFIG_MTD_M25P80_MODULE)
@@ -653,15 +698,15 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
        },
 #endif
 #if defined(CONFIG_TOUCHSCREEN_AD7877) || defined(CONFIG_TOUCHSCREEN_AD7877_MODULE)
-{
-       .modalias               = "ad7877",
-       .platform_data          = &bfin_ad7877_ts_info,
-       .irq                    = IRQ_PB4,      /* old boards (<=Rev 1.3) use IRQ_PJ11 */
-       .max_speed_hz           = 12500000,     /* max spi clock (SCK) speed in HZ */
-       .bus_num                = 0,
-       .chip_select            = 2,
-       .controller_data = &spi_ad7877_chip_info,
-},
+       {
+               .modalias               = "ad7877",
+               .platform_data          = &bfin_ad7877_ts_info,
+               .irq                    = IRQ_PB4,      /* old boards (<=Rev 1.3) use IRQ_PJ11 */
+               .max_speed_hz           = 12500000,     /* max spi clock (SCK) speed in HZ */
+               .bus_num                = 0,
+               .chip_select            = 2,
+               .controller_data = &spi_ad7877_chip_info,
+       },
 #endif
 #if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE)
        {
@@ -672,8 +717,19 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
                .controller_data = &spidev_chip_info,
        },
 #endif
+#if defined(CONFIG_INPUT_ADXL34X_SPI) || defined(CONFIG_INPUT_ADXL34X_SPI_MODULE)
+       {
+               .modalias               = "adxl34x",
+               .platform_data          = &adxl34x_info,
+               .irq                    = IRQ_PC5,
+               .max_speed_hz           = 5000000,     /* max spi clock (SCK) speed in HZ */
+               .bus_num                = 1,
+               .chip_select            = 2,
+               .controller_data = &spi_adxl34x_chip_info,
+               .mode = SPI_MODE_3,
+       },
+#endif
 };
-
 #if defined(CONFIG_SPI_BFIN) || defined(CONFIG_SPI_BFIN_MODULE)
 /* SPI (0) */
 static struct resource bfin_spi0_resource[] = {
@@ -786,7 +842,7 @@ static struct i2c_board_info __initdata bfin_i2c_board_info0[] = {
 
 #if !defined(CONFIG_BF542)     /* The BF542 only has 1 TWI */
 static struct i2c_board_info __initdata bfin_i2c_board_info1[] = {
-#if defined(CONFIG_TWI_LCD) || defined(CONFIG_TWI_LCD_MODULE)
+#if defined(CONFIG_BFIN_TWI_LCD) || defined(CONFIG_TWI_LCD_MODULE)
        {
                I2C_BOARD_INFO("pcf8574_lcd", 0x22),
        },
@@ -797,6 +853,13 @@ static struct i2c_board_info __initdata bfin_i2c_board_info1[] = {
                .irq = 212,
        },
 #endif
+#if defined(CONFIG_INPUT_ADXL34X_I2C) || defined(CONFIG_INPUT_ADXL34X_I2C_MODULE)
+       {
+               I2C_BOARD_INFO("adxl34x", 0x53),
+               .irq = IRQ_PC5,
+               .platform_data = (void *)&adxl34x_info,
+       },
+#endif
 };
 #endif
 
index 882e40ccf0d16f752563bdffffc46f98f2ed6f99..c510ae688e283a59439e199d6fd3acff7826fc51 100644 (file)
@@ -6,26 +6,31 @@
  * Licensed under the GPL-2 or later.
  */
 
-/* This file shoule be up to date with:
+/* This file should be up to date with:
  *  - Revision H, 01/16/2009; ADSP-BF542/BF544/BF547/BF548/BF549 Blackfin Processor Anomaly List
  */
 
 #ifndef _MACH_ANOMALY_H_
 #define _MACH_ANOMALY_H_
 
-/* Multi-Issue Instruction with dsp32shiftimm in slot1 and P-reg Store in slot2 Not Supported */
+/* We do not support 0.0 or 0.1 silicon - sorry */
+#if __SILICON_REVISION__ < 2
+# error will not work on BF548 silicon version 0.0, or 0.1
+#endif
+
+/* Multi-issue instruction with dsp32shiftimm in slot1 and P-reg store in slot 2 not supported */
 #define ANOMALY_05000074 (1)
 /* DMA_RUN Bit Is Not Valid after a Peripheral Receive Channel DMA Stops */
 #define ANOMALY_05000119 (1)
 /* Rx.H Cannot Be Used to Access 16-bit System MMR Registers */
 #define ANOMALY_05000122 (1)
-/* Spurious Hardware Error from an Access in the Shadow of a Conditional Branch */
+/* False Hardware Error from an Access in the Shadow of a Conditional Branch */
 #define ANOMALY_05000245 (1)
 /* Sensitivity To Noise with Slow Input Edge Rates on External SPORT TX and RX Clocks */
 #define ANOMALY_05000265 (1)
 /* Certain Data Cache Writethrough Modes Fail for Vddint <= 0.9V */
 #define ANOMALY_05000272 (1)
-/* False Hardware Error Exception when ISR context is not restored */
+/* False Hardware Error Exception When ISR Context Is Not Restored */
 #define ANOMALY_05000281 (__SILICON_REVISION__ < 1)
 /* SSYNCs After Writes To CAN/DMA MMR Registers Are Not Always Handled Correctly */
 #define ANOMALY_05000304 (__SILICON_REVISION__ < 1)
@@ -59,7 +64,7 @@
 #define ANOMALY_05000340 (__SILICON_REVISION__ < 1)
 /* Boot Host Wait (HWAIT) and Boot Host Wait Alternate (HWAITA) Signals Are Swapped */
 #define ANOMALY_05000344 (__SILICON_REVISION__ < 1)
-/* USB Calibration Value Is Not Intialized */
+/* USB Calibration Value Is Not Initialized */
 #define ANOMALY_05000346 (__SILICON_REVISION__ < 1)
 /* USB Calibration Value to use */
 #define ANOMALY_05000346_value 0x5411
 #define ANOMALY_05000416 (1)
 /* Multichannel SPORT Channel Misalignment Under Specific Configuration */
 #define ANOMALY_05000425 (1)
-/* Speculative Fetches of Indirect-Pointer Instructions Can Cause Spurious Hardware Errors */
+/* Speculative Fetches of Indirect-Pointer Instructions Can Cause False Hardware Errors */
 #define ANOMALY_05000426 (1)
 /* CORE_EPPI_PRIO bit and SYS_EPPI_PRIO bit in the HMDMA1_CONTROL register are not functional */
 #define ANOMALY_05000427 (__SILICON_REVISION__ < 2)
-/* WB_EDGE Bit in NFC_IRQSTAT Incorrectly Behaves as a Buffer Status Bit Instead of an IRQ Status Bit */
+/* WB_EDGE Bit in NFC_IRQSTAT Incorrectly Reflects Buffer Status Instead of IRQ Status */
 #define ANOMALY_05000429 (__SILICON_REVISION__ < 2)
 /* Software System Reset Corrupts PLL_LOCKCNT Register */
 #define ANOMALY_05000430 (__SILICON_REVISION__ >= 2)
 /* Reduced Timing Margins on DDR Output Setup and Hold (tDS and tDH) */
 #define ANOMALY_05000449 (__SILICON_REVISION__ == 1)
 /* USB DMA Mode 1 Short Packet Data Corruption */
-#define ANOMALY_05000450 (1
+#define ANOMALY_05000450 (1)
+/* USB Receive Interrupt Is Not Generated in DMA Mode 1 */
+#define ANOMALY_05000456 (__SILICON_REVISION__ < 3)
+/* False Hardware Error when RETI points to invalid memory */
+#define ANOMALY_05000461 (1)
 
 /* Anomalies that don't exist on this proc */
+#define ANOMALY_05000099 (0)
+#define ANOMALY_05000120 (0)
 #define ANOMALY_05000125 (0)
+#define ANOMALY_05000149 (0)
 #define ANOMALY_05000158 (0)
+#define ANOMALY_05000171 (0)
+#define ANOMALY_05000179 (0)
 #define ANOMALY_05000183 (0)
 #define ANOMALY_05000198 (0)
+#define ANOMALY_05000215 (0)
+#define ANOMALY_05000220 (0)
+#define ANOMALY_05000227 (0)
 #define ANOMALY_05000230 (0)
+#define ANOMALY_05000231 (0)
+#define ANOMALY_05000233 (0)
+#define ANOMALY_05000242 (0)
 #define ANOMALY_05000244 (0)
+#define ANOMALY_05000248 (0)
+#define ANOMALY_05000250 (0)
+#define ANOMALY_05000254 (0)
 #define ANOMALY_05000261 (0)
 #define ANOMALY_05000263 (0)
 #define ANOMALY_05000266 (0)
 #define ANOMALY_05000273 (0)
+#define ANOMALY_05000274 (0)
 #define ANOMALY_05000278 (0)
+#define ANOMALY_05000287 (0)
+#define ANOMALY_05000301 (0)
 #define ANOMALY_05000305 (0)
 #define ANOMALY_05000307 (0)
 #define ANOMALY_05000311 (0)
 #define ANOMALY_05000323 (0)
+#define ANOMALY_05000362 (1)
 #define ANOMALY_05000363 (0)
 #define ANOMALY_05000380 (0)
+#define ANOMALY_05000400 (0)
 #define ANOMALY_05000412 (0)
 #define ANOMALY_05000432 (0)
 #define ANOMALY_05000435 (0)
index ffb1d0a44b4d798e9f83df9a2e5e9b4c02b5d155..ce372ba0f04644ff4c8e7b79060973265608e8be 100644 (file)
 #define P_PPI0_D13     (P_DEFINED | P_IDENT(GPIO_PF13) | P_FUNCT(0))
 #define P_PPI0_D14     (P_DEFINED | P_IDENT(GPIO_PF14) | P_FUNCT(0))
 #define P_PPI0_D15     (P_DEFINED | P_IDENT(GPIO_PF15) | P_FUNCT(0))
-#define P_ATAPI_D0A    (P_DEFINED | P_IDENT(GPIO_PF0) | P_FUNCT(1))
-#define P_ATAPI_D1A    (P_DEFINED | P_IDENT(GPIO_PF1) | P_FUNCT(1))
-#define P_ATAPI_D2A    (P_DEFINED | P_IDENT(GPIO_PF2) | P_FUNCT(1))
-#define P_ATAPI_D3A    (P_DEFINED | P_IDENT(GPIO_PF3) | P_FUNCT(1))
-#define P_ATAPI_D4A    (P_DEFINED | P_IDENT(GPIO_PF4) | P_FUNCT(1))
-#define P_ATAPI_D5A    (P_DEFINED | P_IDENT(GPIO_PF5) | P_FUNCT(1))
-#define P_ATAPI_D6A    (P_DEFINED | P_IDENT(GPIO_PF6) | P_FUNCT(1))
-#define P_ATAPI_D7A    (P_DEFINED | P_IDENT(GPIO_PF7) | P_FUNCT(1))
-#define P_ATAPI_D8A    (P_DEFINED | P_IDENT(GPIO_PF8) | P_FUNCT(1))
-#define P_ATAPI_D9A    (P_DEFINED | P_IDENT(GPIO_PF9) | P_FUNCT(1))
-#define P_ATAPI_D10A   (P_DEFINED | P_IDENT(GPIO_PF10) | P_FUNCT(1))
-#define P_ATAPI_D11A   (P_DEFINED | P_IDENT(GPIO_PF11) | P_FUNCT(1))
-#define P_ATAPI_D12A   (P_DEFINED | P_IDENT(GPIO_PF12) | P_FUNCT(1))
-#define P_ATAPI_D13A   (P_DEFINED | P_IDENT(GPIO_PF13) | P_FUNCT(1))
-#define P_ATAPI_D14A   (P_DEFINED | P_IDENT(GPIO_PF14) | P_FUNCT(1))
-#define P_ATAPI_D15A   (P_DEFINED | P_IDENT(GPIO_PF15) | P_FUNCT(1))
+
+#ifdef CONFIG_BF548_ATAPI_ALTERNATIVE_PORT
+# define P_ATAPI_D0A   (P_DEFINED | P_IDENT(GPIO_PF0) | P_FUNCT(1))
+# define P_ATAPI_D1A   (P_DEFINED | P_IDENT(GPIO_PF1) | P_FUNCT(1))
+# define P_ATAPI_D2A   (P_DEFINED | P_IDENT(GPIO_PF2) | P_FUNCT(1))
+# define P_ATAPI_D3A   (P_DEFINED | P_IDENT(GPIO_PF3) | P_FUNCT(1))
+# define P_ATAPI_D4A   (P_DEFINED | P_IDENT(GPIO_PF4) | P_FUNCT(1))
+# define P_ATAPI_D5A   (P_DEFINED | P_IDENT(GPIO_PF5) | P_FUNCT(1))
+# define P_ATAPI_D6A   (P_DEFINED | P_IDENT(GPIO_PF6) | P_FUNCT(1))
+# define P_ATAPI_D7A   (P_DEFINED | P_IDENT(GPIO_PF7) | P_FUNCT(1))
+# define P_ATAPI_D8A   (P_DEFINED | P_IDENT(GPIO_PF8) | P_FUNCT(1))
+# define P_ATAPI_D9A   (P_DEFINED | P_IDENT(GPIO_PF9) | P_FUNCT(1))
+# define P_ATAPI_D10A  (P_DEFINED | P_IDENT(GPIO_PF10) | P_FUNCT(1))
+# define P_ATAPI_D11A  (P_DEFINED | P_IDENT(GPIO_PF11) | P_FUNCT(1))
+# define P_ATAPI_D12A  (P_DEFINED | P_IDENT(GPIO_PF12) | P_FUNCT(1))
+# define P_ATAPI_D13A  (P_DEFINED | P_IDENT(GPIO_PF13) | P_FUNCT(1))
+# define P_ATAPI_D14A  (P_DEFINED | P_IDENT(GPIO_PF14) | P_FUNCT(1))
+# define P_ATAPI_D15A  (P_DEFINED | P_IDENT(GPIO_PF15) | P_FUNCT(1))
+#else
+# define P_ATAPI_D0A   (P_DONTCARE)
+# define P_ATAPI_D1A   (P_DONTCARE)
+# define P_ATAPI_D2A   (P_DONTCARE)
+# define P_ATAPI_D3A   (P_DONTCARE)
+# define P_ATAPI_D4A   (P_DONTCARE)
+# define P_ATAPI_D5A   (P_DONTCARE)
+# define P_ATAPI_D6A   (P_DONTCARE)
+# define P_ATAPI_D7A   (P_DONTCARE)
+# define P_ATAPI_D8A   (P_DONTCARE)
+# define P_ATAPI_D9A   (P_DONTCARE)
+# define P_ATAPI_D10A  (P_DONTCARE)
+# define P_ATAPI_D11A  (P_DONTCARE)
+# define P_ATAPI_D12A  (P_DONTCARE)
+# define P_ATAPI_D13A  (P_DONTCARE)
+# define P_ATAPI_D14A  (P_DONTCARE)
+# define P_ATAPI_D15A  (P_DONTCARE)
+#endif
 
 #define P_PPI0_CLK     (P_DEFINED | P_IDENT(GPIO_PG0) | P_FUNCT(0))
 #define P_PPI0_FS1     (P_DEFINED | P_IDENT(GPIO_PG1) | P_FUNCT(0))
 #define P_CAN0_RX      (P_DEFINED | P_IDENT(GPIO_PG13) | P_FUNCT(0))
 #define P_CAN1_TX      (P_DEFINED | P_IDENT(GPIO_PG14) | P_FUNCT(0))
 #define P_CAN1_RX      (P_DEFINED | P_IDENT(GPIO_PG15) | P_FUNCT(0))
-#define P_ATAPI_A0A    (P_DEFINED | P_IDENT(GPIO_PG2) | P_FUNCT(1))
-#define P_ATAPI_A1A    (P_DEFINED | P_IDENT(GPIO_PG3) | P_FUNCT(1))
-#define P_ATAPI_A2A    (P_DEFINED | P_IDENT(GPIO_PG4) | P_FUNCT(1))
+#ifdef CONFIG_BF548_ATAPI_ALTERNATIVE_PORT
+# define P_ATAPI_A0A   (P_DEFINED | P_IDENT(GPIO_PG2) | P_FUNCT(1))
+# define P_ATAPI_A1A   (P_DEFINED | P_IDENT(GPIO_PG3) | P_FUNCT(1))
+# define P_ATAPI_A2A   (P_DEFINED | P_IDENT(GPIO_PG4) | P_FUNCT(1))
+#else
+# define P_ATAPI_A0A   (P_DONTCARE)
+# define P_ATAPI_A1A   (P_DONTCARE)
+# define P_ATAPI_A2A   (P_DONTCARE)
+#endif
 #define P_HOST_CE      (P_DEFINED | P_IDENT(GPIO_PG5) | P_FUNCT(1))
 #define P_HOST_RD      (P_DEFINED | P_IDENT(GPIO_PG6) | P_FUNCT(1))
 #define P_HOST_WR      (P_DEFINED | P_IDENT(GPIO_PG7) | P_FUNCT(1))
index 638ec38ca47066ccc8fcfc15a294a00598934c2e..cb97436415113f65725852e125af02d0197bc992 100644 (file)
@@ -9,22 +9,9 @@ if (!SMP)
 comment "Core B Support"
 
 config BF561_COREB
-       bool "Enable Core B support"
+       bool "Enable Core B loader"
        default y
 
-config BF561_COREB_RESET
-       bool "Enable Core B reset support"
-       default n
-       help
-         This requires code in the application that is loaded
-         into Core B. In order to reset, the application needs
-         to install an interrupt handler for Supplemental
-         Interrupt 0, that sets RETI to 0xff600000 and writes
-         bit 11 of SICB_SYSCR when bit 5 of SICA_SYSCR is 0.
-         This causes Core B to stall when Supplemental Interrupt
-         0 is set, and will reset PC to 0xff600000 when
-         COREB_SRAM_INIT is cleared.
-
 endif
 
 comment "Interrupt Priority Assignment"
@@ -138,6 +125,7 @@ config IRQ_DMA2_11
        default 9
 config IRQ_TIMER0
        int "TIMER 0  Interrupt"
+       default 7 if TICKSOURCE_GPTMR0
        default 8
 config IRQ_TIMER1
        int "TIMER 1  Interrupt"
index f623c6b0719fbc2711f1942e19e4a7143f359c4a..0dd9685e5d53f967e9e76a3aea1a317f983c8911 100644 (file)
@@ -83,7 +83,7 @@ static struct bfin5xx_spi_chip spi_flash_chip_info = {
 };
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) || defined(CONFIG_BFIN_SPI_ADC_MODULE)
 /* SPI ADC chip */
 static struct bfin5xx_spi_chip spi_adc_chip_info = {
        .enable_dma = 1,         /* use dma transfer with this chip*/
@@ -126,7 +126,7 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
        },
 #endif
 
-#if defined(CONFIG_SPI_ADC_BF533) || defined(CONFIG_SPI_ADC_BF533_MODULE)
+#if defined(CONFIG_BFIN_SPI_ADC) || defined(CONFIG_BFIN_SPI_ADC_MODULE)
        {
                .modalias = "bfin_spi_adc", /* Name of spi_driver for this device */
                .max_speed_hz = 6250000,     /* max spi clock (SCK) speed in HZ */
index 8598098c08408e85382bb21960f219a130a71ec5..93635a766f9cded6c93e8cc011e47e35e62da0c1 100644 (file)
-/*
- * File:         arch/blackfin/mach-bf561/coreb.c
- * Based on:
- * Author:
+/* Load firmware into Core B on a BF561
  *
- * Created:
- * Description:  Handle CoreB on a BF561
- *
- * Modified:
- *               Copyright 2004-2006 Analog Devices Inc.
- *
- * Bugs:         Enter bugs at http://blackfin.uclinux.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, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ * Copyright 2004-2009 Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+/* The Core B reset func requires code in the application that is loaded into
+ * Core B.  In order to reset, the application needs to install an interrupt
+ * handler for Supplemental Interrupt 0, that sets RETI to 0xff600000 and
+ * writes bit 11 of SICB_SYSCR when bit 5 of SICA_SYSCR is 0.  This causes Core
+ * B to stall when Supplemental Interrupt 0 is set, and will reset PC to
+ * 0xff600000 when COREB_SRAM_INIT is cleared.
  */
 
-#include <linux/mm.h>
-#include <linux/miscdevice.h>
 #include <linux/device.h>
-#include <linux/ioport.h>
-#include <linux/module.h>
-#include <linux/uaccess.h>
 #include <linux/fs.h>
-#include <asm/dma.h>
-#include <asm/cacheflush.h>
-
-#define MODULE_VER             "v0.1"
-
-static spinlock_t coreb_lock;
-static wait_queue_head_t coreb_dma_wait;
-
-#define COREB_IS_OPEN          0x00000001
-#define COREB_IS_RUNNING       0x00000010
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
 
-#define CMD_COREB_INDEX                1
 #define CMD_COREB_START                2
 #define CMD_COREB_STOP         3
 #define CMD_COREB_RESET                4
 
-#define COREB_MINOR            229
-
-static unsigned long coreb_status = 0;
-static unsigned long coreb_base = 0xff600000;
-static unsigned long coreb_size = 0x4000;
-int coreb_dma_done;
-
-static loff_t coreb_lseek(struct file *file, loff_t offset, int origin);
-static ssize_t coreb_read(struct file *file, char *buf, size_t count,
-                         loff_t * ppos);
-static ssize_t coreb_write(struct file *file, const char *buf, size_t count,
-                          loff_t * ppos);
-static int coreb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
-                      unsigned long arg);
-static int coreb_open(struct inode *inode, struct file *file);
-static int coreb_release(struct inode *inode, struct file *file);
-
-static irqreturn_t coreb_dma_interrupt(int irq, void *dev_id)
-{
-       clear_dma_irqstat(CH_MEM_STREAM2_DEST);
-       coreb_dma_done = 1;
-       wake_up_interruptible(&coreb_dma_wait);
-       return IRQ_HANDLED;
-}
-
-static ssize_t coreb_write(struct file *file, const char *buf, size_t count,
-                          loff_t * ppos)
-{
-       unsigned long p = *ppos;
-       ssize_t wrote = 0;
-
-       if (p + count > coreb_size)
-               return -EFAULT;
-
-       while (count > 0) {
-               int len = count;
-
-               if (len > PAGE_SIZE)
-                       len = PAGE_SIZE;
-
-               coreb_dma_done = 0;
-
-               flush_dcache_range((unsigned long)buf, (unsigned long)(buf+len));
-               /* Source Channel */
-               set_dma_start_addr(CH_MEM_STREAM2_SRC, (unsigned long)buf);
-               set_dma_x_count(CH_MEM_STREAM2_SRC, len);
-               set_dma_x_modify(CH_MEM_STREAM2_SRC, sizeof(char));
-               set_dma_config(CH_MEM_STREAM2_SRC, 0);
-               /* Destination Channel */
-               set_dma_start_addr(CH_MEM_STREAM2_DEST, coreb_base + p);
-               set_dma_x_count(CH_MEM_STREAM2_DEST, len);
-               set_dma_x_modify(CH_MEM_STREAM2_DEST, sizeof(char));
-               set_dma_config(CH_MEM_STREAM2_DEST, WNR | RESTART | DI_EN);
-
-               enable_dma(CH_MEM_STREAM2_SRC);
-               enable_dma(CH_MEM_STREAM2_DEST);
-
-               wait_event_interruptible(coreb_dma_wait, coreb_dma_done);
-
-               disable_dma(CH_MEM_STREAM2_SRC);
-               disable_dma(CH_MEM_STREAM2_DEST);
-
-               count -= len;
-               wrote += len;
-               buf += len;
-               p += len;
-       }
-       *ppos = p;
-       return wrote;
-}
-
-static ssize_t coreb_read(struct file *file, char *buf, size_t count,
-                         loff_t * ppos)
-{
-       unsigned long p = *ppos;
-       ssize_t read = 0;
-
-       if ((p + count) > coreb_size)
-               return -EFAULT;
-
-       while (count > 0) {
-               int len = count;
-
-               if (len > PAGE_SIZE)
-                       len = PAGE_SIZE;
-
-               coreb_dma_done = 0;
-
-               invalidate_dcache_range((unsigned long)buf, (unsigned long)(buf+len));
-               /* Source Channel */
-               set_dma_start_addr(CH_MEM_STREAM2_SRC, coreb_base + p);
-               set_dma_x_count(CH_MEM_STREAM2_SRC, len);
-               set_dma_x_modify(CH_MEM_STREAM2_SRC, sizeof(char));
-               set_dma_config(CH_MEM_STREAM2_SRC, 0);
-               /* Destination Channel */
-               set_dma_start_addr(CH_MEM_STREAM2_DEST, (unsigned long)buf);
-               set_dma_x_count(CH_MEM_STREAM2_DEST, len);
-               set_dma_x_modify(CH_MEM_STREAM2_DEST, sizeof(char));
-               set_dma_config(CH_MEM_STREAM2_DEST, WNR | RESTART | DI_EN);
-
-               enable_dma(CH_MEM_STREAM2_SRC);
-               enable_dma(CH_MEM_STREAM2_DEST);
-
-               wait_event_interruptible(coreb_dma_wait, coreb_dma_done);
-
-               disable_dma(CH_MEM_STREAM2_SRC);
-               disable_dma(CH_MEM_STREAM2_DEST);
-
-               count -= len;
-               read += len;
-               buf += len;
-               p += len;
-       }
-
-       return read;
-}
-
-static loff_t coreb_lseek(struct file *file, loff_t offset, int origin)
+static int
+coreb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
-       loff_t ret;
-
-       mutex_lock(&file->f_dentry->d_inode->i_mutex);
-
-       switch (origin) {
-       case 0 /* SEEK_SET */ :
-               if (offset < coreb_size) {
-                       file->f_pos = offset;
-                       ret = file->f_pos;
-               } else
-                       ret = -EINVAL;
-               break;
-       case 1 /* SEEK_CUR */ :
-               if ((offset + file->f_pos) < coreb_size) {
-                       file->f_pos += offset;
-                       ret = file->f_pos;
-               } else
-                       ret = -EINVAL;
-       default:
-               ret = -EINVAL;
-       }
-       mutex_unlock(&file->f_dentry->d_inode->i_mutex);
-       return ret;
-}
-
-/* No BKL needed here */
-static int coreb_open(struct inode *inode, struct file *file)
-{
-       spin_lock_irq(&coreb_lock);
-
-       if (coreb_status & COREB_IS_OPEN)
-               goto out_busy;
-
-       coreb_status |= COREB_IS_OPEN;
-
-       spin_unlock_irq(&coreb_lock);
-       return 0;
-
- out_busy:
-       spin_unlock_irq(&coreb_lock);
-       return -EBUSY;
-}
-
-static int coreb_release(struct inode *inode, struct file *file)
-{
-       spin_lock_irq(&coreb_lock);
-       coreb_status &= ~COREB_IS_OPEN;
-       spin_unlock_irq(&coreb_lock);
-       return 0;
-}
-
-static int coreb_ioctl(struct inode *inode, struct file *file,
-                      unsigned int cmd, unsigned long arg)
-{
-       int retval = 0;
-       int coreb_index = 0;
+       int ret = 0;
 
        switch (cmd) {
-       case CMD_COREB_INDEX:
-               if (copy_from_user(&coreb_index, (int *)arg, sizeof(int))) {
-                       retval = -EFAULT;
-                       break;
-               }
-
-               spin_lock_irq(&coreb_lock);
-               switch (coreb_index) {
-               case 0:
-                       coreb_base = 0xff600000;
-                       coreb_size = 0x4000;
-                       break;
-               case 1:
-                       coreb_base = 0xff610000;
-                       coreb_size = 0x4000;
-                       break;
-               case 2:
-                       coreb_base = 0xff500000;
-                       coreb_size = 0x8000;
-                       break;
-               case 3:
-                       coreb_base = 0xff400000;
-                       coreb_size = 0x8000;
-                       break;
-               default:
-                       retval = -EINVAL;
-                       break;
-               }
-               spin_unlock_irq(&coreb_lock);
-
-               mutex_lock(&file->f_dentry->d_inode->i_mutex);
-               file->f_pos = 0;
-               mutex_unlock(&file->f_dentry->d_inode->i_mutex);
-               break;
        case CMD_COREB_START:
-               spin_lock_irq(&coreb_lock);
-               if (coreb_status & COREB_IS_RUNNING) {
-                       retval = -EBUSY;
-                       break;
-               }
-               printk(KERN_INFO "Starting Core B\n");
-               coreb_status |= COREB_IS_RUNNING;
                bfin_write_SICA_SYSCR(bfin_read_SICA_SYSCR() & ~0x0020);
-               SSYNC();
-               spin_unlock_irq(&coreb_lock);
                break;
-#if defined(CONFIG_BF561_COREB_RESET)
        case CMD_COREB_STOP:
-               spin_lock_irq(&coreb_lock);
-               printk(KERN_INFO "Stopping Core B\n");
                bfin_write_SICA_SYSCR(bfin_read_SICA_SYSCR() | 0x0020);
                bfin_write_SICB_SYSCR(bfin_read_SICB_SYSCR() | 0x0080);
-               coreb_status &= ~COREB_IS_RUNNING;
-               spin_unlock_irq(&coreb_lock);
                break;
        case CMD_COREB_RESET:
-               printk(KERN_INFO "Resetting Core B\n");
                bfin_write_SICB_SYSCR(bfin_read_SICB_SYSCR() | 0x0080);
                break;
-#endif
+       default:
+               ret = -EINVAL;
+               break;
        }
 
-       return retval;
+       CSYNC();
+
+       return ret;
 }
 
 static struct file_operations coreb_fops = {
-       .owner = THIS_MODULE,
-       .llseek = coreb_lseek,
-       .read = coreb_read,
-       .write = coreb_write,
-       .ioctl = coreb_ioctl,
-       .open = coreb_open,
-       .release = coreb_release
+       .owner   = THIS_MODULE,
+       .ioctl   = coreb_ioctl,
 };
 
 static struct miscdevice coreb_dev = {
-       COREB_MINOR,
-       "coreb",
-       &coreb_fops
+       .minor = MISC_DYNAMIC_MINOR,
+       .name  = "coreb",
+       .fops  = &coreb_fops,
 };
 
-static ssize_t coreb_show_status(struct device *dev, struct device_attribute *attr, char *buf)
+static int __init bf561_coreb_init(void)
 {
-       return sprintf(buf,
-                      "Base Address:\t0x%08lx\n"
-                      "Core B is %s\n"
-                      "SICA_SYSCR:\t%04x\n"
-                      "SICB_SYSCR:\t%04x\n"
-                      "\n"
-                      "IRQ Status:\tCore A\t\tCore B\n"
-                      "ISR0:\t\t%08x\t\t%08x\n"
-                      "ISR1:\t\t%08x\t\t%08x\n"
-                      "IMASK0:\t\t%08x\t\t%08x\n"
-                      "IMASK1:\t\t%08x\t\t%08x\n",
-                      coreb_base,
-                      coreb_status & COREB_IS_RUNNING ? "running" : "stalled",
-                      bfin_read_SICA_SYSCR(), bfin_read_SICB_SYSCR(),
-                      bfin_read_SICA_ISR0(), bfin_read_SICB_ISR0(),
-                      bfin_read_SICA_ISR1(), bfin_read_SICB_ISR0(),
-                      bfin_read_SICA_IMASK0(), bfin_read_SICB_IMASK0(),
-                      bfin_read_SICA_IMASK1(), bfin_read_SICB_IMASK1());
-}
-
-static DEVICE_ATTR(coreb_status, S_IRUGO, coreb_show_status, NULL);
-
-int __init bf561_coreb_init(void)
-{
-       init_waitqueue_head(&coreb_dma_wait);
-
-       spin_lock_init(&coreb_lock);
-       /* Request the core memory regions for Core B */
-       if (request_mem_region(0xff600000, 0x4000,
-                              "Core B - Instruction SRAM") == NULL)
-               goto exit;
-
-       if (request_mem_region(0xFF610000, 0x4000,
-                              "Core B - Instruction SRAM") == NULL)
-               goto release_instruction_a_sram;
-
-       if (request_mem_region(0xFF500000, 0x8000,
-                              "Core B - Data Bank B SRAM") == NULL)
-               goto release_instruction_b_sram;
-
-       if (request_mem_region(0xff400000, 0x8000,
-                              "Core B - Data Bank A SRAM") == NULL)
-               goto release_data_b_sram;
-
-       if (request_dma(CH_MEM_STREAM2_DEST, "Core B - DMA Destination") < 0)
-               goto release_data_a_sram;
-
-       if (request_dma(CH_MEM_STREAM2_SRC, "Core B - DMA Source") < 0)
-               goto release_dma_dest;
-
-       set_dma_callback(CH_MEM_STREAM2_DEST, coreb_dma_interrupt, NULL);
-
-       misc_register(&coreb_dev);
-
-       if (device_create_file(coreb_dev.this_device, &dev_attr_coreb_status))
-               goto release_dma_src;
-
-       printk(KERN_INFO "BF561 Core B driver %s initialized.\n", MODULE_VER);
-       return 0;
-
- release_dma_src:
-       free_dma(CH_MEM_STREAM2_SRC);
- release_dma_dest:
-       free_dma(CH_MEM_STREAM2_DEST);
- release_data_a_sram:
-       release_mem_region(0xff400000, 0x8000);
- release_data_b_sram:
-       release_mem_region(0xff500000, 0x8000);
- release_instruction_b_sram:
-       release_mem_region(0xff610000, 0x4000);
- release_instruction_a_sram:
-       release_mem_region(0xff600000, 0x4000);
- exit:
-       return -ENOMEM;
+       return misc_register(&coreb_dev);
 }
+module_init(bf561_coreb_init);
 
-void __exit bf561_coreb_exit(void)
+static void __exit bf561_coreb_exit(void)
 {
-       device_remove_file(coreb_dev.this_device, &dev_attr_coreb_status);
        misc_deregister(&coreb_dev);
-
-       release_mem_region(0xff610000, 0x4000);
-       release_mem_region(0xff600000, 0x4000);
-       release_mem_region(0xff500000, 0x8000);
-       release_mem_region(0xff400000, 0x8000);
-
-       free_dma(CH_MEM_STREAM2_DEST);
-       free_dma(CH_MEM_STREAM2_SRC);
 }
-
-module_init(bf561_coreb_init);
 module_exit(bf561_coreb_exit);
 
 MODULE_AUTHOR("Bas Vermeulen <bvermeul@blackstar.xs4all.nl>");
index d0b0b3506440fee5373c850b19cf703a94941681..dccd396cd93158e188677f8bf0eaf9961fde3958 100644 (file)
@@ -6,7 +6,7 @@
  * Licensed under the GPL-2 or later.
  */
 
-/* This file shoule be up to date with:
+/* This file should be up to date with:
  *  - Revision Q, 11/07/2008; ADSP-BF561 Blackfin Processor Anomaly List
  */
 
 # error will not work on BF561 silicon version 0.0, 0.1, 0.2, or 0.4
 #endif
 
-/* Multi-Issue Instruction with dsp32shiftimm in slot1 and P-reg Store in slot 2 Not Supported */
+/* Multi-issue instruction with dsp32shiftimm in slot1 and P-reg store in slot 2 not supported */
 #define ANOMALY_05000074 (1)
 /* UART Line Status Register (UART_LSR) Bits Are Not Updated at the Same Time */
 #define ANOMALY_05000099 (__SILICON_REVISION__ < 5)
-/* Trace Buffers may contain errors in emulation mode and/or exception, NMI, reset handlers */
+/* Trace Buffers may record discontinuities into emulation mode and/or exception, NMI, reset handlers */
 #define ANOMALY_05000116 (__SILICON_REVISION__ < 3)
 /* Testset instructions restricted to 32-bit aligned memory locations */
 #define ANOMALY_05000120 (1)
@@ -40,7 +40,7 @@
 #define ANOMALY_05000136 (__SILICON_REVISION__ < 3)
 /* Allowing the SPORT RX FIFO to fill will cause an overflow */
 #define ANOMALY_05000140 (__SILICON_REVISION__ < 3)
-/* Infinite Stall may occur with a particular sequence of consecutive dual dag events */
+/* An Infinite Stall occurs with a particular sequence of consecutive dual dag events */
 #define ANOMALY_05000141 (__SILICON_REVISION__ < 3)
 /* Interrupts may be lost when a programmable input flag is configured to be edge sensitive */
 #define ANOMALY_05000142 (__SILICON_REVISION__ < 3)
@@ -80,7 +80,7 @@
 #define ANOMALY_05000163 (__SILICON_REVISION__ < 3)
 /* PPI Data Lengths Between 8 and 16 Do Not Zero Out Upper Bits */
 #define ANOMALY_05000166 (1)
-/* Turning Serial Ports on with External Frame Syncs */
+/* Turning SPORTs on while External Frame Sync Is Active May Corrupt Data */
 #define ANOMALY_05000167 (1)
 /* SDRAM auto-refresh and subsequent Power Ups */
 #define ANOMALY_05000168 (__SILICON_REVISION__ < 5)
 #define ANOMALY_05000242 (__SILICON_REVISION__ < 5)
 /* If I-Cache Is On, CSYNC/SSYNC/IDLE Around Change of Control Causes Failures */
 #define ANOMALY_05000244 (__SILICON_REVISION__ < 5)
-/* Spurious Hardware Error from an Access in the Shadow of a Conditional Branch */
+/* False Hardware Error from an Access in the Shadow of a Conditional Branch */
 #define ANOMALY_05000245 (__SILICON_REVISION__ < 5)
 /* TESTSET operation forces stall on the other core */
 #define ANOMALY_05000248 (__SILICON_REVISION__ < 5)
 #define ANOMALY_05000275 (__SILICON_REVISION__ > 2)
 /* Timing Requirements Change for External Frame Sync PPI Modes with Non-Zero PPI_DELAY */
 #define ANOMALY_05000276 (__SILICON_REVISION__ < 5)
-/* Writes to an I/O data register one SCLK cycle after an edge is detected may clear interrupt */
+/* Writes to an I/O Data Register One SCLK Cycle after an Edge Is Detected May Clear Interrupt */
 #define ANOMALY_05000277 (__SILICON_REVISION__ < 3)
 /* Disabling Peripherals with DMA Running May Cause DMA System Instability */
 #define ANOMALY_05000278 (__SILICON_REVISION__ < 5)
 #define ANOMALY_05000310 (1)
 /* Errors When SSYNC, CSYNC, or Loads to LT, LB and LC Registers Are Interrupted */
 #define ANOMALY_05000312 (1)
-/* PPI Is Level-Sensitive on First Transfer */
+/* PPI Is Level-Sensitive on First Transfer In Single Frame Sync Modes */
 #define ANOMALY_05000313 (1)
 /* Killed System MMR Write Completes Erroneously On Next System MMR Access */
 #define ANOMALY_05000315 (1)
 #define ANOMALY_05000428 (__SILICON_REVISION__ > 3)
 /* IFLUSH Instruction at End of Hardware Loop Causes Infinite Stall */
 #define ANOMALY_05000443 (1)
+/* False Hardware Error when RETI points to invalid memory */
+#define ANOMALY_05000461 (1)
 
 /* Anomalies that don't exist on this proc */
+#define ANOMALY_05000119 (0)
 #define ANOMALY_05000158 (0)
 #define ANOMALY_05000183 (0)
+#define ANOMALY_05000233 (0)
 #define ANOMALY_05000273 (0)
 #define ANOMALY_05000311 (0)
 #define ANOMALY_05000353 (1)
 #define ANOMALY_05000380 (0)
 #define ANOMALY_05000386 (1)
+#define ANOMALY_05000389 (0)
+#define ANOMALY_05000400 (0)
+#define ANOMALY_05000430 (0)
 #define ANOMALY_05000432 (0)
 #define ANOMALY_05000435 (0)
 #define ANOMALY_05000447 (0)
 #define ANOMALY_05000448 (0)
+#define ANOMALY_05000456 (0)
+#define ANOMALY_05000450 (0)
 
 #endif
index 95d609f11c97d207b43bf05eb5e5a0caac890253..9d9858c2be68bacadf7a5f78ca228c9175639a6d 100644 (file)
 #define bfin_read_MDMA_D0_START_ADDR()  bfin_read_MDMA1_D0_START_ADDR()
 #define bfin_write_MDMA_D0_START_ADDR(val) bfin_write_MDMA1_D0_START_ADDR(val)
 
+#define bfin_read_MDMA_S1_CONFIG()  bfin_read_MDMA1_S1_CONFIG()
+#define bfin_write_MDMA_S1_CONFIG(val) bfin_write_MDMA1_S1_CONFIG(val)
+#define bfin_read_MDMA_S1_IRQ_STATUS()  bfin_read_MDMA1_S1_IRQ_STATUS()
+#define bfin_write_MDMA_S1_IRQ_STATUS(val) bfin_write_MDMA1_S1_IRQ_STATUS(val)
+#define bfin_read_MDMA_S1_X_MODIFY()  bfin_read_MDMA1_S1_X_MODIFY()
+#define bfin_write_MDMA_S1_X_MODIFY(val) bfin_write_MDMA1_S1_X_MODIFY(val)
+#define bfin_read_MDMA_S1_Y_MODIFY()  bfin_read_MDMA1_S1_Y_MODIFY()
+#define bfin_write_MDMA_S1_Y_MODIFY(val) bfin_write_MDMA1_S1_Y_MODIFY(val)
+#define bfin_read_MDMA_S1_X_COUNT()  bfin_read_MDMA1_S1_X_COUNT()
+#define bfin_write_MDMA_S1_X_COUNT(val) bfin_write_MDMA1_S1_X_COUNT(val)
+#define bfin_read_MDMA_S1_Y_COUNT()  bfin_read_MDMA1_S1_Y_COUNT()
+#define bfin_write_MDMA_S1_Y_COUNT(val) bfin_write_MDMA1_S1_Y_COUNT(val)
+#define bfin_read_MDMA_S1_START_ADDR()  bfin_read_MDMA1_S1_START_ADDR()
+#define bfin_write_MDMA_S1_START_ADDR(val) bfin_write_MDMA1_S1_START_ADDR(val)
+#define bfin_read_MDMA_D1_CONFIG()  bfin_read_MDMA1_D1_CONFIG()
+#define bfin_write_MDMA_D1_CONFIG(val) bfin_write_MDMA1_D1_CONFIG(val)
+#define bfin_read_MDMA_D1_IRQ_STATUS()  bfin_read_MDMA1_D1_IRQ_STATUS()
+#define bfin_write_MDMA_D1_IRQ_STATUS(val) bfin_write_MDMA1_D1_IRQ_STATUS(val)
+#define bfin_read_MDMA_D1_X_MODIFY()  bfin_read_MDMA1_D1_X_MODIFY()
+#define bfin_write_MDMA_D1_X_MODIFY(val) bfin_write_MDMA1_D1_X_MODIFY(val)
+#define bfin_read_MDMA_D1_Y_MODIFY()  bfin_read_MDMA1_D1_Y_MODIFY()
+#define bfin_write_MDMA_D1_Y_MODIFY(val) bfin_write_MDMA1_D1_Y_MODIFY(val)
+#define bfin_read_MDMA_D1_X_COUNT()  bfin_read_MDMA1_D1_X_COUNT()
+#define bfin_write_MDMA_D1_X_COUNT(val) bfin_write_MDMA1_D1_X_COUNT(val)
+#define bfin_read_MDMA_D1_Y_COUNT()  bfin_read_MDMA1_D1_Y_COUNT()
+#define bfin_write_MDMA_D1_Y_COUNT(val) bfin_write_MDMA1_D1_Y_COUNT(val)
+#define bfin_read_MDMA_D1_START_ADDR()  bfin_read_MDMA1_D1_START_ADDR()
+#define bfin_write_MDMA_D1_START_ADDR(val) bfin_write_MDMA1_D1_START_ADDR(val)
+
 /* These need to be last due to the cdef/linux inter-dependencies */
 #include <asm/irq.h>
 
index cf922295f4ce61902a1e959c8ae74f5522592121..5fc0f05026e093a7f4cb74fee1b41f6fb206e2c0 100644 (file)
 #define MDMA2_S1_IRQ_STATUS 0xFFC00FE8 /*MemDMA2 Stream 1 Source Interrupt/Status Register */
 #define MDMA2_S1_PERIPHERAL_MAP 0xFFC00FEC     /*MemDMA2 Stream 1 Source Peripheral Map register */
 
+#define MDMA_D0_NEXT_DESC_PTR MDMA1_D0_NEXT_DESC_PTR
+#define MDMA_D0_START_ADDR MDMA1_D0_START_ADDR
+#define MDMA_D0_CONFIG MDMA1_D0_CONFIG
+#define MDMA_D0_X_COUNT MDMA1_D0_X_COUNT
+#define MDMA_D0_X_MODIFY MDMA1_D0_X_MODIFY
+#define MDMA_D0_Y_COUNT MDMA1_D0_Y_COUNT
+#define MDMA_D0_Y_MODIFY MDMA1_D0_Y_MODIFY
+#define MDMA_D0_CURR_DESC_PTR MDMA1_D0_CURR_DESC_PTR
+#define MDMA_D0_CURR_ADDR MDMA1_D0_CURR_ADDR
+#define MDMA_D0_IRQ_STATUS MDMA1_D0_IRQ_STATUS
+#define MDMA_D0_PERIPHERAL_MAP MDMA1_D0_PERIPHERAL_MAP
+#define MDMA_D0_CURR_X_COUNT MDMA1_D0_CURR_X_COUNT
+#define MDMA_D0_CURR_Y_COUNT MDMA1_D0_CURR_Y_COUNT
+
+#define MDMA_S0_NEXT_DESC_PTR MDMA1_S0_NEXT_DESC_PTR
+#define MDMA_S0_START_ADDR MDMA1_S0_START_ADDR
+#define MDMA_S0_CONFIG MDMA1_S0_CONFIG
+#define MDMA_S0_X_COUNT MDMA1_S0_X_COUNT
+#define MDMA_S0_X_MODIFY MDMA1_S0_X_MODIFY
+#define MDMA_S0_Y_COUNT MDMA1_S0_Y_COUNT
+#define MDMA_S0_Y_MODIFY MDMA1_S0_Y_MODIFY
+#define MDMA_S0_CURR_DESC_PTR MDMA1_S0_CURR_DESC_PTR
+#define MDMA_S0_CURR_ADDR MDMA1_S0_CURR_ADDR
+#define MDMA_S0_IRQ_STATUS MDMA1_S0_IRQ_STATUS
+#define MDMA_S0_PERIPHERAL_MAP MDMA1_S0_PERIPHERAL_MAP
+#define MDMA_S0_CURR_X_COUNT MDMA1_S0_CURR_X_COUNT
+#define MDMA_S0_CURR_Y_COUNT MDMA1_S0_CURR_Y_COUNT
+
+#define MDMA_D1_NEXT_DESC_PTR MDMA1_D1_NEXT_DESC_PTR
+#define MDMA_D1_START_ADDR MDMA1_D1_START_ADDR
+#define MDMA_D1_CONFIG MDMA1_D1_CONFIG
+#define MDMA_D1_X_COUNT MDMA1_D1_X_COUNT
+#define MDMA_D1_X_MODIFY MDMA1_D1_X_MODIFY
+#define MDMA_D1_Y_COUNT MDMA1_D1_Y_COUNT
+#define MDMA_D1_Y_MODIFY MDMA1_D1_Y_MODIFY
+#define MDMA_D1_CURR_DESC_PTR MDMA1_D1_CURR_DESC_PTR
+#define MDMA_D1_CURR_ADDR MDMA1_D1_CURR_ADDR
+#define MDMA_D1_IRQ_STATUS MDMA1_D1_IRQ_STATUS
+#define MDMA_D1_PERIPHERAL_MAP MDMA1_D1_PERIPHERAL_MAP
+#define MDMA_D1_CURR_X_COUNT MDMA1_D1_CURR_X_COUNT
+#define MDMA_D1_CURR_Y_COUNT MDMA1_D1_CURR_Y_COUNT
+
+#define MDMA_S1_NEXT_DESC_PTR MDMA1_S1_NEXT_DESC_PTR
+#define MDMA_S1_START_ADDR MDMA1_S1_START_ADDR
+#define MDMA_S1_CONFIG MDMA1_S1_CONFIG
+#define MDMA_S1_X_COUNT MDMA1_S1_X_COUNT
+#define MDMA_S1_X_MODIFY MDMA1_S1_X_MODIFY
+#define MDMA_S1_Y_COUNT MDMA1_S1_Y_COUNT
+#define MDMA_S1_Y_MODIFY MDMA1_S1_Y_MODIFY
+#define MDMA_S1_CURR_DESC_PTR MDMA1_S1_CURR_DESC_PTR
+#define MDMA_S1_CURR_ADDR MDMA1_S1_CURR_ADDR
+#define MDMA_S1_IRQ_STATUS MDMA1_S1_IRQ_STATUS
+#define MDMA_S1_PERIPHERAL_MAP MDMA1_S1_PERIPHERAL_MAP
+#define MDMA_S1_CURR_X_COUNT MDMA1_S1_CURR_X_COUNT
+#define MDMA_S1_CURR_Y_COUNT MDMA1_S1_CURR_Y_COUNT
+
 /* Internal Memory DMA Registers (0xFFC0_1800 - 0xFFC0_19FF) */
 #define IMDMA_D0_CONFIG 0xFFC01808     /*IMDMA Stream 0 Destination Configuration */
 #define IMDMA_D0_NEXT_DESC_PTR 0xFFC01800      /*IMDMA Stream 0 Destination Next Descriptor Ptr Reg */
index 9b27e698c0b2e717bd97ff8033bb630112097750..8c10701c251f4f4ffd4962a2172b323146904465 100644 (file)
@@ -133,9 +133,9 @@ void __init platform_request_ipi(irq_handler_t handler)
        int ret;
 
        ret = request_irq(IRQ_SUPPLE_0, handler, IRQF_DISABLED,
-                         "SMP interrupt", handler);
+                         "Supplemental Interrupt0", handler);
        if (ret)
-               panic("Cannot request supplemental interrupt 0 for IPI service\n");
+               panic("Cannot request supplemental interrupt 0 for IPI service");
 }
 
 void platform_send_ipi(cpumask_t callmap)
index 80d39b2f9db295d2709574664528a9c71b6ac1db..da93d92071659cc6e70ea8be3f17b7587b247733 100644 (file)
 #if ANOMALY_05000448
 # error You are using a part with anomaly 05000448, this issue causes random memory read/write failures - that means random crashes.
 #endif
+
+/* if 220 exists, can not set External Memory WB and L2 not_cached, either External Memory not_cached and L2 WB */
+#if ANOMALY_05000220 && \
+       ((defined(CONFIG_BFIN_WB) && defined(CONFIG_BFIN_L2_NOT_CACHED)) || \
+        (!defined(CONFIG_BFIN_DCACHE) && defined(CONFIG_BFIN_L2_WB)))
+# error You are exposing Anomaly 220 in this config, either config L2 as Write Through, or make External Memory WB.
+#endif
index aa0648c6a9feb19111d2b8e60e96dfef12bc9340..d9666fe6c3d6e4847db784ae6d3f2ab1bb14af01 100644 (file)
 
 .text
 
+/* 05000443 - IFLUSH cannot be last instruction in hardware loop */
+#if ANOMALY_05000443
+# define BROK_FLUSH_INST "IFLUSH"
+#else
+# define BROK_FLUSH_INST "no anomaly! yeah!"
+#endif
+
 /* Since all L1 caches work the same way, we use the same method for flushing
  * them.  Only the actual flush instruction differs.  We write this in asm as
  * GCC can be hard to coax into writing nice hardware loops.
@@ -23,7 +30,7 @@
  * R0 = start address
  * R1 = end address
  */
-.macro do_flush flushins:req optflushins optnopins label
+.macro do_flush flushins:req label
 
        R2 = -L1_CACHE_BYTES;
 
 \label :
 .endif
        P0 = R0;
+
        LSETUP (1f, 2f) LC1 = P1;
 1:
-.ifnb \optflushins
-       \optflushins [P0];
-.endif
-#if ANOMALY_05000443
-.ifb \optnopins
-2:
-.endif
+.ifeqs "\flushins", BROK_FLUSH_INST
        \flushins [P0++];
-.ifnb \optnopins
-2:     \optnopins;
-.endif
-#else
+2:     nop;
+.else
 2:     \flushins [P0++];
-#endif
+.endif
 
        RTS;
 .endm
@@ -77,25 +77,9 @@ ENTRY(_blackfin_icache_flush_range)
  */
        P0 = R0;
        IFLUSH[P0];
-       do_flush IFLUSH, , nop
+       do_flush IFLUSH
 ENDPROC(_blackfin_icache_flush_range)
 
-/* Flush all cache lines assocoiated with this area of memory. */
-ENTRY(_blackfin_icache_dcache_flush_range)
-/*
- * Walkaround to avoid loading wrong instruction after invalidating icache
- * and following sequence is met.
- *
- * 1) One instruction address is cached in the instruction cache.
- * 2) This instruction in SDRAM is changed.
- * 3) IFLASH[P0] is executed only once in blackfin_icache_flush_range().
- * 4) This instruction is executed again, but the old one is loaded.
- */
-       P0 = R0;
-       IFLUSH[P0];
-       do_flush FLUSH, IFLUSH
-ENDPROC(_blackfin_icache_dcache_flush_range)
-
 /* Throw away all D-cached data in specified region without any obligation to
  * write them back.  Since the Blackfin ISA does not have an "invalidate"
  * instruction, we use flush/invalidate.  Perhaps as a speed optimization we
@@ -107,7 +91,7 @@ ENDPROC(_blackfin_dcache_invalidate_range)
 
 /* Flush all data cache lines assocoiated with this memory area */
 ENTRY(_blackfin_dcache_flush_range)
-       do_flush FLUSH, , , .Ldfr
+       do_flush FLUSH, .Ldfr
 ENDPROC(_blackfin_dcache_flush_range)
 
 /* Our headers convert the page structure to an address, so just need to flush
index 35393651359bfc004d996154fb7d8d2497e45b5b..ef6870e9eea6d37c4771cfbd73906d85ffe03dec 100644 (file)
@@ -72,6 +72,7 @@ void init_clocks(void)
 #endif
        bfin_write_PLL_LOCKCNT(0x300);
        do_sync();
+       /* We always write PLL_CTL thus avoiding Anomaly 05000242 */
        bfin_write16(PLL_CTL, PLL_CTL_VAL);
        __asm__ __volatile__("IDLE;");
        bfin_write_PLL_DIV(CONFIG_CCLK_ACT_DIV | CONFIG_SCLK_DIV);
index 72e16605ca09f61f41bb3d8d3302ad6123d08d8b..70e3411f558c189b7f2f28ef67c3a5e706fa8857 100644 (file)
@@ -140,7 +140,8 @@ static int __init __bfin_cpu_init(struct cpufreq_policy *policy)
        cclk = get_cclk() / 1000;
        sclk = get_sclk() / 1000;
 
-#if ANOMALY_05000273 || (!defined(CONFIG_BF54x) && defined(CONFIG_BFIN_DCACHE))
+#if ANOMALY_05000273 || ANOMALY_05000274 || \
+       (!defined(CONFIG_BF54x) && defined(CONFIG_BFIN_DCACHE))
        min_cclk = sclk * 2;
 #else
        min_cclk = sclk;
index a063a434f7e35e5235f343a570a8ef60ded304a1..da0558ad1b1a7495764d2b375de4de409e7df992 100644 (file)
@@ -36,7 +36,6 @@
 #include <linux/init.h>
 #include <linux/linkage.h>
 #include <linux/unistd.h>
-#include <linux/threads.h>
 #include <asm/blackfin.h>
 #include <asm/errno.h>
 #include <asm/fixed_code.h>
@@ -201,7 +200,18 @@ ENTRY(_ex_single_step)
        cc = r7 == 0;
        if !cc jump 1f;
 #endif
-
+#ifdef CONFIG_EXACT_HWERR
+       /* Read the ILAT, and to check to see if the process we are
+        * single stepping caused a previous hardware error
+        * If so, do not single step, (which lowers to IRQ5, and makes
+        * us miss the error).
+        */
+       p5.l = lo(ILAT);
+       p5.h = hi(ILAT);
+       r7 = [p5];
+       cc = bittst(r7, EVT_IVHW_P);
+       if cc jump 1f;
+#endif
        /* Single stepping only a single instruction, so clear the trace
         * bit here.  */
        r7 = syscfg;
@@ -263,15 +273,6 @@ ENTRY(_bfin_return_from_exception)
        r6 = 0x25;
        CC = R7 == R6;
        if CC JUMP _double_fault;
-
-       /* Did we cause a HW error? */
-       p5.l = lo(ILAT);
-       p5.h = hi(ILAT);
-       r6 = [p5];
-       r7 = 0x20;              /* Did I just cause anther HW error? */
-       r6 = r7 & r6;
-       CC = R7 == R6;
-       if CC JUMP _double_fault;
 #endif
 
        (R7:6,P5:4) = [sp++];
@@ -473,6 +474,16 @@ ENTRY(_trap) /* Exception: 4th entry into system event table(supervisor mode)*/
        [--sp] = ASTAT;
        [--sp] = (R7:6,P5:4);
 
+#ifdef CONFIG_EXACT_HWERR
+       /* Make sure all pending read/writes complete. This will ensure any
+        * accesses which could cause hardware errors completes, and signal
+        * the the hardware before we do something silly, like crash the
+        * kernel. We don't need to work around anomaly 05000312, since
+        * we are already atomic
+        */
+       ssync;
+#endif
+
 #if ANOMALY_05000283 || ANOMALY_05000315
        cc = r7 == r7;
        p5.h = HI(CHIPID);
@@ -855,7 +866,7 @@ ENTRY(_ret_from_exception)
        p1.h = _schedule_and_signal;
        [p0] = p1;
        csync;
-       raise 15;               /* raise evt14 to do signal or reschedule */
+       raise 15;               /* raise evt15 to do signal or reschedule */
 4:
        r0 = syscfg;
        bitclr(r0, 0);
@@ -916,7 +927,7 @@ ENTRY(_return_from_int)
        p1.h = _schedule_and_signal_from_int;
        [p0] = p1;
        csync;
-#if ANOMALY_05000281
+#if ANOMALY_05000281 || ANOMALY_05000461
        r0.l = lo(SAFE_USER_INSTRUCTION);
        r0.h = hi(SAFE_USER_INSTRUCTION);
        reti = r0;
@@ -930,18 +941,27 @@ ENTRY(_return_from_int)
 ENDPROC(_return_from_int)
 
 ENTRY(_lower_to_irq14)
-#if ANOMALY_05000281
+#if ANOMALY_05000281 || ANOMALY_05000461
        r0.l = lo(SAFE_USER_INSTRUCTION);
        r0.h = hi(SAFE_USER_INSTRUCTION);
        reti = r0;
 #endif
-       r0 = 0x401f;
+
+#ifdef CONFIG_DEBUG_HWERR
+       /* enable irq14 & hwerr interrupt, until we transition to _evt14_softirq */
+       r0 = (EVT_IVG14 | EVT_IVHW | EVT_IRPTEN | EVT_EVX | EVT_NMI | EVT_RST | EVT_EMU);
+#else
+       /* Only enable irq14 interrupt, until we transition to _evt14_softirq */
+       r0 = (EVT_IVG14 | EVT_IRPTEN | EVT_EVX | EVT_NMI | EVT_RST | EVT_EMU);
+#endif
        sti r0;
        raise 14;
        rti;
+ENDPROC(_lower_to_irq14)
+
 ENTRY(_evt14_softirq)
 #ifdef CONFIG_DEBUG_HWERR
-       r0 = 0x3f;
+       r0 = (EVT_IVHW | EVT_IRPTEN | EVT_EVX | EVT_NMI | EVT_RST | EVT_EMU);
        sti r0;
 #else
        cli r0;
@@ -949,8 +969,9 @@ ENTRY(_evt14_softirq)
        [--sp] = RETI;
        SP += 4;
        rts;
+ENDPROC(_evt14_softirq)
 
-_schedule_and_signal_from_int:
+ENTRY(_schedule_and_signal_from_int)
        /* To end up here, vector 15 was changed - so we have to change it
         * back.
         */
@@ -983,8 +1004,9 @@ _schedule_and_signal_from_int:
        call _finish_atomic_sections;
        sp += 12;
        jump.s .Lresume_userspace;
+ENDPROC(_schedule_and_signal_from_int)
 
-_schedule_and_signal:
+ENTRY(_schedule_and_signal)
        SAVE_CONTEXT_SYSCALL
        /* To end up here, vector 15 was changed - so we have to change it
         * back.
@@ -1002,7 +1024,7 @@ _schedule_and_signal:
 1:
        RESTORE_CONTEXT
        rti;
-ENDPROC(_lower_to_irq14)
+ENDPROC(_schedule_and_signal)
 
 /* We handle this 100% in exception space - to reduce overhead
  * Only potiential problem is if the software buffer gets swapped out of the
@@ -1588,19 +1610,3 @@ ENTRY(_sys_call_table)
        .long _sys_ni_syscall
        .endr
 END(_sys_call_table)
-
-#ifdef CONFIG_EXCEPTION_L1_SCRATCH
-/* .section .l1.bss.scratch */
-.set _exception_stack_top, L1_SCRATCH_START + L1_SCRATCH_LENGTH
-#else
-#ifdef CONFIG_SYSCALL_TAB_L1
-.section .l1.bss
-#else
-.bss
-#endif
-ENTRY(_exception_stack)
-       .rept 1024 * NR_CPUS
-       .long 0
-       .endr
-_exception_stack_top:
-#endif
index 698d4c05947e8ba48a21320344a8fc3f93162c00..f826f6b9f917e168e31ef84604549424745015c7 100644 (file)
@@ -30,8 +30,6 @@ ENTRY(__init_clear_bss)
        rts;
 ENDPROC(__init_clear_bss)
 
-#define INITIAL_STACK  (L1_SCRATCH_START + L1_SCRATCH_LENGTH - 12)
-
 ENTRY(__start)
        /* R0: argument of command line string, passed from uboot, save it */
        R7 = R0;
@@ -126,30 +124,30 @@ ENTRY(__start)
         * below
         */
        GET_PDA(p0, r0);
-       r7 = [p0 + PDA_RETX];
+       r6 = [p0 + PDA_RETX];
        p1.l = _init_saved_retx;
        p1.h = _init_saved_retx;
-       [p1] = r7;
+       [p1] = r6;
 
-       r7 = [p0 + PDA_DCPLB];
+       r6 = [p0 + PDA_DCPLB];
        p1.l = _init_saved_dcplb_fault_addr;
        p1.h = _init_saved_dcplb_fault_addr;
-       [p1] = r7;
+       [p1] = r6;
 
-       r7 = [p0 + PDA_ICPLB];
+       r6 = [p0 + PDA_ICPLB];
        p1.l = _init_saved_icplb_fault_addr;
        p1.h = _init_saved_icplb_fault_addr;
-       [p1] = r7;
+       [p1] = r6;
 
-       r7 = [p0 + PDA_SEQSTAT];
+       r6 = [p0 + PDA_SEQSTAT];
        p1.l = _init_saved_seqstat;
        p1.h = _init_saved_seqstat;
-       [p1] = r7;
+       [p1] = r6;
 #endif
 
        /* Initialize stack pointer */
-       sp.l = lo(INITIAL_STACK);
-       sp.h = hi(INITIAL_STACK);
+       sp.l = _init_thread_union;
+       sp.h = _init_thread_union;
        fp = sp;
        usp = sp;
 
@@ -189,7 +187,15 @@ ENTRY(__start)
        /* Put The Code for PLL Programming and SDRAM Programming in L1 ISRAM */
        call _bfin_relocate_l1_mem;
 #ifdef CONFIG_BFIN_KERNEL_CLOCK
+       /* Only use on-chip scratch space for stack when absolutely required
+        * to avoid Anomaly 05000227 ... we know the init_clocks() func only
+        * uses L1 text and stack space and no other memory region.
+        */
+# define KERNEL_CLOCK_STACK (L1_SCRATCH_START + L1_SCRATCH_LENGTH - 12)
+       sp.l = lo(KERNEL_CLOCK_STACK);
+       sp.h = hi(KERNEL_CLOCK_STACK);
        call _init_clocks;
+       sp = usp;       /* usp hasnt been touched, so restore from there */
 #endif
 
        /* This section keeps the processor in supervisor mode
@@ -243,9 +249,7 @@ ENTRY(_real_start)
        call _cmdline_init;
 
        /* Load the current thread pointer and stack */
-       sp.l = _init_thread_union;
-       sp.h = _init_thread_union;
-       p1 = THREAD_SIZE (z);
+       p1 = THREAD_SIZE + 4 (z);       /* +4 is for reti loading */
        sp = sp + p1;
        usp = sp;
        fp = sp;
index 0069c2dd462520db2a1abb7ad4c54108efcc5203..9c46680186e4de2cab991257451d2f96c50e3dde 100644 (file)
@@ -145,6 +145,14 @@ __common_int_entry:
 
 /* interrupt routine for ivhw - 5 */
 ENTRY(_evt_ivhw)
+       /* In case a single action kicks off multiple memory transactions, (like
+        * a cache line fetch, - this can cause multiple hardware errors, let's
+        * catch them all. First - make sure all the actions are complete, and
+        * the core sees the hardware errors.
+        */
+       SSYNC;
+       SSYNC;
+
        SAVE_ALL_SYS
 #ifdef CONFIG_FRAME_POINTER
        fp = 0;
@@ -159,6 +167,25 @@ ENTRY(_evt_ivhw)
 1:
 #endif
 
+       /* Handle all stacked hardware errors
+        * To make sure we don't hang forever, only do it 10 times
+        */
+       R0 = 0;
+       R2 = 10;
+1:
+       P0.L = LO(ILAT);
+       P0.H = HI(ILAT);
+       R1 = [P0];
+       CC = BITTST(R1, EVT_IVHW_P);
+       IF ! CC JUMP 2f;
+       /* OK a hardware error is pending - clear it */
+       R1 = EVT_IVHW_P;
+       [P0] = R1;
+       R0 += 1;
+       CC = R1 == R2;
+       if CC JUMP 2f;
+       JUMP 1b;
+2:
        # We are going to dump something out, so make sure we print IPEND properly
        p2.l = lo(IPEND);
        p2.h = hi(IPEND);
index a7d7b2dd4059a7f5685cec710bb9409174c31589..351afd0e36d8f8552dc4d2b85f37e0c49d4523a3 100644 (file)
@@ -1052,7 +1052,7 @@ int __init init_arch_irq(void)
                        set_irq_chained_handler(irq, bfin_demux_error_irq);
                        break;
 #endif
-#if defined(CONFIG_TICK_SOURCE_SYSTMR0) || defined(CONFIG_IPIPE)
+#if defined(CONFIG_TICKSOURCE_GPTMR0)
                case IRQ_TIMER0:
                        set_irq_handler(irq, handle_percpu_irq);
                        break;
@@ -1116,6 +1116,9 @@ int __init init_arch_irq(void)
            IMASK_IVG14 | IMASK_IVG13 | IMASK_IVG12 | IMASK_IVG11 |
            IMASK_IVG10 | IMASK_IVG9 | IMASK_IVG8 | IMASK_IVG7 | IMASK_IVGHW;
 
+       /* This implicitly covers ANOMALY_05000171
+        * Boot-ROM code modifies SICA_IWRx wakeup registers
+        */
 #ifdef SIC_IWR0
        bfin_write_SIC_IWR0(IWR_DISABLE_ALL);
 # ifdef SIC_IWR1
@@ -1136,13 +1139,6 @@ int __init init_arch_irq(void)
        bfin_write_SIC_IWR(IWR_DISABLE_ALL);
 #endif
 
-#ifdef CONFIG_IPIPE
-       for (irq = 0; irq < NR_IRQS; irq++) {
-               struct irq_desc *desc = irq_to_desc(irq);
-               desc->ic_prio = __ipipe_get_irq_priority(irq);
-       }
-#endif /* CONFIG_IPIPE */
-
        return 0;
 }
 
@@ -1156,23 +1152,22 @@ void do_irq(int vec, struct pt_regs *fp)
        } else {
                struct ivgx *ivg = ivg7_13[vec - IVG7].ifirst;
                struct ivgx *ivg_stop = ivg7_13[vec - IVG7].istop;
-#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) \
-       || defined(BF538_FAMILY) || defined(CONFIG_BF51x)
+#if defined(SIC_ISR0) || defined(SICA_ISR0)
                unsigned long sic_status[3];
 
                if (smp_processor_id()) {
-#ifdef CONFIG_SMP
+# ifdef SICB_ISR0
                        /* This will be optimized out in UP mode. */
                        sic_status[0] = bfin_read_SICB_ISR0() & bfin_read_SICB_IMASK0();
                        sic_status[1] = bfin_read_SICB_ISR1() & bfin_read_SICB_IMASK1();
-#endif
+# endif
                } else {
                        sic_status[0] = bfin_read_SIC_ISR0() & bfin_read_SIC_IMASK0();
                        sic_status[1] = bfin_read_SIC_ISR1() & bfin_read_SIC_IMASK1();
                }
-#ifdef CONFIG_BF54x
+# ifdef SIC_ISR2
                sic_status[2] = bfin_read_SIC_ISR2() & bfin_read_SIC_IMASK2();
-#endif
+# endif
                for (;; ivg++) {
                        if (ivg >= ivg_stop) {
                                atomic_inc(&num_spurious);
@@ -1236,20 +1231,16 @@ asmlinkage int __ipipe_grab_irq(int vec, struct pt_regs *regs)
 
        if (likely(vec == EVT_IVTMR_P)) {
                irq = IRQ_CORETMR;
-               goto core_tick;
-       }
 
-       SSYNC();
-
-#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561)
-       {
+       } else {
+#if defined(SIC_ISR0) || defined(SICA_ISR0)
                unsigned long sic_status[3];
 
                sic_status[0] = bfin_read_SIC_ISR0() & bfin_read_SIC_IMASK0();
                sic_status[1] = bfin_read_SIC_ISR1() & bfin_read_SIC_IMASK1();
-#ifdef CONFIG_BF54x
+# ifdef SIC_ISR2
                sic_status[2] = bfin_read_SIC_ISR2() & bfin_read_SIC_IMASK2();
-#endif
+# endif
                for (;; ivg++) {
                        if (ivg >= ivg_stop) {
                                atomic_inc(&num_spurious);
@@ -1258,9 +1249,7 @@ asmlinkage int __ipipe_grab_irq(int vec, struct pt_regs *regs)
                        if (sic_status[(ivg->irqno - IVG7) / 32] & ivg->isrflag)
                                break;
                }
-       }
 #else
-       {
                unsigned long sic_status;
 
                sic_status = bfin_read_SIC_IMASK() & bfin_read_SIC_ISR();
@@ -1272,15 +1261,13 @@ asmlinkage int __ipipe_grab_irq(int vec, struct pt_regs *regs)
                        } else if (sic_status & ivg->isrflag)
                                break;
                }
-       }
 #endif
 
-       irq = ivg->irqno;
+               irq = ivg->irqno;
+       }
 
        if (irq == IRQ_SYSTMR) {
-#ifdef CONFIG_GENERIC_CLOCKEVENTS
-core_tick:
-#else
+#ifndef CONFIG_GENERIC_CLOCKEVENTS
                bfin_write_TIMER_STATUS(1); /* Latch TIMIL0 */
 #endif
                /* This is basically what we need from the register frame. */
@@ -1292,9 +1279,6 @@ core_tick:
                        __raw_get_cpu_var(__ipipe_tick_regs).ipend |= 0x10;
        }
 
-#ifndef CONFIG_GENERIC_CLOCKEVENTS
-core_tick:
-#endif
        if (this_domain == ipipe_root_domain) {
                s = __test_and_set_bit(IPIPE_SYNCDEFER_FLAG, &p->status);
                barrier();
@@ -1312,7 +1296,7 @@ core_tick:
                }
        }
 
-       return 0;
+       return 0;
 }
 
 #endif /* CONFIG_IPIPE */
index 93eab61460792b8a10d08f7ce33ee3d1d9f1d853..3b8ebaee77f2d1dd38adbaa5a2a6f0eac036a5db 100644 (file)
 #include <asm/processor.h>
 #include <asm/ptrace.h>
 #include <asm/cpu.h>
+#include <asm/time.h>
 #include <linux/err.h>
 
+/*
+ * Anomaly notes:
+ * 05000120 - we always define corelock as 32-bit integer in L2
+ */
 struct corelock_slot corelock __attribute__ ((__section__(".l2.bss")));
 
 void __cpuinitdata *init_retx_coreb, *init_saved_retx_coreb,
@@ -352,7 +357,7 @@ int __cpuinit __cpu_up(unsigned int cpu)
 
 static void __cpuinit setup_secondary(unsigned int cpu)
 {
-#if !(defined(CONFIG_TICK_SOURCE_SYSTMR0) || defined(CONFIG_IPIPE))
+#if !defined(CONFIG_TICKSOURCE_GPTMR0)
        struct irq_desc *timer_desc;
 #endif
        unsigned long ilat;
@@ -364,16 +369,13 @@ static void __cpuinit setup_secondary(unsigned int cpu)
        bfin_write_ILAT(ilat);
        CSYNC();
 
-       /* Reserve the PDA space for the secondary CPU. */
-       reserve_pda();
-
        /* Enable interrupt levels IVG7-15. IARs have been already
         * programmed by the boot CPU.  */
        bfin_irq_flags |= IMASK_IVG15 |
            IMASK_IVG14 | IMASK_IVG13 | IMASK_IVG12 | IMASK_IVG11 |
            IMASK_IVG10 | IMASK_IVG9 | IMASK_IVG8 | IMASK_IVG7 | IMASK_IVGHW;
 
-#if defined(CONFIG_TICK_SOURCE_SYSTMR0) || defined(CONFIG_IPIPE)
+#if defined(CONFIG_TICKSOURCE_GPTMR0)
        /* Power down the core timer, just to play safe. */
        bfin_write_TCNTL(0);
 
index 8cb0945563f9eda946c5ac274af431c58786c921..bc0062884fde3388337a30a0c0bf4b833e04430a 100644 (file)
@@ -30,7 +30,6 @@
 #ifndef __BLACKFIN_SRAM_H__
 #define __BLACKFIN_SRAM_H__
 
-extern void bfin_sram_init(void);
 extern void *l1sram_alloc(size_t);
 
 #endif
index 9c3629b9a689420edb42a34e3d7611c9650fbe57..014a55abd09a4999731871efd6e095ad6951f26c 100644 (file)
@@ -52,9 +52,14 @@ static unsigned long empty_bad_page_table;
 
 static unsigned long empty_bad_page;
 
-unsigned long empty_zero_page;
+static unsigned long empty_zero_page;
 
-extern unsigned long exception_stack[NR_CPUS][1024];
+#ifndef CONFIG_EXCEPTION_L1_SCRATCH
+#if defined CONFIG_SYSCALL_TAB_L1
+__attribute__((l1_data))
+#endif
+static unsigned long exception_stack[NR_CPUS][1024];
+#endif
 
 struct blackfin_pda cpu_pda[NR_CPUS];
 EXPORT_SYMBOL(cpu_pda);
@@ -117,19 +122,18 @@ asmlinkage void __init init_pda(void)
        cpu_pda[0].next = &cpu_pda[1];
        cpu_pda[1].next = &cpu_pda[0];
 
+#ifdef CONFIG_EXCEPTION_L1_SCRATCH
+       cpu_pda[cpu].ex_stack = (unsigned long *)(L1_SCRATCH_START + \
+                                       L1_SCRATCH_LENGTH);
+#else
        cpu_pda[cpu].ex_stack = exception_stack[cpu + 1];
+#endif
 
 #ifdef CONFIG_SMP
        cpu_pda[cpu].imask = 0x1f;
 #endif
 }
 
-void __cpuinit reserve_pda(void)
-{
-       printk(KERN_INFO "PDA for CPU%u reserved at %p\n", smp_processor_id(),
-                                       &cpu_pda[smp_processor_id()]);
-}
-
 void __init mem_init(void)
 {
        unsigned int codek = 0, datak = 0, initk = 0;
@@ -171,19 +175,6 @@ void __init mem_init(void)
                initk, codek, datak, DMA_UNCACHED_REGION >> 10, (reservedpages << (PAGE_SHIFT-10)));
 }
 
-static int __init sram_init(void)
-{
-       /* Initialize the blackfin L1 Memory. */
-       bfin_sram_init();
-
-       /* Reserve the PDA space for the boot CPU right after we
-        * initialized the scratch memory allocator.
-        */
-       reserve_pda();
-       return 0;
-}
-pure_initcall(sram_init);
-
 static void __init free_init_pages(const char *what, unsigned long begin, unsigned long end)
 {
        unsigned long addr;
index 22913e7a1818169a4909c2403da1f185858eae67..c080e70f98b0c579eafbb7d07365daaa2f8d484c 100644 (file)
@@ -125,7 +125,7 @@ static bool isram_check_addr(const void *addr, size_t n)
 {
        if ((addr >= (void *)L1_CODE_START) &&
            (addr < (void *)(L1_CODE_START + L1_CODE_LENGTH))) {
-               if ((addr + n) >= (void *)(L1_CODE_START + L1_CODE_LENGTH)) {
+               if ((addr + n) > (void *)(L1_CODE_START + L1_CODE_LENGTH)) {
                        show_stack(NULL, NULL);
                        printk(KERN_ERR "isram_memcpy: copy involving %p length "
                                        "(%zu) too long\n", addr, n);
index 530d1393a23226926c80ef833dd6b32230405214..0bc3c4ef0aad54664610b7713a7a9f9696845890 100644 (file)
@@ -83,6 +83,14 @@ static struct kmem_cache *sram_piece_cache;
 static void __init l1sram_init(void)
 {
        unsigned int cpu;
+       unsigned long reserve;
+
+#ifdef CONFIG_SMP
+       reserve = 0;
+#else
+       reserve = sizeof(struct l1_scratch_task_info);
+#endif
+
        for (cpu = 0; cpu < num_possible_cpus(); ++cpu) {
                per_cpu(free_l1_ssram_head, cpu).next =
                        kmem_cache_alloc(sram_piece_cache, GFP_KERNEL);
@@ -91,8 +99,8 @@ static void __init l1sram_init(void)
                        return;
                }
 
-               per_cpu(free_l1_ssram_head, cpu).next->paddr = (void *)get_l1_scratch_start_cpu(cpu);
-               per_cpu(free_l1_ssram_head, cpu).next->size = L1_SCRATCH_LENGTH;
+               per_cpu(free_l1_ssram_head, cpu).next->paddr = (void *)get_l1_scratch_start_cpu(cpu) + reserve;
+               per_cpu(free_l1_ssram_head, cpu).next->size = L1_SCRATCH_LENGTH - reserve;
                per_cpu(free_l1_ssram_head, cpu).next->pid = 0;
                per_cpu(free_l1_ssram_head, cpu).next->next = NULL;
 
@@ -223,7 +231,7 @@ static void __init l2_sram_init(void)
        spin_lock_init(&l2_sram_lock);
 }
 
-void __init bfin_sram_init(void)
+static int __init bfin_sram_init(void)
 {
        sram_piece_cache = kmem_cache_create("sram_piece_cache",
                                sizeof(struct sram_piece),
@@ -233,7 +241,10 @@ void __init bfin_sram_init(void)
        l1_data_sram_init();
        l1_inst_sram_init();
        l2_sram_init();
+
+       return 0;
 }
+pure_initcall(bfin_sram_init);
 
 /* SRAM allocate function */
 static void *_sram_alloc(size_t size, struct sram_piece *pfree_head,
@@ -732,6 +743,10 @@ found:
 }
 EXPORT_SYMBOL(sram_free_with_lsl);
 
+/* Allocate memory and keep in L1 SRAM List (lsl) so that the resources are
+ * tracked.  These are designed for userspace so that when a process exits,
+ * we can safely reap their resources.
+ */
 void *sram_alloc_with_lsl(size_t size, unsigned long flags)
 {
        void *addr = NULL;
index 5718dd8902a11d0953f77c03d75db4306c861073..a6aca819e9f334050d6927146fa0a2941901c1e6 100644 (file)
@@ -158,5 +158,5 @@ static inline int atomic_add_unless(atomic_t *v, int a, int u)
 #define smp_mb__before_atomic_inc()    barrier()
 #define smp_mb__after_atomic_inc()     barrier()
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 #endif
diff --git a/arch/cris/include/asm/bitsperlong.h b/arch/cris/include/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..6dc0bb0
--- /dev/null
@@ -0,0 +1 @@
+#include <asm-generic/bitsperlong.h>
index 1c35e1b66b46e206a24d5b99a65a13eedcf44ad3..b7f0afba3ce052c515972d9c28d17ec264057243 100644 (file)
@@ -3,7 +3,7 @@
 
 /* verbatim copy of asm-i386/ version */
 
-#include <asm-generic/mman.h>
+#include <asm-generic/mman-common.h>
 
 #define MAP_GROWSDOWN  0x0100          /* stack-like segment */
 #define MAP_DENYWRITE  0x0800          /* ETXTBSY */
index f3fdbd09c34cfe0b7cd62d50487f4460ac668977..be45ee366be922f43dd0a4b6e4e1f8dcc9aa3d25 100644 (file)
@@ -68,7 +68,7 @@ typedef struct page *pgtable_t;
                                 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
 
 #include <asm-generic/memory_model.h>
-#include <asm-generic/page.h>
+#include <asm-generic/getorder.h>
 
 #endif /* _CRIS_PAGE_H */
 
index 349ae682b568625083fd2dcbf3f8e157a4251ab6..ea6af9aad76c25c92d255c842ea95f0aeba27fbe 100644 (file)
@@ -106,7 +106,7 @@ typedef unsigned long sigset_t;
 #define MINSIGSTKSZ    2048
 #define SIGSTKSZ       8192
 
-#include <asm-generic/signal.h>
+#include <asm-generic/signal-defs.h>
 
 #ifdef __KERNEL__
 struct old_sigaction {
index a187833febc8b0cfd582a39b84aaefdabe54a205..abc13e368b909540df0c66206f7e95ad42d07745 100644 (file)
@@ -48,8 +48,6 @@ void *module_alloc(unsigned long size)
 void module_free(struct module *mod, void *module_region)
 {
        FREE_MODULE(module_region);
-       /* FIXME: If module_region == mod->init_region, trim exception
-          table entries. */
 }
 
 /* We don't need anything special. */
index 296c35cfb207a5a9d4342f5bfb9bd7baee7f9519..0409d981fd39501820618036f8667957ef7f205d 100644 (file)
@@ -194,5 +194,5 @@ static __inline__ int atomic_add_unless(atomic_t *v, int a, int u)
 
 #define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 #endif /* _ASM_ATOMIC_H */
diff --git a/arch/frv/include/asm/bitsperlong.h b/arch/frv/include/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..6dc0bb0
--- /dev/null
@@ -0,0 +1 @@
+#include <asm-generic/bitsperlong.h>
index b4371e9286836ff5f3d84da2c6ddf4becc6ce049..58c1d11e2ac71fe225f7e2a05137d80e53dbc32a 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef __ASM_MMAN_H__
 #define __ASM_MMAN_H__
 
-#include <asm-generic/mman.h>
+#include <asm-generic/mman-common.h>
 
 #define MAP_GROWSDOWN  0x0100          /* stack-like segment */
 #define MAP_DENYWRITE  0x0800          /* ETXTBSY */
index bd9c220094c74b288a03dc958f91e1b977dcb57c..25c6a5002355da190f88b90c5d8954b912453a6f 100644 (file)
@@ -73,6 +73,6 @@ extern unsigned long max_pfn;
 #endif /* __ASSEMBLY__ */
 
 #include <asm-generic/memory_model.h>
-#include <asm-generic/page.h>
+#include <asm-generic/getorder.h>
 
 #endif /* _ASM_PAGE_H */
index cc685e60b0f9ce92c06372cd695346a3b3ba6b17..492b5c4dfed606d3004faf808b7a7270c3c94d0d 100644 (file)
@@ -10,8 +10,8 @@
  * 2 of the License, or (at your option) any later version.
  */
 
-#ifndef ASM_PCI_H
-#define        ASM_PCI_H
+#ifndef _ASM_FRV_PCI_H
+#define _ASM_FRV_PCI_H
 
 #include <linux/mm.h>
 #include <asm/scatterlist.h>
@@ -43,12 +43,6 @@ extern void pci_free_consistent(struct pci_dev *hwdev, size_t size,
 /* Return the index of the PCI controller for device PDEV. */
 #define pci_controller_num(PDEV)       (0)
 
-/* The PCI address space does equal the physical memory
- * address space.  The networking and block device layers use
- * this boolean for bounce buffer decisions.
- */
-#define PCI_DMA_BUS_IS_PHYS    (1)
-
 /* pci_unmap_{page,single} is a nop so... */
 #define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
 #define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
@@ -111,5 +105,4 @@ static inline void pci_dma_sync_sg(struct pci_dev *hwdev,
                                    sg_dma_address(&sg[i])+sg_dma_len(&sg[i]));
 }
 
-
-#endif
+#endif /* _ASM_FRV_PCI_H */
index a62fb58723758faeef174533a1e867356f0da697..b4868aafe79c3c99e58254ef21f673e822fcd345 100644 (file)
@@ -52,7 +52,7 @@ struct termio {
 /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
 
 #ifdef __KERNEL__
-#include <asm-generic/termios.h>
+#include <asm-generic/termios-base.h>
 #endif
 
 #endif /* _ASM_TERMIOS_H */
index 850d168f69fc553b9cfc51ae3abe6122067ac8da..711763c8a6f3a76c5901d43f0744e09aa94d6e6c 100644 (file)
@@ -35,8 +35,6 @@ void *module_alloc(unsigned long size)
 void module_free(struct module *mod, void *module_region)
 {
        vfree(module_region);
-       /* FIXME: If module_region == mod->init_region, trim exception
-           table entries. */
 }
 
 /* We don't need anything special. */
index 833186c8dc3b9cab79f0e801feb602ee952a098c..33c8c0fa9583e0c2794cf0950006d824da961778 100644 (file)
@@ -141,5 +141,5 @@ static __inline__ void atomic_set_mask(unsigned long mask, unsigned long *v)
 #define smp_mb__before_atomic_inc()    barrier()
 #define smp_mb__after_atomic_inc() barrier()
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 #endif /* __ARCH_H8300_ATOMIC __ */
diff --git a/arch/h8300/include/asm/bitsperlong.h b/arch/h8300/include/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..6dc0bb0
--- /dev/null
@@ -0,0 +1 @@
+#include <asm-generic/bitsperlong.h>
index b9f104f22a36a96b976758baca4215e47955b9df..cf35f0a6f12eee77729b51551f7a87bb3aeba477 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef __H8300_MMAN_H__
 #define __H8300_MMAN_H__
 
-#include <asm-generic/mman.h>
+#include <asm-generic/mman-common.h>
 
 #define MAP_GROWSDOWN  0x0100          /* stack-like segment */
 #define MAP_DENYWRITE  0x0800          /* ETXTBSY */
index 0b6acf0b03aa823a884cd8a861666ef9b1a75ecd..837381a2df464d74fb1d156ea65f2215f6835e4f 100644 (file)
@@ -73,6 +73,6 @@ extern unsigned long memory_end;
 #endif /* __ASSEMBLY__ */
 
 #include <asm-generic/memory_model.h>
-#include <asm-generic/page.h>
+#include <asm-generic/getorder.h>
 
 #endif /* _H8300_PAGE_H */
index 7bc15048a64f810ad4a0fee4475cd31c1ba4c975..fd8b66e40dcaa41c726500c66237e2b4b87cc92a 100644 (file)
@@ -105,7 +105,7 @@ typedef unsigned long sigset_t;
 #define MINSIGSTKSZ    2048
 #define SIGSTKSZ       8192
 
-#include <asm-generic/signal.h>
+#include <asm-generic/signal-defs.h>
 
 #ifdef __KERNEL__
 struct old_sigaction {
index cfc9127d2cede26312fcaef364a35d6be0c95080..0865e291c20d2948c95edc70f52925121409599d 100644 (file)
@@ -23,8 +23,6 @@ void *module_alloc(unsigned long size)
 void module_free(struct module *mod, void *module_region)
 {
        vfree(module_region);
-       /* FIXME: If module_region == mod->init_region, trim exception
-           table entries. */
 }
 
 /* We don't need anything special. */
index d37292bd98757402f2f075689bb0e04cf254cda6..88405cb0832ae88e48f2770e5d8af797d519a346 100644 (file)
@@ -216,5 +216,5 @@ atomic64_add_negative (__s64 i, atomic64_t *v)
 #define smp_mb__before_atomic_inc()    barrier()
 #define smp_mb__after_atomic_inc()     barrier()
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 #endif /* _ASM_IA64_ATOMIC_H */
diff --git a/arch/ia64/include/asm/bitsperlong.h b/arch/ia64/include/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..ec4db3c
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __ASM_IA64_BITSPERLONG_H
+#define __ASM_IA64_BITSPERLONG_H
+
+#define __BITS_PER_LONG 64
+
+#include <asm-generic/bitsperlong.h>
+
+#endif /* __ASM_IA64_BITSPERLONG_H */
index c73b87832a1e56a3b9cae408ed0d6f2f504e8195..48cf8b98a0b47bbb5eb4ff2c58ea837c6e56e623 100644 (file)
@@ -8,7 +8,7 @@
  *     David Mosberger-Tang <davidm@hpl.hp.com>, Hewlett-Packard Co
  */
 
-#include <asm-generic/mman.h>
+#include <asm-generic/mman-common.h>
 
 #define MAP_GROWSDOWN  0x00100         /* stack-like segment */
 #define MAP_GROWSUP    0x00200         /* register stack-like segment */
index 4f5ca5643cb12553430f9b013e0096695567582e..b166248d49a46b884b425dd530b4dd5fd0d96ad9 100644 (file)
 
 #endif /* __KERNEL__ */
 
-#include <asm-generic/signal.h>
+#include <asm-generic/signal-defs.h>
 
 # ifndef __ASSEMBLY__
 
diff --git a/arch/ia64/include/asm/suspend.h b/arch/ia64/include/asm/suspend.h
deleted file mode 100644 (file)
index b05bbb6..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/* dummy (must be non-empty to prevent prejudicial removal...) */
index e36b3716e71851493137eddc7a04702f4bc578d5..fbf1ed3b44ced9ea3701fd59c38d1b61957adca7 100644 (file)
 # define __IA64_UL(x)          (x)
 # define __IA64_UL_CONST(x)    x
 
-# ifdef __KERNEL__
-#  define BITS_PER_LONG 64
-# endif
-
 #else
 # define __IA64_UL(x)          ((unsigned long)(x))
 # define __IA64_UL_CONST(x)    x##UL
@@ -34,10 +30,7 @@ typedef unsigned int umode_t;
  */
 # ifdef __KERNEL__
 
-#define BITS_PER_LONG 64
-
 /* DMA addresses are 64-bits wide, in general.  */
-
 typedef u64 dma_addr_t;
 
 # endif /* __KERNEL__ */
index 71c50dd8f8706da65a0362dc2ee8e15888c42060..e95d5ad9285dab575aa10f70e398c1c8368e0316 100644 (file)
@@ -53,6 +53,32 @@ void sort_extable (struct exception_table_entry *start,
             cmp_ex, swap_ex);
 }
 
+static inline unsigned long ex_to_addr(const struct exception_table_entry *x)
+{
+       return (unsigned long)&x->insn + x->insn;
+}
+
+#ifdef CONFIG_MODULES
+/*
+ * Any entry referring to the module init will be at the beginning or
+ * the end.
+ */
+void trim_init_extable(struct module *m)
+{
+       /*trim the beginning*/
+       while (m->num_exentries &&
+              within_module_init(ex_to_addr(&m->extable[0]), m)) {
+               m->extable++;
+               m->num_exentries--;
+       }
+       /*trim the end*/
+       while (m->num_exentries &&
+              within_module_init(ex_to_addr(&m->extable[m->num_exentries-1]),
+                                 m))
+               m->num_exentries--;
+}
+#endif /* CONFIG_MODULES */
+
 const struct exception_table_entry *
 search_extable (const struct exception_table_entry *first,
                const struct exception_table_entry *last,
index 2eed30f84080c66d3c102338cbb38bc70461cbc9..63f0cf0f50dde3949725305e42fa6472971c6f45 100644 (file)
@@ -314,5 +314,5 @@ static __inline__ void atomic_set_mask(unsigned long  mask, atomic_t *addr)
 #define smp_mb__before_atomic_inc()    barrier()
 #define smp_mb__after_atomic_inc()     barrier()
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 #endif /* _ASM_M32R_ATOMIC_H */
diff --git a/arch/m32r/include/asm/bitsperlong.h b/arch/m32r/include/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..6dc0bb0
--- /dev/null
@@ -0,0 +1 @@
+#include <asm-generic/bitsperlong.h>
index 516a8973b1302c177643915c35ee248f5631e987..04a5f40aa401c988b9799e7362c99db07f2189e5 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef __M32R_MMAN_H__
 #define __M32R_MMAN_H__
 
-#include <asm-generic/mman.h>
+#include <asm-generic/mman-common.h>
 
 #define MAP_GROWSDOWN  0x0100          /* stack-like segment */
 #define MAP_DENYWRITE  0x0800          /* ETXTBSY */
index c9333089fe111e5b0e4878bb4c6cd063d6b9271f..11777f7a5628e7a7324f298ba41cfb01aa10118d 100644 (file)
@@ -82,6 +82,6 @@ typedef struct page *pgtable_t;
 #define devmem_is_allowed(x) 1
 
 #include <asm-generic/memory_model.h>
-#include <asm-generic/page.h>
+#include <asm-generic/getorder.h>
 
 #endif /* _ASM_M32R_PAGE_H */
index fe785d167db6142d2af839368874bca5665254ec..07d3834c6dec106645851972be2c171917e1694a 100644 (file)
@@ -3,6 +3,4 @@
 
 #include <asm-generic/pci.h>
 
-#define PCI_DMA_BUS_IS_PHYS    (1)
-
 #endif /* _ASM_M32R_PCI_H */
index 1a607066bc645ce0a6767702640438930402ebd3..9c1acb2b1a928984c8f62ce4a5cd91a25329dc21 100644 (file)
@@ -107,7 +107,7 @@ typedef unsigned long sigset_t;
 #define MINSIGSTKSZ    2048
 #define SIGSTKSZ       8192
 
-#include <asm-generic/signal.h>
+#include <asm-generic/signal-defs.h>
 
 #ifdef __KERNEL__
 struct old_sigaction {
index 8d4205794380f93612f9dc62974e8895427f5912..cb5f37d78d490889c0fc80e747d54d35c97c263a 100644 (file)
@@ -44,8 +44,6 @@ void *module_alloc(unsigned long size)
 void module_free(struct module *mod, void *module_region)
 {
        vfree(module_region);
-       /* FIXME: If module_region == mod->init_region, trim exception
-           table entries. */
 }
 
 /* We don't need anything special. */
index eb0ab9d4ee77e8f083a6861c7fdbfa1798f37f0b..88b7af20a9960c59f42258d017fe6b16ef79b5ce 100644 (file)
@@ -192,5 +192,5 @@ static __inline__ int atomic_add_unless(atomic_t *v, int a, int u)
 #define smp_mb__before_atomic_inc()    barrier()
 #define smp_mb__after_atomic_inc()     barrier()
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 #endif /* __ARCH_M68K_ATOMIC __ */
index 6bb674855a3f57867ddd6d829eb8ca3e953f9bc9..5674cb9449bd81278fa67e319a0b66abca0efd39 100644 (file)
@@ -151,5 +151,5 @@ static __inline__ int atomic_add_unless(atomic_t *v, int a, int u)
 #define atomic_dec_return(v) atomic_sub_return(1,(v))
 #define atomic_inc_return(v) atomic_add_return(1,(v))
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 #endif /* __ARCH_M68KNOMMU_ATOMIC __ */
diff --git a/arch/m68k/include/asm/bitsperlong.h b/arch/m68k/include/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..6dc0bb0
--- /dev/null
@@ -0,0 +1 @@
+#include <asm-generic/bitsperlong.h>
index 1626d37f48980c1fce8179051790323b557c5a70..9f5c4c4b3c7b20d37a4184e6d76a6888358d4510 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef __M68K_MMAN_H__
 #define __M68K_MMAN_H__
 
-#include <asm-generic/mman.h>
+#include <asm-generic/mman-common.h>
 
 #define MAP_GROWSDOWN  0x0100          /* stack-like segment */
 #define MAP_DENYWRITE  0x0800          /* ETXTBSY */
index a34b8bad784703b1597c5e15e5f655e3a05a7a3e..d009f3ea39abdaf133e06b122a86e4053b44636f 100644 (file)
@@ -223,6 +223,6 @@ static inline __attribute_const__ int __virt_to_node_shift(void)
 #define VM_DATA_DEFAULT_FLAGS  (VM_READ | VM_WRITE | VM_EXEC | \
                                 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
 
-#include <asm-generic/page.h>
+#include <asm-generic/getorder.h>
 
 #endif /* _M68K_PAGE_H */
index 3a1ede4544cba4815e357a23daafbb07189f59f1..9aa3f90f4855b85ade95e2977cf9422e397d32f6 100644 (file)
@@ -72,6 +72,6 @@ extern unsigned long memory_end;
 
 #endif /* __ASSEMBLY__ */
 
-#include <asm-generic/page.h>
+#include <asm-generic/getorder.h>
 
 #endif /* _M68KNOMMU_PAGE_H */
index 08788fdefde0c66cb330145fd95b319131330c4b..5bc09c787a1175a05850bac9fec1d739e192bb3e 100644 (file)
@@ -103,7 +103,7 @@ typedef unsigned long sigset_t;
 #define MINSIGSTKSZ    2048
 #define SIGSTKSZ       8192
 
-#include <asm-generic/signal.h>
+#include <asm-generic/signal-defs.h>
 
 #ifdef __KERNEL__
 struct old_sigaction {
diff --git a/arch/m68k/include/asm/suspend.h b/arch/m68k/include/asm/suspend.h
deleted file mode 100644 (file)
index 57b3ddb..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _M68K_SUSPEND_H
-#define _M68K_SUSPEND_H
-
-/* Dummy include. */
-
-#endif  /* _M68K_SUSPEND_H */
index 774862bc6977297c0099c4bdfa96980820dcf1df..cd6bcb1c957e925c55583a0249961f09e395b74d 100644 (file)
@@ -31,8 +31,6 @@ void *module_alloc(unsigned long size)
 void module_free(struct module *mod, void *module_region)
 {
        vfree(module_region);
-       /* FIXME: If module_region == mod->init_region, trim exception
-           table entries. */
 }
 
 /* We don't need anything special. */
index 3b1a2ff61ddcf18846fc108859e964a6d28110fd..d11ffae7956a5d31fbbc02251aff1fff073f3b0a 100644 (file)
@@ -23,8 +23,6 @@ void *module_alloc(unsigned long size)
 void module_free(struct module *mod, void *module_region)
 {
        vfree(module_region);
-       /* FIXME: If module_region == mod->init_region, trim exception
-           table entries. */
 }
 
 /* We don't need anything special. */
index 8cc312b5d4dce12b0f591bb9b1063b7b33827bd0..b50b845fdd506d6f6e914038794a854769301bfc 100644 (file)
@@ -6,6 +6,7 @@ mainmenu "Linux/Microblaze Kernel Configuration"
 config MICROBLAZE
        def_bool y
        select HAVE_LMB
+       select ARCH_WANT_OPTIONAL_GPIOLIB
 
 config SWAP
        def_bool n
@@ -49,13 +50,14 @@ config GENERIC_CLOCKEVENTS
 config GENERIC_HARDIRQS_NO__DO_IRQ
        def_bool y
 
+config GENERIC_GPIO
+       def_bool y
+
 config PCI
-       depends on !MMU
        def_bool n
 
 config NO_DMA
-       depends on !MMU
-       def_bool n
+       def_bool y
 
 source "init/Kconfig"
 
@@ -72,7 +74,8 @@ source "kernel/Kconfig.preempt"
 source "kernel/Kconfig.hz"
 
 config MMU
-       def_bool n
+       bool "MMU support"
+       default n
 
 config NO_MMU
        bool
@@ -105,9 +108,6 @@ config CMDLINE_FORCE
 config OF
        def_bool y
 
-config OF_DEVICE
-       def_bool y
-
 config PROC_DEVICETREE
        bool "Support for device tree in /proc"
        depends on PROC_FS
@@ -118,6 +118,113 @@ config PROC_DEVICETREE
 
 endmenu
 
+menu "Advanced setup"
+
+config ADVANCED_OPTIONS
+       bool "Prompt for advanced kernel configuration options"
+       depends on MMU
+       help
+         This option will enable prompting for a variety of advanced kernel
+         configuration options.  These options can cause the kernel to not
+         work if they are set incorrectly, but can be used to optimize certain
+         aspects of kernel memory management.
+
+         Unless you know what you are doing, say N here.
+
+comment "Default settings for advanced configuration options are used"
+       depends on !ADVANCED_OPTIONS
+
+config HIGHMEM_START_BOOL
+       bool "Set high memory pool address"
+       depends on ADVANCED_OPTIONS && HIGHMEM
+       help
+         This option allows you to set the base address of the kernel virtual
+         area used to map high memory pages.  This can be useful in
+         optimizing the layout of kernel virtual memory.
+
+         Say N here unless you know what you are doing.
+
+config HIGHMEM_START
+       hex "Virtual start address of high memory pool" if HIGHMEM_START_BOOL
+       depends on MMU
+       default "0xfe000000"
+
+config LOWMEM_SIZE_BOOL
+       bool "Set maximum low memory"
+       depends on ADVANCED_OPTIONS
+       help
+         This option allows you to set the maximum amount of memory which
+         will be used as "low memory", that is, memory which the kernel can
+         access directly, without having to set up a kernel virtual mapping.
+         This can be useful in optimizing the layout of kernel virtual
+         memory.
+
+         Say N here unless you know what you are doing.
+
+config LOWMEM_SIZE
+       hex "Maximum low memory size (in bytes)" if LOWMEM_SIZE_BOOL
+       depends on MMU
+       default "0x30000000"
+
+config KERNEL_START_BOOL
+       bool "Set custom kernel base address"
+       depends on ADVANCED_OPTIONS
+       help
+         This option allows you to set the kernel virtual address at which
+         the kernel will map low memory (the kernel image will be linked at
+         this address).  This can be useful in optimizing the virtual memory
+         layout of the system.
+
+         Say N here unless you know what you are doing.
+
+config KERNEL_START
+       hex "Virtual address of kernel base" if KERNEL_START_BOOL
+       default "0xc0000000" if MMU
+       default KERNEL_BASE_ADDR if !MMU
+
+config TASK_SIZE_BOOL
+       bool "Set custom user task size"
+       depends on ADVANCED_OPTIONS
+       help
+         This option allows you to set the amount of virtual address space
+         allocated to user tasks.  This can be useful in optimizing the
+         virtual memory layout of the system.
+
+         Say N here unless you know what you are doing.
+
+config TASK_SIZE
+       hex "Size of user task space" if TASK_SIZE_BOOL
+       depends on MMU
+       default "0x80000000"
+
+config CONSISTENT_START_BOOL
+       bool "Set custom consistent memory pool address"
+       depends on ADVANCED_OPTIONS && NOT_COHERENT_CACHE
+       help
+         This option allows you to set the base virtual address
+         of the the consistent memory pool.  This pool of virtual
+         memory is used to make consistent memory allocations.
+
+config CONSISTENT_START
+       hex "Base virtual address of consistent memory pool" if CONSISTENT_START_BOOL
+       depends on MMU
+       default "0xff100000" if NOT_COHERENT_CACHE
+
+config CONSISTENT_SIZE_BOOL
+       bool "Set custom consistent memory pool size"
+       depends on ADVANCED_OPTIONS && NOT_COHERENT_CACHE
+       help
+         This option allows you to set the size of the the
+         consistent memory pool.  This pool of virtual memory
+         is used to make consistent memory allocations.
+
+config CONSISTENT_SIZE
+       hex "Size of consistent memory pool" if CONSISTENT_SIZE_BOOL
+       depends on MMU
+       default "0x00200000" if NOT_COHERENT_CACHE
+
+endmenu
+
 source "mm/Kconfig"
 
 menu "Exectuable file formats"
index aaadfa701da30b82475583bde4db5c3bea9cc116..d0bcf80a113659d2b3da42e9cd0e7c4c58f701a9 100644 (file)
@@ -1,4 +1,8 @@
+ifeq ($(CONFIG_MMU),y)
+UTS_SYSNAME = -DUTS_SYSNAME=\"Linux\"
+else
 UTS_SYSNAME = -DUTS_SYSNAME=\"uClinux\"
+endif
 
 # What CPU vesion are we building for, and crack it open
 # as major.minor.rev
@@ -36,6 +40,8 @@ CPUFLAGS-1 += $(call cc-option,-mcpu=v$(CPU_VER))
 # r31 holds current when in kernel mode
 CFLAGS_KERNEL += -ffixed-r31 $(CPUFLAGS-1) $(CPUFLAGS-2)
 
+LDFLAGS                :=
+LDFLAGS_vmlinux        :=
 LDFLAGS_BLOB := --format binary --oformat elf32-microblaze
 
 LIBGCC := $(shell $(CC) $(CFLAGS_KERNEL) -print-libgcc-file-name)
index 844edf406d34cb6b901fdc807c02e371bb1148a5..c2bb043a029d2df7891d17ae6e92e46e0360657b 100644 (file)
@@ -7,6 +7,8 @@ targets := linux.bin linux.bin.gz
 OBJCOPYFLAGS_linux.bin  := -O binary
 
 $(obj)/linux.bin: vmlinux FORCE
+       [ -n $(CONFIG_INITRAMFS_SOURCE) ] && [ ! -e $(CONFIG_INITRAMFS_SOURCE) ] && \
+       touch $(CONFIG_INITRAMFS_SOURCE) || echo "No CPIO image"
        $(call if_changed,objcopy)
        @echo 'Kernel: $@ is ready' ' (#'`cat .version`')'
 
diff --git a/arch/microblaze/configs/mmu_defconfig b/arch/microblaze/configs/mmu_defconfig
new file mode 100644 (file)
index 0000000..bd0b85e
--- /dev/null
@@ -0,0 +1,798 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.30-rc6
+# Fri May 22 10:02:33 2009
+#
+CONFIG_MICROBLAZE=y
+# CONFIG_SWAP is not set
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_FIND_NEXT_BIT=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_GENERIC_TIME=y
+# CONFIG_GENERIC_TIME_VSYSCALL is not set
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_CLASSIC_RCU=y
+# CONFIG_TREE_RCU is not set
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_PREEMPT_RCU_TRACE is not set
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=17
+# CONFIG_GROUP_SCHED is not set
+# CONFIG_CGROUPS is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+# CONFIG_NAMESPACES is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE="rootfs.cpio"
+CONFIG_INITRAMFS_ROOT_UID=0
+CONFIG_INITRAMFS_ROOT_GID=0
+CONFIG_RD_GZIP=y
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
+CONFIG_INITRAMFS_COMPRESSION_NONE=y
+# CONFIG_INITRAMFS_COMPRESSION_GZIP is not set
+# CONFIG_INITRAMFS_COMPRESSION_BZIP2 is not set
+# CONFIG_INITRAMFS_COMPRESSION_LZMA is not set
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+CONFIG_EMBEDDED=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_KALLSYMS_EXTRA_PASS=y
+# CONFIG_STRIP_ASM_SYMS is not set
+# CONFIG_HOTPLUG is not set
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+# CONFIG_BASE_FULL is not set
+# CONFIG_FUTEX is not set
+# CONFIG_EPOLL is not set
+# CONFIG_SIGNALFD is not set
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+# CONFIG_SHMEM is not set
+CONFIG_AIO=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_COMPAT_BRK=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+# CONFIG_SLOW_WORK is not set
+# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
+CONFIG_SLABINFO=y
+CONFIG_BASE_SMALL=1
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+# CONFIG_FREEZER is not set
+
+#
+# Platform options
+#
+CONFIG_PLATFORM_GENERIC=y
+CONFIG_OPT_LIB_FUNCTION=y
+CONFIG_OPT_LIB_ASM=y
+CONFIG_ALLOW_EDIT_AUTO=y
+
+#
+# Automatic platform settings from Kconfig.auto
+#
+
+#
+# Definitions for MICROBLAZE0
+#
+CONFIG_KERNEL_BASE_ADDR=0x90000000
+CONFIG_XILINX_MICROBLAZE0_FAMILY="virtex5"
+CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR=1
+CONFIG_XILINX_MICROBLAZE0_USE_PCMP_INSTR=1
+CONFIG_XILINX_MICROBLAZE0_USE_BARREL=1
+CONFIG_XILINX_MICROBLAZE0_USE_DIV=1
+CONFIG_XILINX_MICROBLAZE0_USE_HW_MUL=2
+CONFIG_XILINX_MICROBLAZE0_USE_FPU=2
+CONFIG_XILINX_MICROBLAZE0_HW_VER="7.10.d"
+
+#
+# Processor type and features
+#
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_HZ_100=y
+# CONFIG_HZ_250 is not set
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=100
+# CONFIG_SCHED_HRTICK is not set
+CONFIG_MMU=y
+
+#
+# Boot options
+#
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttyUL0,115200"
+CONFIG_CMDLINE_FORCE=y
+CONFIG_OF=y
+CONFIG_PROC_DEVICETREE=y
+
+#
+# Advanced setup
+#
+# CONFIG_ADVANCED_OPTIONS is not set
+
+#
+# Default settings for advanced configuration options are used
+#
+CONFIG_HIGHMEM_START=0xfe000000
+CONFIG_LOWMEM_SIZE=0x30000000
+CONFIG_KERNEL_START=0xc0000000
+CONFIG_TASK_SIZE=0x80000000
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+CONFIG_UNEVICTABLE_LRU=y
+CONFIG_HAVE_MLOCK=y
+CONFIG_HAVE_MLOCKED_PAGE_BIT=y
+
+#
+# Exectuable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_HAVE_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+# CONFIG_IP_PNP 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_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA 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_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_PHONET is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_DCB is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+# CONFIG_WIRELESS is not set
+# CONFIG_WIMAX is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+# CONFIG_MTD is not set
+CONFIG_OF_DEVICE=y
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# 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=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=8192
+# CONFIG_BLK_DEV_XIP is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_XILINX_SYSACE is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_C2PORT is not set
+
+#
+# EEPROM support
+#
+# CONFIG_EEPROM_93CX6 is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+CONFIG_COMPAT_NET_DEV_OPS=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+# CONFIG_PHYLIB is not set
+CONFIG_NET_ETHERNET=y
+# CONFIG_MII is not set
+# CONFIG_ETHOC is not set
+# CONFIG_DNET is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
+# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
+# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
+# CONFIG_B44 is not set
+CONFIG_NETDEV_1000=y
+CONFIG_NETDEV_10000=y
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+# CONFIG_INPUT is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_UARTLITE=y
+CONFIG_SERIAL_UARTLITE_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_RTC is not set
+# CONFIG_GEN_RTC is not set
+# CONFIG_XILINX_HWICAP is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_I2C is not set
+# CONFIG_SPI is not set
+CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
+# CONFIG_GPIOLIB is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_REGULATOR is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+# CONFIG_SOUND is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_NEW_LEDS is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+CONFIG_FILE_LOCKING=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY is not set
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# Caches
+#
+# CONFIG_FSCACHE 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 is not set
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+# CONFIG_NFS_V4 is not set
+# CONFIG_NFSD is not set
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_COMMON=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=y
+CONFIG_CIFS_STATS=y
+CONFIG_CIFS_STATS2=y
+# CONFIG_CIFS_WEAK_PW_HASH is not set
+# CONFIG_CIFS_XATTR is not set
+# CONFIG_CIFS_DEBUG2 is not set
+# CONFIG_CIFS_EXPERIMENTAL is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
+CONFIG_DETECT_HUNG_TASK=y
+# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0
+CONFIG_SCHED_DEBUG=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_TIMER_STATS is not set
+# CONFIG_DEBUG_OBJECTS is not set
+CONFIG_DEBUG_SLAB=y
+# CONFIG_DEBUG_SLAB_LEAK is not set
+CONFIG_DEBUG_SPINLOCK=y
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_INFO=y
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_SG is not set
+# CONFIG_DEBUG_NOTIFIERS is not set
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_SYSCTL_SYSCALL_CHECK is not set
+# CONFIG_PAGE_POISONING is not set
+# CONFIG_SAMPLES is not set
+CONFIG_EARLY_PRINTK=y
+CONFIG_HEART_BEAT=y
+CONFIG_DEBUG_BOOTMEM=y
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+# CONFIG_CRYPTO_FIPS is not set
+# CONFIG_CRYPTO_MANAGER is not set
+# CONFIG_CRYPTO_MANAGER2 is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+# CONFIG_CRYPTO_CBC is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+
+#
+# Compression
+#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_ZLIB is not set
+# CONFIG_CRYPTO_LZO is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_HW=y
+# CONFIG_BINARY_PRINTF is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_FIND_LAST_BIT=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_DECOMPRESS_GZIP=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+CONFIG_HAVE_LMB=y
+CONFIG_NLATTR=y
index 31820dfef56b6a1a69e8b03740f4f2aa59717098..db5294c30cafef1d2cbb8f83b787edb36f812613 100644 (file)
@@ -1,26 +1,3 @@
 include include/asm-generic/Kbuild.asm
 
-header-y += auxvec.h
-header-y += errno.h
-header-y += fcntl.h
-header-y += ioctl.h
-header-y += ioctls.h
-header-y += ipcbuf.h
-header-y += linkage.h
-header-y += msgbuf.h
-header-y += poll.h
-header-y += resource.h
-header-y += sembuf.h
-header-y += shmbuf.h
-header-y += sigcontext.h
-header-y += siginfo.h
-header-y += socket.h
-header-y += sockios.h
-header-y += statfs.h
-header-y += stat.h
-header-y += termbits.h
-header-y += ucontext.h
-
-unifdef-y += cputable.h
-unifdef-y += elf.h
-unifdef-y += termios.h
+header-y  += elf.h
index a448d94ab721802f4d196b72b20fd0b45c52a8df..0de612ad7cb2e5b2ca2ef8f98b4616768ab8d6c2 100644 (file)
@@ -118,6 +118,6 @@ static inline int atomic_dec_if_positive(atomic_t *v)
 #define smp_mb__before_atomic_inc()    barrier()
 #define smp_mb__after_atomic_inc()     barrier()
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 
 #endif /* _ASM_MICROBLAZE_ATOMIC_H */
diff --git a/arch/microblaze/include/asm/bitsperlong.h b/arch/microblaze/include/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..6dc0bb0
--- /dev/null
@@ -0,0 +1 @@
+#include <asm-generic/bitsperlong.h>
index 3300b785049bb8a6bb218ae6496c507c788bdb1e..f989d6aad6482b30f4e8bc3fa33c57a3c67b52b6 100644 (file)
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2007 PetaLogix
+ * Copyright (C) 2007-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2007-2009 PetaLogix
  * Copyright (C) 2007 John Williams <john.williams@petalogix.com>
  * based on v850 version which was
  * Copyright (C) 2001,02,03 NEC Electronics Corporation
 #define flush_icache_range(start, len) __invalidate_icache_range(start, len)
 #define flush_icache_page(vma, pg)             do { } while (0)
 
+#ifndef CONFIG_MMU
+# define flush_icache_user_range(start, len)   do { } while (0)
+#else
+# define flush_icache_user_range(vma, pg, adr, len) __invalidate_icache_all()
+
+# define flush_page_to_ram(page)               do { } while (0)
+
+# define flush_icache()                        __invalidate_icache_all()
+# define flush_cache_sigtramp(vaddr) \
+                       __invalidate_icache_range(vaddr, vaddr + 8)
+
+# define flush_dcache_mmap_lock(mapping)       do { } while (0)
+# define flush_dcache_mmap_unlock(mapping)     do { } while (0)
+
+# define flush_cache_dup_mm(mm)                        do { } while (0)
+#endif
+
 #define flush_cache_vmap(start, end)           do { } while (0)
 #define flush_cache_vunmap(start, end)         do { } while (0)
 
index 92b30762ce59a2e4cf115f65bb8816ba1531c4d2..97ea46b5cf80a2a1071b5b89dc9a85cb2e8e3f28 100644 (file)
@@ -51,7 +51,8 @@ extern __wsum csum_partial(const void *buff, int len, __wsum sum);
  * here even more important to align src and dst on a 32-bit (or even
  * better 64-bit) boundary
  */
-extern __wsum csum_partial_copy(const char *src, char *dst, int len, int sum);
+extern __wsum csum_partial_copy(const void *src, void *dst, int len,
+                                                               __wsum sum);
 
 /*
  * the same as csum_partial_copy, but copies from user space.
@@ -59,8 +60,8 @@ extern __wsum csum_partial_copy(const char *src, char *dst, int len, int sum);
  * here even more important to align src and dst on a 32-bit (or even
  * better 64-bit) boundary
  */
-extern __wsum csum_partial_copy_from_user(const char *src, char *dst,
-                                       int len, int sum, int *csum_err);
+extern __wsum csum_partial_copy_from_user(const void __user *src, void *dst,
+                                       int len, __wsum sum, int *csum_err);
 
 #define csum_partial_copy_nocheck(src, dst, len, sum)  \
        csum_partial_copy((src), (dst), (len), (sum))
@@ -75,11 +76,12 @@ extern __sum16 ip_fast_csum(const void *iph, unsigned int ihl);
 /*
  *     Fold a partial checksum
  */
-static inline __sum16 csum_fold(unsigned int sum)
+static inline __sum16 csum_fold(__wsum csum)
 {
+       u32 sum = (__force u32)csum;
        sum = (sum & 0xffff) + (sum >> 16);
        sum = (sum & 0xffff) + (sum >> 16);
-       return ~sum;
+       return (__force __sum16)~sum;
 }
 
 static inline __sum16
@@ -93,6 +95,6 @@ csum_tcpudp_magic(__be32 saddr, __be32 daddr, unsigned short len,
  * this routine is used for miscellaneous IP-like checksums, mainly
  * in icmp.c
  */
-extern __sum16 ip_compute_csum(const unsigned char *buff, int len);
+extern __sum16 ip_compute_csum(const void *buff, int len);
 
 #endif /* _ASM_MICROBLAZE_CHECKSUM_H */
index 8375ea991e2633900c16d3c63094b4eee5249041..29303ed825cce591770d93e6fb9d7912e4022e42 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2008-2009 PetaLogix
  * Copyright (C) 2006 Atmark Techno, Inc.
  *
  * This file is subject to the terms and conditions of the GNU General Public
@@ -9,6 +11,12 @@
 #ifndef _ASM_MICROBLAZE_CURRENT_H
 #define _ASM_MICROBLAZE_CURRENT_H
 
+/*
+ * Register used to hold the current task pointer while in the kernel.
+ * Any `call clobbered' register without a special meaning should be OK,
+ * but check asm/microblaze/kernel/entry.S to be sure.
+ */
+#define CURRENT_TASK   r31
 # ifndef __ASSEMBLY__
 /*
  * Dedicate r31 to keeping the current task pointer
index 17336252a9b827932be864ba051c46603a8f8f7c..d00e40099165b7075ef8efd35df050f06246fce2 100644 (file)
@@ -1,129 +1 @@
-/*
- * Copyright (C) 2006 Atmark Techno, 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_MICROBLAZE_DMA_MAPPING_H
-#define _ASM_MICROBLAZE_DMA_MAPPING_H
-
-#include <asm/cacheflush.h>
-#include <linux/io.h>
-#include <linux/bug.h>
-
-struct scatterlist;
-
-#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
-#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
-
-/* FIXME */
-static inline int
-dma_supported(struct device *dev, u64 mask)
-{
-       return 1;
-}
-
-static inline dma_addr_t
-dma_map_page(struct device *dev, struct page *page,
-       unsigned long offset, size_t size,
-       enum dma_data_direction direction)
-{
-       BUG();
-       return 0;
-}
-
-static inline void
-dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size,
-       enum dma_data_direction direction)
-{
-       BUG();
-}
-
-static inline int
-dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
-       enum dma_data_direction direction)
-{
-       BUG();
-       return 0;
-}
-
-static inline void
-dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries,
-       enum dma_data_direction direction)
-{
-       BUG();
-}
-
-static inline void
-dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size,
-                       enum dma_data_direction direction)
-{
-       BUG();
-}
-
-static inline void
-dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle,
-               size_t size, enum dma_data_direction direction)
-{
-       BUG();
-}
-
-static inline void
-dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems,
-               enum dma_data_direction direction)
-{
-       BUG();
-}
-
-static inline void
-dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems,
-               enum dma_data_direction direction)
-{
-       BUG();
-}
-
-static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
-{
-       return 0;
-}
-
-static inline void *dma_alloc_coherent(struct device *dev, size_t size,
-                               dma_addr_t *dma_handle, int flag)
-{
-       return NULL; /* consistent_alloc(flag, size, dma_handle); */
-}
-
-static inline void dma_free_coherent(struct device *dev, size_t size,
-                       void *vaddr, dma_addr_t dma_handle)
-{
-       BUG();
-}
-
-static inline dma_addr_t
-dma_map_single(struct device *dev, void *ptr, size_t size,
-       enum dma_data_direction direction)
-{
-       BUG_ON(direction == DMA_NONE);
-
-       return virt_to_bus(ptr);
-}
-
-static inline void dma_unmap_single(struct device *dev, dma_addr_t dma_addr,
-                                   size_t size,
-                                   enum dma_data_direction direction)
-{
-       switch (direction) {
-       case DMA_FROM_DEVICE:
-               flush_dcache_range((unsigned)dma_addr,
-                       (unsigned)dma_addr + size);
-                       /* Fall through */
-       case DMA_TO_DEVICE:
-               break;
-       default:
-               BUG();
-       }
-}
-
-#endif /* _ASM_MICROBLAZE_DMA_MAPPING_H */
+#include <asm-generic/dma-mapping-broken.h>
index 0967fa04fc5eb4e95338bf9e8f2d7a92ab90f8f8..08c073badf198f3296b384742b98a31a1ab9b408 100644 (file)
@@ -9,8 +9,13 @@
 #ifndef _ASM_MICROBLAZE_DMA_H
 #define _ASM_MICROBLAZE_DMA_H
 
+#ifndef CONFIG_MMU
 /* we don't have dma address limit. define it as zero to be
  * unlimited. */
 #define MAX_DMA_ADDRESS                (0)
+#else
+/* Virtual address corresponding to last available physical memory address.  */
+#define MAX_DMA_ADDRESS (CONFIG_KERNEL_START + memory_size - 1)
+#endif
 
 #endif /* _ASM_MICROBLAZE_DMA_H */
index 81337f241347a15ad53f165a95876e0bb2d48054..f92fc0dda006d40b886d36b947bb3891cb83656a 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2008-2009 PetaLogix
  * Copyright (C) 2006 Atmark Techno, Inc.
  *
  * This file is subject to the terms and conditions of the GNU General Public
  */
 #define ELF_CLASS      ELFCLASS32
 
+#ifndef __uClinux__
+
+/*
+ * ELF register definitions..
+ */
+
+#include <asm/ptrace.h>
+#include <asm/byteorder.h>
+
+#ifndef ELF_GREG_T
+#define ELF_GREG_T
+typedef unsigned long elf_greg_t;
+#endif
+
+#ifndef ELF_NGREG
+#define ELF_NGREG (sizeof(struct pt_regs) / sizeof(elf_greg_t))
+#endif
+
+#ifndef ELF_GREGSET_T
+#define ELF_GREGSET_T
+typedef elf_greg_t elf_gregset_t[ELF_NGREG];
+#endif
+
+#ifndef ELF_FPREGSET_T
+#define ELF_FPREGSET_T
+
+/* TBD */
+#define ELF_NFPREG     33      /* includes fsr */
+typedef unsigned long elf_fpreg_t;
+typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
+
+/* typedef struct user_fpu_struct elf_fpregset_t; */
+#endif
+
+/* This is the location that an ET_DYN program is loaded if exec'ed.  Typical
+ * use of this is to invoke "./ld.so someprog" to test out a new version of
+ * the loader.  We need to make sure that it is out of the way of the program
+ * that it will "exec", and that there is sufficient room for the brk.
+ */
+
+#define ELF_ET_DYN_BASE         (0x08000000)
+
+#ifdef __LITTLE_ENDIAN__
+#define ELF_DATA       ELFDATA2LSB
+#else
+#define ELF_DATA       ELFDATA2MSB
+#endif
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE      4096
+
+
+#define ELF_CORE_COPY_REGS(_dest, _regs)                       \
+       memcpy((char *) &_dest, (char *) _regs,         \
+       sizeof(struct pt_regs));
+
+/* This yields a mask that user programs can use to figure out what
+ * instruction set this CPU supports.  This could be done in user space,
+ * but it's not easy, and we've already done it here.
+ */
+#define ELF_HWCAP      (0)
+
+/* This yields a string that ld.so will use to load implementation
+ * specific libraries for optimization.  This is more specific in
+ * intent than poking at uname or /proc/cpuinfo.
+
+ * For the moment, we have only optimizations for the Intel generations,
+ * but that could change...
+ */
+#define ELF_PLATFORM  (NULL)
+
+/* Added _f parameter. Is this definition correct: TBD */
+#define ELF_PLAT_INIT(_r, _f)                          \
+do {                                                   \
+       _r->r1 =  _r->r1 =  _r->r2 =  _r->r3 =          \
+       _r->r4 =  _r->r5 =  _r->r6 =  _r->r7 =          \
+       _r->r8 =  _r->r9 =  _r->r10 = _r->r11 =         \
+       _r->r12 = _r->r13 = _r->r14 = _r->r15 =         \
+       _r->r16 = _r->r17 = _r->r18 = _r->r19 =         \
+       _r->r20 = _r->r21 = _r->r22 = _r->r23 =         \
+       _r->r24 = _r->r25 = _r->r26 = _r->r27 =         \
+       _r->r28 = _r->r29 = _r->r30 = _r->r31 =         \
+       0;                                              \
+} while (0)
+
+#ifdef __KERNEL__
+#define SET_PERSONALITY(ex) set_personality(PER_LINUX_32BIT)
+#endif
+
+#endif /* __uClinux__ */
+
 #endif /* _ASM_MICROBLAZE_ELF_H */
index e4c3aef884df0dacdba060af7d6d95dd3e122460..61abbd232640ff6b99f8a9110c55e47a8f0edf84 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * Definitions used by low-level trap handlers
  *
- * Copyright (C) 2008 Michal Simek
- * Copyright (C) 2007 - 2008 PetaLogix
+ * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2007-2009 PetaLogix
  * Copyright (C) 2007 John Williams <john.williams@petalogix.com>
  *
  * This file is subject to the terms and conditions of the GNU General
@@ -31,7 +31,40 @@ DECLARE_PER_CPU(unsigned int, R11_SAVE); /* Temp variable for entry */
 DECLARE_PER_CPU(unsigned int, CURRENT_SAVE); /* Saved current pointer */
 # endif /* __ASSEMBLY__ */
 
+#ifndef CONFIG_MMU
+
 /* noMMU hasn't any space for args */
 # define STATE_SAVE_ARG_SPACE  (0)
 
+#else /* CONFIG_MMU */
+
+/* If true, system calls save and restore all registers (except result
+ * registers, of course).  If false, then `call clobbered' registers
+ * will not be preserved, on the theory that system calls are basically
+ * function calls anyway, and the caller should be able to deal with it.
+ * This is a security risk, of course, as `internal' values may leak out
+ * after a system call, but that certainly doesn't matter very much for
+ * a processor with no MMU protection!  For a protected-mode kernel, it
+ * would be faster to just zero those registers before returning.
+ *
+ * I can not rely on the glibc implementation. If you turn it off make
+ * sure that r11/r12 is saved in user-space. --KAA
+ *
+ * These are special variables using by the kernel trap/interrupt code
+ * to save registers in, at a time when there are no spare registers we
+ * can use to do so, and we can't depend on the value of the stack
+ * pointer.  This means that they must be within a signed 16-bit
+ * displacement of 0x00000000.
+ */
+
+/* A `state save frame' is a struct pt_regs preceded by some extra space
+ * suitable for a function call stack frame. */
+
+/* Amount of room on the stack reserved for arguments and to satisfy the
+ * C calling conventions, in addition to the space used by the struct
+ * pt_regs that actually holds saved values. */
+#define STATE_SAVE_ARG_SPACE   (6*4) /* Up to six arguments */
+
+#endif /* CONFIG_MMU */
+
 #endif /* _ASM_MICROBLAZE_ENTRY_H */
index 24ca540e77c043dc45e6dba1d389b18a4a441dfb..90731df9e574c30bd8684272be6f11250147997c 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * Preliminary support for HW exception handing for Microblaze
  *
- * Copyright (C) 2008 Michal Simek
- * Copyright (C) 2008 PetaLogix
+ * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2008-2009 PetaLogix
  * Copyright (C) 2005 John Williams <jwilliams@itee.uq.edu.au>
  *
  * This file is subject to the terms and conditions of the GNU General
@@ -64,21 +64,13 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type,
 void die(const char *str, struct pt_regs *fp, long err);
 void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr);
 
-#if defined(CONFIG_XMON)
-extern void xmon(struct pt_regs *regs);
-extern int xmon_bpt(struct pt_regs *regs);
-extern int xmon_sstep(struct pt_regs *regs);
-extern int xmon_iabr_match(struct pt_regs *regs);
-extern int xmon_dabr_match(struct pt_regs *regs);
-extern void (*xmon_fault_handler)(struct pt_regs *regs);
+#ifdef CONFIG_MMU
+void __bug(const char *file, int line, void *data);
+int bad_trap(int trap_num, struct pt_regs *regs);
+int debug_trap(struct pt_regs *regs);
+#endif /* CONFIG_MMU */
 
-void (*debugger)(struct pt_regs *regs) = xmon;
-int (*debugger_bpt)(struct pt_regs *regs) = xmon_bpt;
-int (*debugger_sstep)(struct pt_regs *regs) = xmon_sstep;
-int (*debugger_iabr_match)(struct pt_regs *regs) = xmon_iabr_match;
-int (*debugger_dabr_match)(struct pt_regs *regs) = xmon_dabr_match;
-void (*debugger_fault_handler)(struct pt_regs *regs);
-#elif defined(CONFIG_KGDB)
+#if defined(CONFIG_KGDB)
 void (*debugger)(struct pt_regs *regs);
 int (*debugger_bpt)(struct pt_regs *regs);
 int (*debugger_sstep)(struct pt_regs *regs);
index acf0da543ef140d2dbccc1fe610e8720a48e5939..6847c1512c7baf17f6f58a000566e79631ce3db8 100644 (file)
@@ -13,7 +13,6 @@
 
 #include <asm/unaligned.h>
 
-#define        flat_stack_align(sp) /* nothing needed */
 #define        flat_argvp_envp_on_stack()      0
 #define        flat_old_ram_flag(flags)        (flags)
 #define        flat_reloc_valid(reloc, size)   ((reloc) <= (size))
index ea04632399d8d25f8cf5e4f4720c28bd7dbbaef9..2345ac354d9be3b865e0477c206b46630bca50a1 100644 (file)
@@ -11,8 +11,8 @@
  * (at your option) any later version.
  */
 
-#ifndef __ASM_POWERPC_GPIO_H
-#define __ASM_POWERPC_GPIO_H
+#ifndef _ASM_MICROBLAZE_GPIO_H
+#define _ASM_MICROBLAZE_GPIO_H
 
 #include <linux/errno.h>
 #include <asm-generic/gpio.h>
@@ -53,4 +53,4 @@ static inline int irq_to_gpio(unsigned int irq)
 
 #endif /* CONFIG_GPIOLIB */
 
-#endif /* __ASM_POWERPC_GPIO_H */
+#endif /* _ASM_MICROBLAZE_GPIO_H */
index 8b5853ee6b5c8a9dec3fae1b320a02461e658d27..5c173424d0744bdd6cfdda945e57985adff9060c 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * Copyright (C) 2007-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2007-2009 PetaLogix
  * Copyright (C) 2006 Atmark Techno, Inc.
  *
  * This file is subject to the terms and conditions of the GNU General Public
@@ -12,6 +14,9 @@
 #include <asm/byteorder.h>
 #include <asm/page.h>
 #include <linux/types.h>
+#include <asm/byteorder.h>
+#include <linux/mm.h>          /* Get struct page {...} */
+
 
 #define IO_SPACE_LIMIT (0xFFFFFFFF)
 
@@ -112,6 +117,30 @@ static inline void writel(unsigned int v, volatile void __iomem *addr)
 #define memcpy_fromio(a, b, c) memcpy((a), (void *)(b), (c))
 #define memcpy_toio(a, b, c)   memcpy((void *)(a), (b), (c))
 
+#ifdef CONFIG_MMU
+
+#define mm_ptov(addr)          ((void *)__phys_to_virt(addr))
+#define mm_vtop(addr)          ((unsigned long)__virt_to_phys(addr))
+#define phys_to_virt(addr)     ((void *)__phys_to_virt(addr))
+#define virt_to_phys(addr)     ((unsigned long)__virt_to_phys(addr))
+#define virt_to_bus(addr)      ((unsigned long)__virt_to_phys(addr))
+
+#define __page_address(page) \
+               (PAGE_OFFSET + (((page) - mem_map) << PAGE_SHIFT))
+#define page_to_phys(page)     virt_to_phys((void *)__page_address(page))
+#define page_to_bus(page)      (page_to_phys(page))
+#define bus_to_virt(addr)      (phys_to_virt(addr))
+
+extern void iounmap(void *addr);
+/*extern void *__ioremap(phys_addr_t address, unsigned long size,
+               unsigned long flags);*/
+extern void __iomem *ioremap(phys_addr_t address, unsigned long size);
+#define ioremap_writethrough(addr, size) ioremap((addr), (size))
+#define ioremap_nocache(addr, size)      ioremap((addr), (size))
+#define ioremap_fullcache(addr, size)    ioremap((addr), (size))
+
+#else /* CONFIG_MMU */
+
 /**
  *     virt_to_phys - map virtual addresses to physical
  *     @address: address to remap
@@ -160,6 +189,8 @@ static inline void __iomem *__ioremap(phys_addr_t address, unsigned long size,
 #define iounmap(addr)          ((void)0)
 #define ioremap_nocache(physaddr, size)        ioremap(physaddr, size)
 
+#endif /* CONFIG_MMU */
+
 /*
  * Convert a physical pointer to a virtual kernel pointer for /dev/mem
  * access
index 0e0431d61635ae2dcbe02c93fe390ea0152eb5bd..66cad6a99d77b41a8c5b50f140de1c3daf356121 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2008-2009 PetaLogix
  * Copyright (C) 2006 Atmark Techno, Inc.
  *
  * This file is subject to the terms and conditions of the GNU General Public
 #ifndef _ASM_MICROBLAZE_MMU_H
 #define _ASM_MICROBLAZE_MMU_H
 
-#ifndef __ASSEMBLY__
+# ifndef CONFIG_MMU
+#  ifndef __ASSEMBLY__
 typedef struct {
        struct vm_list_struct   *vmlist;
        unsigned long           end_brk;
 } mm_context_t;
-#endif /* __ASSEMBLY__ */
+#  endif /* __ASSEMBLY__ */
+# else /* CONFIG_MMU */
+#  ifdef __KERNEL__
+#   ifndef __ASSEMBLY__
 
+/* Default "unsigned long" context */
+typedef unsigned long mm_context_t;
+
+/* Hardware Page Table Entry */
+typedef struct _PTE {
+       unsigned long    v:1;   /* Entry is valid */
+       unsigned long vsid:24;  /* Virtual segment identifier */
+       unsigned long    h:1;   /* Hash algorithm indicator */
+       unsigned long  api:6;   /* Abbreviated page index */
+       unsigned long  rpn:20;  /* Real (physical) page number */
+       unsigned long     :3;   /* Unused */
+       unsigned long    r:1;   /* Referenced */
+       unsigned long    c:1;   /* Changed */
+       unsigned long    w:1;   /* Write-thru cache mode */
+       unsigned long    i:1;   /* Cache inhibited */
+       unsigned long    m:1;   /* Memory coherence */
+       unsigned long    g:1;   /* Guarded */
+       unsigned long     :1;   /* Unused */
+       unsigned long   pp:2;   /* Page protection */
+} PTE;
+
+/* Values for PP (assumes Ks=0, Kp=1) */
+#  define PP_RWXX      0 /* Supervisor read/write, User none */
+#  define PP_RWRX      1 /* Supervisor read/write, User read */
+#  define PP_RWRW      2 /* Supervisor read/write, User read/write */
+#  define PP_RXRX      3 /* Supervisor read,       User read */
+
+/* Segment Register */
+typedef struct _SEGREG {
+       unsigned long    t:1;   /* Normal or I/O  type */
+       unsigned long   ks:1;   /* Supervisor 'key' (normally 0) */
+       unsigned long   kp:1;   /* User 'key' (normally 1) */
+       unsigned long    n:1;   /* No-execute */
+       unsigned long     :4;   /* Unused */
+       unsigned long vsid:24;  /* Virtual Segment Identifier */
+} SEGREG;
+
+extern void _tlbie(unsigned long va);  /* invalidate a TLB entry */
+extern void _tlbia(void);              /* invalidate all TLB entries */
+#   endif /* __ASSEMBLY__ */
+
+/*
+ * The MicroBlaze processor has a TLB architecture identical to PPC-40x. The
+ * instruction and data sides share a unified, 64-entry, semi-associative
+ * TLB which is maintained totally under software control. In addition, the
+ * instruction side has a hardware-managed, 2,4, or 8-entry, fully-associative
+ * TLB which serves as a first level to the shared TLB. These two TLBs are
+ * known as the UTLB and ITLB, respectively.
+ */
+
+#  define MICROBLAZE_TLB_SIZE 64
+
+/*
+ * TLB entries are defined by a "high" tag portion and a "low" data
+ * portion. The data portion is 32-bits.
+ *
+ * TLB entries are managed entirely under software control by reading,
+ * writing, and searching using the MTS and MFS instructions.
+ */
+
+#  define TLB_LO               1
+#  define TLB_HI               0
+#  define TLB_DATA             TLB_LO
+#  define TLB_TAG              TLB_HI
+
+/* Tag portion */
+#  define TLB_EPN_MASK         0xFFFFFC00 /* Effective Page Number */
+#  define TLB_PAGESZ_MASK      0x00000380
+#  define TLB_PAGESZ(x)                (((x) & 0x7) << 7)
+#  define PAGESZ_1K            0
+#  define PAGESZ_4K            1
+#  define PAGESZ_16K           2
+#  define PAGESZ_64K           3
+#  define PAGESZ_256K          4
+#  define PAGESZ_1M            5
+#  define PAGESZ_4M            6
+#  define PAGESZ_16M           7
+#  define TLB_VALID            0x00000040 /* Entry is valid */
+
+/* Data portion */
+#  define TLB_RPN_MASK         0xFFFFFC00 /* Real Page Number */
+#  define TLB_PERM_MASK                0x00000300
+#  define TLB_EX               0x00000200 /* Instruction execution allowed */
+#  define TLB_WR               0x00000100 /* Writes permitted */
+#  define TLB_ZSEL_MASK                0x000000F0
+#  define TLB_ZSEL(x)          (((x) & 0xF) << 4)
+#  define TLB_ATTR_MASK                0x0000000F
+#  define TLB_W                        0x00000008 /* Caching is write-through */
+#  define TLB_I                        0x00000004 /* Caching is inhibited */
+#  define TLB_M                        0x00000002 /* Memory is coherent */
+#  define TLB_G                        0x00000001 /* Memory is guarded from prefetch */
+
+#  endif /* __KERNEL__ */
+# endif /* CONFIG_MMU */
 #endif /* _ASM_MICROBLAZE_MMU_H */
index 150ca01b74ba160d929dc5214dc700426a388bbc..385fed16bbfb12eb04281c838037c475fb72c8fa 100644 (file)
@@ -1,21 +1,5 @@
-/*
- * Copyright (C) 2006 Atmark Techno, 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_MICROBLAZE_MMU_CONTEXT_H
-#define _ASM_MICROBLAZE_MMU_CONTEXT_H
-
-# define init_new_context(tsk, mm)             ({ 0; })
-
-# define enter_lazy_tlb(mm, tsk)               do {} while (0)
-# define change_mm_context(old, ctx, _pml4)    do {} while (0)
-# define destroy_context(mm)                   do {} while (0)
-# define deactivate_mm(tsk, mm)                        do {} while (0)
-# define switch_mm(prev, next, tsk)            do {} while (0)
-# define activate_mm(prev, next)               do {} while (0)
-
-#endif /* _ASM_MICROBLAZE_MMU_CONTEXT_H */
+#ifdef CONFIG_MMU
+# include "mmu_context_mm.h"
+#else
+# include "mmu_context_no.h"
+#endif
diff --git a/arch/microblaze/include/asm/mmu_context_mm.h b/arch/microblaze/include/asm/mmu_context_mm.h
new file mode 100644 (file)
index 0000000..3e5c254
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2008-2009 PetaLogix
+ * Copyright (C) 2006 Atmark Techno, 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_MICROBLAZE_MMU_CONTEXT_H
+#define _ASM_MICROBLAZE_MMU_CONTEXT_H
+
+#include <asm/atomic.h>
+#include <asm/bitops.h>
+#include <asm/mmu.h>
+#include <asm-generic/mm_hooks.h>
+
+# ifdef __KERNEL__
+/*
+ * This function defines the mapping from contexts to VSIDs (virtual
+ * segment IDs).  We use a skew on both the context and the high 4 bits
+ * of the 32-bit virtual address (the "effective segment ID") in order
+ * to spread out the entries in the MMU hash table.
+ */
+# define CTX_TO_VSID(ctx, va)  (((ctx) * (897 * 16) + ((va) >> 28) * 0x111) \
+                                & 0xffffff)
+
+/*
+   MicroBlaze has 256 contexts, so we can just rotate through these
+   as a way of "switching" contexts.  If the TID of the TLB is zero,
+   the PID/TID comparison is disabled, so we can use a TID of zero
+   to represent all kernel pages as shared among all contexts.
+ */
+
+static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
+{
+}
+
+# define NO_CONTEXT    256
+# define LAST_CONTEXT  255
+# define FIRST_CONTEXT 1
+
+/*
+ * Set the current MMU context.
+ * This is done byloading up the segment registers for the user part of the
+ * address space.
+ *
+ * Since the PGD is immediately available, it is much faster to simply
+ * pass this along as a second parameter, which is required for 8xx and
+ * can be used for debugging on all processors (if you happen to have
+ * an Abatron).
+ */
+extern void set_context(mm_context_t context, pgd_t *pgd);
+
+/*
+ * Bitmap of contexts in use.
+ * The size of this bitmap is LAST_CONTEXT + 1 bits.
+ */
+extern unsigned long context_map[];
+
+/*
+ * This caches the next context number that we expect to be free.
+ * Its use is an optimization only, we can't rely on this context
+ * number to be free, but it usually will be.
+ */
+extern mm_context_t next_mmu_context;
+
+/*
+ * Since we don't have sufficient contexts to give one to every task
+ * that could be in the system, we need to be able to steal contexts.
+ * These variables support that.
+ */
+extern atomic_t nr_free_contexts;
+extern struct mm_struct *context_mm[LAST_CONTEXT+1];
+extern void steal_context(void);
+
+/*
+ * Get a new mmu context for the address space described by `mm'.
+ */
+static inline void get_mmu_context(struct mm_struct *mm)
+{
+       mm_context_t ctx;
+
+       if (mm->context != NO_CONTEXT)
+               return;
+       while (atomic_dec_if_positive(&nr_free_contexts) < 0)
+               steal_context();
+       ctx = next_mmu_context;
+       while (test_and_set_bit(ctx, context_map)) {
+               ctx = find_next_zero_bit(context_map, LAST_CONTEXT+1, ctx);
+               if (ctx > LAST_CONTEXT)
+                       ctx = 0;
+       }
+       next_mmu_context = (ctx + 1) & LAST_CONTEXT;
+       mm->context = ctx;
+       context_mm[ctx] = mm;
+}
+
+/*
+ * Set up the context for a new address space.
+ */
+# define init_new_context(tsk, mm)     (((mm)->context = NO_CONTEXT), 0)
+
+/*
+ * We're finished using the context for an address space.
+ */
+static inline void destroy_context(struct mm_struct *mm)
+{
+       if (mm->context != NO_CONTEXT) {
+               clear_bit(mm->context, context_map);
+               mm->context = NO_CONTEXT;
+               atomic_inc(&nr_free_contexts);
+       }
+}
+
+static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
+                            struct task_struct *tsk)
+{
+       tsk->thread.pgdir = next->pgd;
+       get_mmu_context(next);
+       set_context(next->context, next->pgd);
+}
+
+/*
+ * After we have set current->mm to a new value, this activates
+ * the context for the new mm so we see the new mappings.
+ */
+static inline void activate_mm(struct mm_struct *active_mm,
+                       struct mm_struct *mm)
+{
+       current->thread.pgdir = mm->pgd;
+       get_mmu_context(mm);
+       set_context(mm->context, mm->pgd);
+}
+
+extern void mmu_context_init(void);
+
+# endif /* __KERNEL__ */
+#endif /* _ASM_MICROBLAZE_MMU_CONTEXT_H */
diff --git a/arch/microblaze/include/asm/mmu_context_no.h b/arch/microblaze/include/asm/mmu_context_no.h
new file mode 100644 (file)
index 0000000..ba55671
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2008-2009 PetaLogix
+ * Copyright (C) 2006 Atmark Techno, 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_MICROBLAZE_MMU_CONTEXT_H
+#define _ASM_MICROBLAZE_MMU_CONTEXT_H
+
+# define init_new_context(tsk, mm)             ({ 0; })
+
+# define enter_lazy_tlb(mm, tsk)               do {} while (0)
+# define change_mm_context(old, ctx, _pml4)    do {} while (0)
+# define destroy_context(mm)                   do {} while (0)
+# define deactivate_mm(tsk, mm)                        do {} while (0)
+# define switch_mm(prev, next, tsk)            do {} while (0)
+# define activate_mm(prev, next)               do {} while (0)
+
+#endif /* _ASM_MICROBLAZE_MMU_CONTEXT_H */
index 7238dcfcc5174af979475f75013608f1f8558158..72aceae88680a2b427253d2941a3f29452f34e26 100644 (file)
@@ -1,6 +1,8 @@
 /*
- * Copyright (C) 2008 Michal Simek
- * Copyright (C) 2008 PetaLogix
+ * VM ops
+ *
+ * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2008-2009 PetaLogix
  * Copyright (C) 2006 Atmark Techno, Inc.
  * Changes for MMU support:
  *    Copyright (C) 2007 Xilinx, Inc.  All rights reserved.
 
 #include <linux/pfn.h>
 #include <asm/setup.h>
+#include <linux/const.h>
+
+#ifdef __KERNEL__
 
 /* PAGE_SHIFT determines the page size */
 #define PAGE_SHIFT     (12)
-#define PAGE_SIZE      (1UL << PAGE_SHIFT)
+#define PAGE_SIZE      (_AC(1, UL) << PAGE_SHIFT)
 #define PAGE_MASK      (~(PAGE_SIZE-1))
 
-#ifdef __KERNEL__
-
 #ifndef __ASSEMBLY__
 
 #define PAGE_UP(addr)  (((addr)+((PAGE_SIZE)-1))&(~((PAGE_SIZE)-1)))
@@ -35,6 +38,7 @@
 /* align addr on a size boundary - adjust address up if needed */
 #define _ALIGN(addr, size)     _ALIGN_UP(addr, size)
 
+#ifndef CONFIG_MMU
 /*
  * PAGE_OFFSET -- the first address of the first page of memory. When not
  * using MMU this corresponds to the first free page in physical memory (aligned
 extern unsigned int __page_offset;
 #define PAGE_OFFSET __page_offset
 
-#define copy_page(to, from)                    memcpy((to), (from), PAGE_SIZE)
-#define get_user_page(vaddr)                   __get_free_page(GFP_KERNEL)
-#define free_user_page(page, addr)             free_page(addr)
+#else /* CONFIG_MMU */
 
-#define clear_page(pgaddr)                     memset((pgaddr), 0, PAGE_SIZE)
+/*
+ * PAGE_OFFSET -- the first address of the first page of memory. With MMU
+ * it is set to the kernel start address (aligned on a page boundary).
+ *
+ * CONFIG_KERNEL_START is defined in arch/microblaze/config.in and used
+ * in arch/microblaze/Makefile.
+ */
+#define PAGE_OFFSET    CONFIG_KERNEL_START
 
+/*
+ * MAP_NR -- given an address, calculate the index of the page struct which
+ * points to the address's page.
+ */
+#define MAP_NR(addr) (((unsigned long)(addr) - PAGE_OFFSET) >> PAGE_SHIFT)
 
-#define clear_user_page(pgaddr, vaddr, page)   memset((pgaddr), 0, PAGE_SIZE)
-#define copy_user_page(vto, vfrom, vaddr, topg) \
+/*
+ * The basic type of a PTE - 32 bit physical addressing.
+ */
+typedef unsigned long pte_basic_t;
+#define PTE_SHIFT      (PAGE_SHIFT - 2)        /* 1024 ptes per page */
+#define PTE_FMT                "%.8lx"
+
+#endif /* CONFIG_MMU */
+
+#  ifndef CONFIG_MMU
+#  define copy_page(to, from)                  memcpy((to), (from), PAGE_SIZE)
+#  define get_user_page(vaddr)                 __get_free_page(GFP_KERNEL)
+#  define free_user_page(page, addr)           free_page(addr)
+#  else /* CONFIG_MMU */
+extern void copy_page(void *to, void *from);
+#  endif /* CONFIG_MMU */
+
+# define clear_page(pgaddr)                    memset((pgaddr), 0, PAGE_SIZE)
+
+# define clear_user_page(pgaddr, vaddr, page)  memset((pgaddr), 0, PAGE_SIZE)
+# define copy_user_page(vto, vfrom, vaddr, topg) \
                        memcpy((vto), (vfrom), PAGE_SIZE)
 
 /*
@@ -60,21 +93,32 @@ extern unsigned int __page_offset;
 typedef struct page *pgtable_t;
 typedef struct { unsigned long pte; }          pte_t;
 typedef struct { unsigned long pgprot; }       pgprot_t;
+/* FIXME this can depend on linux kernel version */
+#   ifdef CONFIG_MMU
+typedef struct { unsigned long pmd; } pmd_t;
+typedef struct { unsigned long pgd; } pgd_t;
+#   else /* CONFIG_MMU */
 typedef struct { unsigned long ste[64]; }      pmd_t;
 typedef struct { pmd_t         pue[1]; }       pud_t;
 typedef struct { pud_t         pge[1]; }       pgd_t;
+#   endif /* CONFIG_MMU */
 
+# define pte_val(x)    ((x).pte)
+# define pgprot_val(x) ((x).pgprot)
 
-#define pte_val(x)     ((x).pte)
-#define pgprot_val(x)  ((x).pgprot)
-#define pmd_val(x)     ((x).ste[0])
-#define pud_val(x)     ((x).pue[0])
-#define pgd_val(x)     ((x).pge[0])
+#   ifdef CONFIG_MMU
+#   define pmd_val(x)      ((x).pmd)
+#   define pgd_val(x)      ((x).pgd)
+#   else  /* CONFIG_MMU */
+#   define pmd_val(x)  ((x).ste[0])
+#   define pud_val(x)  ((x).pue[0])
+#   define pgd_val(x)  ((x).pge[0])
+#   endif  /* CONFIG_MMU */
 
-#define __pte(x)       ((pte_t) { (x) })
-#define __pmd(x)       ((pmd_t) { (x) })
-#define __pgd(x)       ((pgd_t) { (x) })
-#define __pgprot(x)    ((pgprot_t) { (x) })
+# define __pte(x)      ((pte_t) { (x) })
+# define __pmd(x)      ((pmd_t) { (x) })
+# define __pgd(x)      ((pgd_t) { (x) })
+# define __pgprot(x)   ((pgprot_t) { (x) })
 
 /**
  * Conversions for virtual address, physical address, pfn, and struct
@@ -94,47 +138,83 @@ extern unsigned long max_low_pfn;
 extern unsigned long min_low_pfn;
 extern unsigned long max_pfn;
 
-#define __pa(vaddr)            ((unsigned long) (vaddr))
-#define __va(paddr)            ((void *) (paddr))
+extern unsigned long memory_start;
+extern unsigned long memory_end;
+extern unsigned long memory_size;
 
-#define phys_to_pfn(phys)      (PFN_DOWN(phys))
-#define pfn_to_phys(pfn)       (PFN_PHYS(pfn))
+extern int page_is_ram(unsigned long pfn);
 
-#define virt_to_pfn(vaddr)     (phys_to_pfn((__pa(vaddr))))
-#define pfn_to_virt(pfn)       __va(pfn_to_phys((pfn)))
+# define phys_to_pfn(phys)     (PFN_DOWN(phys))
+# define pfn_to_phys(pfn)      (PFN_PHYS(pfn))
 
-#define virt_to_page(vaddr)    (pfn_to_page(virt_to_pfn(vaddr)))
-#define page_to_virt(page)     (pfn_to_virt(page_to_pfn(page)))
+# define virt_to_pfn(vaddr)    (phys_to_pfn((__pa(vaddr))))
+# define pfn_to_virt(pfn)      __va(pfn_to_phys((pfn)))
 
-#define page_to_phys(page)     (pfn_to_phys(page_to_pfn(page)))
-#define page_to_bus(page)      (page_to_phys(page))
-#define phys_to_page(paddr)    (pfn_to_page(phys_to_pfn(paddr)))
+#  ifdef CONFIG_MMU
+#  define virt_to_page(kaddr)  (mem_map +  MAP_NR(kaddr))
+#  else /* CONFIG_MMU */
+#  define virt_to_page(vaddr)  (pfn_to_page(virt_to_pfn(vaddr)))
+#  define page_to_virt(page)   (pfn_to_virt(page_to_pfn(page)))
+#  define page_to_phys(page)   (pfn_to_phys(page_to_pfn(page)))
+#  define page_to_bus(page)    (page_to_phys(page))
+#  define phys_to_page(paddr)  (pfn_to_page(phys_to_pfn(paddr)))
+#  endif /* CONFIG_MMU */
 
-extern unsigned int memory_start;
-extern unsigned int memory_end;
-extern unsigned int memory_size;
+#  ifndef CONFIG_MMU
+#  define pfn_valid(pfn)       ((pfn) >= min_low_pfn && (pfn) <= max_mapnr)
+#  define ARCH_PFN_OFFSET      (PAGE_OFFSET >> PAGE_SHIFT)
+#  else /* CONFIG_MMU */
+#  define ARCH_PFN_OFFSET      (memory_start >> PAGE_SHIFT)
+#  define pfn_valid(pfn)       ((pfn) < (max_mapnr + ARCH_PFN_OFFSET))
+#  define VALID_PAGE(page)     ((page - mem_map) < max_mapnr)
+#  endif /* CONFIG_MMU */
 
-#define pfn_valid(pfn)         ((pfn) >= min_low_pfn && (pfn) < max_mapnr)
+# endif /* __ASSEMBLY__ */
 
-#define ARCH_PFN_OFFSET                (PAGE_OFFSET >> PAGE_SHIFT)
+#define        virt_addr_valid(vaddr)  (pfn_valid(virt_to_pfn(vaddr)))
 
-#else
-#define tophys(rd, rs) (addik rd, rs, 0)
-#define tovirt(rd, rs) (addik rd, rs, 0)
-#endif /* __ASSEMBLY__ */
 
-#define virt_addr_valid(vaddr) (pfn_valid(virt_to_pfn(vaddr)))
+#  ifndef CONFIG_MMU
+#  define __pa(vaddr)  ((unsigned long) (vaddr))
+#  define __va(paddr)  ((void *) (paddr))
+#  else /* CONFIG_MMU */
+#  define __pa(x)      __virt_to_phys((unsigned long)(x))
+#  define __va(x)      ((void *)__phys_to_virt((unsigned long)(x)))
+#  endif /* CONFIG_MMU */
+
 
-/* Convert between virtual and physical address for MMU.  */
-/* Handle MicroBlaze processor with virtual memory.  */
+/* Convert between virtual and physical address for MMU. */
+/* Handle MicroBlaze processor with virtual memory. */
+#ifndef CONFIG_MMU
 #define __virt_to_phys(addr)   addr
 #define __phys_to_virt(addr)   addr
+#define tophys(rd, rs) addik rd, rs, 0
+#define tovirt(rd, rs) addik rd, rs, 0
+#else
+#define __virt_to_phys(addr) \
+       ((addr) + CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START)
+#define __phys_to_virt(addr) \
+       ((addr) + CONFIG_KERNEL_START - CONFIG_KERNEL_BASE_ADDR)
+#define tophys(rd, rs) \
+       addik rd, rs, (CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START)
+#define tovirt(rd, rs) \
+       addik rd, rs, (CONFIG_KERNEL_START - CONFIG_KERNEL_BASE_ADDR)
+#endif /* CONFIG_MMU */
 
 #define TOPHYS(addr)  __virt_to_phys(addr)
 
+#ifdef CONFIG_MMU
+#ifdef CONFIG_CONTIGUOUS_PAGE_ALLOC
+#define WANT_PAGE_VIRTUAL 1 /* page alloc 2 relies on this */
+#endif
+
+#define VM_DATA_DEFAULT_FLAGS  (VM_READ | VM_WRITE | VM_EXEC | \
+                                VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
+#endif /* CONFIG_MMU */
+
 #endif /* __KERNEL__ */
 
 #include <asm-generic/memory_model.h>
-#include <asm-generic/page.h>
+#include <asm-generic/getorder.h>
 
 #endif /* _ASM_MICROBLAZE_PAGE_H */
index 2a4b3548401095f98e3111f8422005f008c931fe..59a757e46ba552ad57736103ea42c3378e83af90 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2008-2009 PetaLogix
  * Copyright (C) 2006 Atmark Techno, Inc.
  *
  * This file is subject to the terms and conditions of the GNU General Public
 #ifndef _ASM_MICROBLAZE_PGALLOC_H
 #define _ASM_MICROBLAZE_PGALLOC_H
 
+#ifdef CONFIG_MMU
+
+#include <linux/kernel.h>      /* For min/max macros */
+#include <linux/highmem.h>
+#include <asm/setup.h>
+#include <asm/io.h>
+#include <asm/page.h>
+#include <asm/cache.h>
+
+#define PGDIR_ORDER    0
+
+/*
+ * This is handled very differently on MicroBlaze since out page tables
+ * are all 0's and I want to be able to use these zero'd pages elsewhere
+ * as well - it gives us quite a speedup.
+ * -- Cort
+ */
+extern struct pgtable_cache_struct {
+       unsigned long *pgd_cache;
+       unsigned long *pte_cache;
+       unsigned long pgtable_cache_sz;
+} quicklists;
+
+#define pgd_quicklist          (quicklists.pgd_cache)
+#define pmd_quicklist          ((unsigned long *)0)
+#define pte_quicklist          (quicklists.pte_cache)
+#define pgtable_cache_size     (quicklists.pgtable_cache_sz)
+
+extern unsigned long *zero_cache; /* head linked list of pre-zero'd pages */
+extern atomic_t zero_sz; /* # currently pre-zero'd pages */
+extern atomic_t zeropage_hits; /* # zero'd pages request that we've done */
+extern atomic_t zeropage_calls; /* # zero'd pages request that've been made */
+extern atomic_t zerototal; /* # pages zero'd over time */
+
+#define zero_quicklist         (zero_cache)
+#define zero_cache_sz          (zero_sz)
+#define zero_cache_calls       (zeropage_calls)
+#define zero_cache_hits                (zeropage_hits)
+#define zero_cache_total       (zerototal)
+
+/*
+ * return a pre-zero'd page from the list,
+ * return NULL if none available -- Cort
+ */
+extern unsigned long get_zero_page_fast(void);
+
+extern void __bad_pte(pmd_t *pmd);
+
+extern inline pgd_t *get_pgd_slow(void)
+{
+       pgd_t *ret;
+
+       ret = (pgd_t *)__get_free_pages(GFP_KERNEL, PGDIR_ORDER);
+       if (ret != NULL)
+               clear_page(ret);
+       return ret;
+}
+
+extern inline pgd_t *get_pgd_fast(void)
+{
+       unsigned long *ret;
+
+       ret = pgd_quicklist;
+       if (ret != NULL) {
+               pgd_quicklist = (unsigned long *)(*ret);
+               ret[0] = 0;
+               pgtable_cache_size--;
+       } else
+               ret = (unsigned long *)get_pgd_slow();
+       return (pgd_t *)ret;
+}
+
+extern inline void free_pgd_fast(pgd_t *pgd)
+{
+       *(unsigned long **)pgd = pgd_quicklist;
+       pgd_quicklist = (unsigned long *) pgd;
+       pgtable_cache_size++;
+}
+
+extern inline void free_pgd_slow(pgd_t *pgd)
+{
+       free_page((unsigned long)pgd);
+}
+
+#define pgd_free(mm, pgd)        free_pgd_fast(pgd)
+#define pgd_alloc(mm)          get_pgd_fast()
+
+#define pmd_pgtable(pmd)       pmd_page(pmd)
+
+/*
+ * We don't have any real pmd's, and this code never triggers because
+ * the pgd will always be present..
+ */
+#define pmd_alloc_one_fast(mm, address)        ({ BUG(); ((pmd_t *)1); })
+#define pmd_alloc_one(mm, address)     ({ BUG(); ((pmd_t *)2); })
+/* FIXME two definition - look below */
+#define pmd_free(mm, x)                        do { } while (0)
+#define pgd_populate(mm, pmd, pte)     BUG()
+
+static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm,
+               unsigned long address)
+{
+       pte_t *pte;
+       extern int mem_init_done;
+       extern void *early_get_page(void);
+       if (mem_init_done) {
+               pte = (pte_t *)__get_free_page(GFP_KERNEL |
+                                       __GFP_REPEAT | __GFP_ZERO);
+       } else {
+               pte = (pte_t *)early_get_page();
+               if (pte)
+                       clear_page(pte);
+       }
+       return pte;
+}
+
+static inline struct page *pte_alloc_one(struct mm_struct *mm,
+               unsigned long address)
+{
+       struct page *ptepage;
+
+#ifdef CONFIG_HIGHPTE
+       int flags = GFP_KERNEL | __GFP_HIGHMEM | __GFP_REPEAT;
+#else
+       int flags = GFP_KERNEL | __GFP_REPEAT;
+#endif
+
+       ptepage = alloc_pages(flags, 0);
+       if (ptepage)
+               clear_highpage(ptepage);
+       return ptepage;
+}
+
+static inline pte_t *pte_alloc_one_fast(struct mm_struct *mm,
+               unsigned long address)
+{
+       unsigned long *ret;
+
+       ret = pte_quicklist;
+       if (ret != NULL) {
+               pte_quicklist = (unsigned long *)(*ret);
+               ret[0] = 0;
+               pgtable_cache_size--;
+       }
+       return (pte_t *)ret;
+}
+
+extern inline void pte_free_fast(pte_t *pte)
+{
+       *(unsigned long **)pte = pte_quicklist;
+       pte_quicklist = (unsigned long *) pte;
+       pgtable_cache_size++;
+}
+
+extern inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
+{
+       free_page((unsigned long)pte);
+}
+
+extern inline void pte_free_slow(struct page *ptepage)
+{
+       __free_page(ptepage);
+}
+
+extern inline void pte_free(struct mm_struct *mm, struct page *ptepage)
+{
+       __free_page(ptepage);
+}
+
+#define __pte_free_tlb(tlb, pte)       pte_free((tlb)->mm, (pte))
+
+#define pmd_populate(mm, pmd, pte)     (pmd_val(*(pmd)) = page_address(pte))
+
+#define pmd_populate_kernel(mm, pmd, pte) \
+               (pmd_val(*(pmd)) = (unsigned long) (pte))
+
+/*
+ * We don't have any real pmd's, and this code never triggers because
+ * the pgd will always be present..
+ */
+#define pmd_alloc_one(mm, address)     ({ BUG(); ((pmd_t *)2); })
+/*#define pmd_free(mm, x)                      do { } while (0)*/
+#define __pmd_free_tlb(tlb, x)         do { } while (0)
+#define pgd_populate(mm, pmd, pte)     BUG()
+
+extern int do_check_pgt_cache(int, int);
+
+#endif /* CONFIG_MMU */
+
 #define check_pgt_cache()      do {} while (0)
 
 #endif /* _ASM_MICROBLAZE_PGALLOC_H */
index 4df31e46568e7649e43f69170604f5a28484bfc3..4c57a586a989e6695be8b14a2337d34f87f7f751 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2008-2009 PetaLogix
  * Copyright (C) 2006 Atmark Techno, Inc.
  *
  * This file is subject to the terms and conditions of the GNU General Public
@@ -14,6 +16,8 @@
 #define io_remap_pfn_range(vma, vaddr, pfn, size, prot)                \
                remap_pfn_range(vma, vaddr, pfn, size, prot)
 
+#ifndef CONFIG_MMU
+
 #define pgd_present(pgd)       (1) /* pages are always present on non MMU */
 #define pgd_none(pgd)          (0)
 #define pgd_bad(pgd)           (0)
@@ -27,6 +31,8 @@
 #define PAGE_READONLY          __pgprot(0) /* these mean nothing to non MMU */
 #define PAGE_KERNEL            __pgprot(0) /* these mean nothing to non MMU */
 
+#define pgprot_noncached(x)    (x)
+
 #define __swp_type(x)          (0)
 #define __swp_offset(x)                (0)
 #define __swp_entry(typ, off)  ((swp_entry_t) { ((typ) | ((off) << 7)) })
@@ -45,6 +51,538 @@ static inline int pte_file(pte_t pte) { return 0; }
 
 #define arch_enter_lazy_cpu_mode()     do {} while (0)
 
+#else /* CONFIG_MMU */
+
+#include <asm-generic/4level-fixup.h>
+
+#ifdef __KERNEL__
+#ifndef __ASSEMBLY__
+
+#include <linux/sched.h>
+#include <linux/threads.h>
+#include <asm/processor.h>             /* For TASK_SIZE */
+#include <asm/mmu.h>
+#include <asm/page.h>
+
+#define FIRST_USER_ADDRESS     0
+
+extern unsigned long va_to_phys(unsigned long address);
+extern pte_t *va_to_pte(unsigned long address);
+extern unsigned long ioremap_bot, ioremap_base;
+
+/*
+ * The following only work if pte_present() is true.
+ * Undefined behaviour if not..
+ */
+
+static inline int pte_special(pte_t pte)       { return 0; }
+
+static inline pte_t pte_mkspecial(pte_t pte)   { return pte; }
+
+/* Start and end of the vmalloc area. */
+/* Make sure to map the vmalloc area above the pinned kernel memory area
+   of 32Mb.  */
+#define VMALLOC_START  (CONFIG_KERNEL_START + \
+                               max(32 * 1024 * 1024UL, memory_size))
+#define VMALLOC_END    ioremap_bot
+#define VMALLOC_VMADDR(x) ((unsigned long)(x))
+
+#endif /* __ASSEMBLY__ */
+
+/*
+ * The MicroBlaze MMU is identical to the PPC-40x MMU, and uses a hash
+ * table containing PTEs, together with a set of 16 segment registers, to
+ * define the virtual to physical address mapping.
+ *
+ * We use the hash table as an extended TLB, i.e. a cache of currently
+ * active mappings.  We maintain a two-level page table tree, much
+ * like that used by the i386, for the sake of the Linux memory
+ * management code.  Low-level assembler code in hashtable.S
+ * (procedure hash_page) is responsible for extracting ptes from the
+ * tree and putting them into the hash table when necessary, and
+ * updating the accessed and modified bits in the page table tree.
+ */
+
+/*
+ * The MicroBlaze processor has a TLB architecture identical to PPC-40x. The
+ * instruction and data sides share a unified, 64-entry, semi-associative
+ * TLB which is maintained totally under software control. In addition, the
+ * instruction side has a hardware-managed, 2,4, or 8-entry, fully-associative
+ * TLB which serves as a first level to the shared TLB. These two TLBs are
+ * known as the UTLB and ITLB, respectively (see "mmu.h" for definitions).
+ */
+
+/*
+ * The normal case is that PTEs are 32-bits and we have a 1-page
+ * 1024-entry pgdir pointing to 1-page 1024-entry PTE pages.  -- paulus
+ *
+ */
+
+/* PMD_SHIFT determines the size of the area mapped by the PTE pages */
+#define PMD_SHIFT      (PAGE_SHIFT + PTE_SHIFT)
+#define PMD_SIZE       (1UL << PMD_SHIFT)
+#define PMD_MASK       (~(PMD_SIZE-1))
+
+/* PGDIR_SHIFT determines what a top-level page table entry can map */
+#define PGDIR_SHIFT    PMD_SHIFT
+#define PGDIR_SIZE     (1UL << PGDIR_SHIFT)
+#define PGDIR_MASK     (~(PGDIR_SIZE-1))
+
+/*
+ * entries per page directory level: our page-table tree is two-level, so
+ * we don't really have any PMD directory.
+ */
+#define PTRS_PER_PTE   (1 << PTE_SHIFT)
+#define PTRS_PER_PMD   1
+#define PTRS_PER_PGD   (1 << (32 - PGDIR_SHIFT))
+
+#define USER_PTRS_PER_PGD      (TASK_SIZE / PGDIR_SIZE)
+#define FIRST_USER_PGD_NR      0
+
+#define USER_PGD_PTRS (PAGE_OFFSET >> PGDIR_SHIFT)
+#define KERNEL_PGD_PTRS (PTRS_PER_PGD-USER_PGD_PTRS)
+
+#define pte_ERROR(e) \
+       printk(KERN_ERR "%s:%d: bad pte "PTE_FMT".\n", \
+               __FILE__, __LINE__, pte_val(e))
+#define pmd_ERROR(e) \
+       printk(KERN_ERR "%s:%d: bad pmd %08lx.\n", \
+               __FILE__, __LINE__, pmd_val(e))
+#define pgd_ERROR(e) \
+       printk(KERN_ERR "%s:%d: bad pgd %08lx.\n", \
+               __FILE__, __LINE__, pgd_val(e))
+
+/*
+ * Bits in a linux-style PTE.  These match the bits in the
+ * (hardware-defined) PTE as closely as possible.
+ */
+
+/* There are several potential gotchas here.  The hardware TLBLO
+ * field looks like this:
+ *
+ * 0  1  2  3  4  ... 18 19 20 21 22 23 24 25 26 27 28 29 30 31
+ * RPN.....................  0  0 EX WR ZSEL.......  W  I  M  G
+ *
+ * Where possible we make the Linux PTE bits match up with this
+ *
+ * - bits 20 and 21 must be cleared, because we use 4k pages (4xx can
+ * support down to 1k pages), this is done in the TLBMiss exception
+ * handler.
+ * - We use only zones 0 (for kernel pages) and 1 (for user pages)
+ * of the 16 available.  Bit 24-26 of the TLB are cleared in the TLB
+ * miss handler.  Bit 27 is PAGE_USER, thus selecting the correct
+ * zone.
+ * - PRESENT *must* be in the bottom two bits because swap cache
+ * entries use the top 30 bits.  Because 4xx doesn't support SMP
+ * anyway, M is irrelevant so we borrow it for PAGE_PRESENT.  Bit 30
+ * is cleared in the TLB miss handler before the TLB entry is loaded.
+ * - All other bits of the PTE are loaded into TLBLO without
+ *  * modification, leaving us only the bits 20, 21, 24, 25, 26, 30 for
+ * software PTE bits.  We actually use use bits 21, 24, 25, and
+ * 30 respectively for the software bits: ACCESSED, DIRTY, RW, and
+ * PRESENT.
+ */
+
+/* Definitions for MicroBlaze. */
+#define        _PAGE_GUARDED   0x001   /* G: page is guarded from prefetch */
+#define _PAGE_PRESENT  0x002   /* software: PTE contains a translation */
+#define        _PAGE_NO_CACHE  0x004   /* I: caching is inhibited */
+#define        _PAGE_WRITETHRU 0x008   /* W: caching is write-through */
+#define        _PAGE_USER      0x010   /* matches one of the zone permission bits */
+#define        _PAGE_RW        0x040   /* software: Writes permitted */
+#define        _PAGE_DIRTY     0x080   /* software: dirty page */
+#define _PAGE_HWWRITE  0x100   /* hardware: Dirty & RW, set in exception */
+#define _PAGE_HWEXEC   0x200   /* hardware: EX permission */
+#define _PAGE_ACCESSED 0x400   /* software: R: page referenced */
+#define _PMD_PRESENT   PAGE_MASK
+
+/*
+ * Some bits are unused...
+ */
+#ifndef _PAGE_HASHPTE
+#define _PAGE_HASHPTE  0
+#endif
+#ifndef _PTE_NONE_MASK
+#define _PTE_NONE_MASK 0
+#endif
+#ifndef _PAGE_SHARED
+#define _PAGE_SHARED   0
+#endif
+#ifndef _PAGE_HWWRITE
+#define _PAGE_HWWRITE  0
+#endif
+#ifndef _PAGE_HWEXEC
+#define _PAGE_HWEXEC   0
+#endif
+#ifndef _PAGE_EXEC
+#define _PAGE_EXEC     0
+#endif
+
+#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
+
+/*
+ * Note: the _PAGE_COHERENT bit automatically gets set in the hardware
+ * PTE if CONFIG_SMP is defined (hash_page does this); there is no need
+ * to have it in the Linux PTE, and in fact the bit could be reused for
+ * another purpose.  -- paulus.
+ */
+#define _PAGE_BASE     (_PAGE_PRESENT | _PAGE_ACCESSED)
+#define _PAGE_WRENABLE (_PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE)
+
+#define _PAGE_KERNEL \
+       (_PAGE_BASE | _PAGE_WRENABLE | _PAGE_SHARED | _PAGE_HWEXEC)
+
+#define _PAGE_IO       (_PAGE_KERNEL | _PAGE_NO_CACHE | _PAGE_GUARDED)
+
+#define PAGE_NONE      __pgprot(_PAGE_BASE)
+#define PAGE_READONLY  __pgprot(_PAGE_BASE | _PAGE_USER)
+#define PAGE_READONLY_X        __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_EXEC)
+#define PAGE_SHARED    __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_RW)
+#define PAGE_SHARED_X \
+               __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_RW | _PAGE_EXEC)
+#define PAGE_COPY      __pgprot(_PAGE_BASE | _PAGE_USER)
+#define PAGE_COPY_X    __pgprot(_PAGE_BASE | _PAGE_USER | _PAGE_EXEC)
+
+#define PAGE_KERNEL    __pgprot(_PAGE_KERNEL)
+#define PAGE_KERNEL_RO __pgprot(_PAGE_BASE | _PAGE_SHARED)
+#define PAGE_KERNEL_CI __pgprot(_PAGE_IO)
+
+/*
+ * We consider execute permission the same as read.
+ * Also, write permissions imply read permissions.
+ */
+#define __P000 PAGE_NONE
+#define __P001 PAGE_READONLY_X
+#define __P010 PAGE_COPY
+#define __P011 PAGE_COPY_X
+#define __P100 PAGE_READONLY
+#define __P101 PAGE_READONLY_X
+#define __P110 PAGE_COPY
+#define __P111 PAGE_COPY_X
+
+#define __S000 PAGE_NONE
+#define __S001 PAGE_READONLY_X
+#define __S010 PAGE_SHARED
+#define __S011 PAGE_SHARED_X
+#define __S100 PAGE_READONLY
+#define __S101 PAGE_READONLY_X
+#define __S110 PAGE_SHARED
+#define __S111 PAGE_SHARED_X
+
+#ifndef __ASSEMBLY__
+/*
+ * ZERO_PAGE is a global shared page that is always zero: used
+ * for zero-mapped memory areas etc..
+ */
+extern unsigned long empty_zero_page[1024];
+#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page))
+
+#endif /* __ASSEMBLY__ */
+
+#define pte_none(pte)          ((pte_val(pte) & ~_PTE_NONE_MASK) == 0)
+#define pte_present(pte)       (pte_val(pte) & _PAGE_PRESENT)
+#define pte_clear(mm, addr, ptep) \
+       do { set_pte_at((mm), (addr), (ptep), __pte(0)); } while (0)
+
+#define pmd_none(pmd)          (!pmd_val(pmd))
+#define        pmd_bad(pmd)            ((pmd_val(pmd) & _PMD_PRESENT) == 0)
+#define        pmd_present(pmd)        ((pmd_val(pmd) & _PMD_PRESENT) != 0)
+#define        pmd_clear(pmdp)         do { pmd_val(*(pmdp)) = 0; } while (0)
+
+#define pte_page(x)            (mem_map + (unsigned long) \
+                               ((pte_val(x) - memory_start) >> PAGE_SHIFT))
+#define PFN_SHIFT_OFFSET       (PAGE_SHIFT)
+
+#define pte_pfn(x)             (pte_val(x) >> PFN_SHIFT_OFFSET)
+
+#define pfn_pte(pfn, prot) \
+       __pte(((pte_basic_t)(pfn) << PFN_SHIFT_OFFSET) | pgprot_val(prot))
+
+#ifndef __ASSEMBLY__
+/*
+ * The "pgd_xxx()" functions here are trivial for a folded two-level
+ * setup: the pgd is never bad, and a pmd always exists (as it's folded
+ * into the pgd entry)
+ */
+static inline int pgd_none(pgd_t pgd)          { return 0; }
+static inline int pgd_bad(pgd_t pgd)           { return 0; }
+static inline int pgd_present(pgd_t pgd)       { return 1; }
+#define pgd_clear(xp)                          do { } while (0)
+#define pgd_page(pgd) \
+       ((unsigned long) __va(pgd_val(pgd) & PAGE_MASK))
+
+/*
+ * The following only work if pte_present() is true.
+ * Undefined behaviour if not..
+ */
+static inline int pte_read(pte_t pte)  { return pte_val(pte) & _PAGE_USER; }
+static inline int pte_write(pte_t pte) { return pte_val(pte) & _PAGE_RW; }
+static inline int pte_exec(pte_t pte)  { return pte_val(pte) & _PAGE_EXEC; }
+static inline int pte_dirty(pte_t pte) { return pte_val(pte) & _PAGE_DIRTY; }
+static inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED; }
+/* FIXME */
+static inline int pte_file(pte_t pte)          { return 0; }
+
+static inline void pte_uncache(pte_t pte) { pte_val(pte) |= _PAGE_NO_CACHE; }
+static inline void pte_cache(pte_t pte)   { pte_val(pte) &= ~_PAGE_NO_CACHE; }
+
+static inline pte_t pte_rdprotect(pte_t pte) \
+               { pte_val(pte) &= ~_PAGE_USER; return pte; }
+static inline pte_t pte_wrprotect(pte_t pte) \
+       { pte_val(pte) &= ~(_PAGE_RW | _PAGE_HWWRITE); return pte; }
+static inline pte_t pte_exprotect(pte_t pte) \
+       { pte_val(pte) &= ~_PAGE_EXEC; return pte; }
+static inline pte_t pte_mkclean(pte_t pte) \
+       { pte_val(pte) &= ~(_PAGE_DIRTY | _PAGE_HWWRITE); return pte; }
+static inline pte_t pte_mkold(pte_t pte) \
+       { pte_val(pte) &= ~_PAGE_ACCESSED; return pte; }
+
+static inline pte_t pte_mkread(pte_t pte) \
+       { pte_val(pte) |= _PAGE_USER; return pte; }
+static inline pte_t pte_mkexec(pte_t pte) \
+       { pte_val(pte) |= _PAGE_USER | _PAGE_EXEC; return pte; }
+static inline pte_t pte_mkwrite(pte_t pte) \
+       { pte_val(pte) |= _PAGE_RW; return pte; }
+static inline pte_t pte_mkdirty(pte_t pte) \
+       { pte_val(pte) |= _PAGE_DIRTY; return pte; }
+static inline pte_t pte_mkyoung(pte_t pte) \
+       { pte_val(pte) |= _PAGE_ACCESSED; return pte; }
+
+/*
+ * Conversion functions: convert a page and protection to a page entry,
+ * and a page entry and page directory to the page they refer to.
+ */
+
+static inline pte_t mk_pte_phys(phys_addr_t physpage, pgprot_t pgprot)
+{
+       pte_t pte;
+       pte_val(pte) = physpage | pgprot_val(pgprot);
+       return pte;
+}
+
+#define mk_pte(page, pgprot) \
+({                                                                        \
+       pte_t pte;                                                         \
+       pte_val(pte) = (((page - mem_map) << PAGE_SHIFT) + memory_start) |  \
+                       pgprot_val(pgprot);                                \
+       pte;                                                               \
+})
+
+static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
+{
+       pte_val(pte) = (pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot);
+       return pte;
+}
+
+/*
+ * Atomic PTE updates.
+ *
+ * pte_update clears and sets bit atomically, and returns
+ * the old pte value.
+ * The ((unsigned long)(p+1) - 4) hack is to get to the least-significant
+ * 32 bits of the PTE regardless of whether PTEs are 32 or 64 bits.
+ */
+static inline unsigned long pte_update(pte_t *p, unsigned long clr,
+                               unsigned long set)
+{
+       unsigned long old, tmp, msr;
+
+       __asm__ __volatile__("\
+       msrclr  %2, 0x2\n\
+       nop\n\
+       lw      %0, %4, r0\n\
+       andn    %1, %0, %5\n\
+       or      %1, %1, %6\n\
+       sw      %1, %4, r0\n\
+       mts     rmsr, %2\n\
+       nop"
+       : "=&r" (old), "=&r" (tmp), "=&r" (msr), "=m" (*p)
+       : "r" ((unsigned long)(p+1) - 4), "r" (clr), "r" (set), "m" (*p)
+       : "cc");
+
+       return old;
+}
+
+/*
+ * set_pte stores a linux PTE into the linux page table.
+ */
+static inline void set_pte(struct mm_struct *mm, unsigned long addr,
+               pte_t *ptep, pte_t pte)
+{
+       *ptep = pte;
+}
+
+static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
+               pte_t *ptep, pte_t pte)
+{
+       *ptep = pte;
+}
+
+static inline int ptep_test_and_clear_young(struct mm_struct *mm,
+               unsigned long addr, pte_t *ptep)
+{
+       return (pte_update(ptep, _PAGE_ACCESSED, 0) & _PAGE_ACCESSED) != 0;
+}
+
+static inline int ptep_test_and_clear_dirty(struct mm_struct *mm,
+               unsigned long addr, pte_t *ptep)
+{
+       return (pte_update(ptep, \
+               (_PAGE_DIRTY | _PAGE_HWWRITE), 0) & _PAGE_DIRTY) != 0;
+}
+
+static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
+               unsigned long addr, pte_t *ptep)
+{
+       return __pte(pte_update(ptep, ~_PAGE_HASHPTE, 0));
+}
+
+/*static inline void ptep_set_wrprotect(struct mm_struct *mm,
+               unsigned long addr, pte_t *ptep)
+{
+       pte_update(ptep, (_PAGE_RW | _PAGE_HWWRITE), 0);
+}*/
+
+static inline void ptep_mkdirty(struct mm_struct *mm,
+               unsigned long addr, pte_t *ptep)
+{
+       pte_update(ptep, 0, _PAGE_DIRTY);
+}
+
+/*#define pte_same(A,B)        (((pte_val(A) ^ pte_val(B)) & ~_PAGE_HASHPTE) == 0)*/
+
+/* Convert pmd entry to page */
+/* our pmd entry is an effective address of pte table*/
+/* returns effective address of the pmd entry*/
+#define pmd_page_kernel(pmd)   ((unsigned long) (pmd_val(pmd) & PAGE_MASK))
+
+/* returns struct *page of the pmd entry*/
+#define pmd_page(pmd)  (pfn_to_page(__pa(pmd_val(pmd)) >> PAGE_SHIFT))
+
+/* to find an entry in a kernel page-table-directory */
+#define pgd_offset_k(address) pgd_offset(&init_mm, address)
+
+/* to find an entry in a page-table-directory */
+#define pgd_index(address)      ((address) >> PGDIR_SHIFT)
+#define pgd_offset(mm, address)         ((mm)->pgd + pgd_index(address))
+
+/* Find an entry in the second-level page table.. */
+static inline pmd_t *pmd_offset(pgd_t *dir, unsigned long address)
+{
+       return (pmd_t *) dir;
+}
+
+/* Find an entry in the third-level page table.. */
+#define pte_index(address)             \
+       (((address) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
+#define pte_offset_kernel(dir, addr)   \
+       ((pte_t *) pmd_page_kernel(*(dir)) + pte_index(addr))
+#define pte_offset_map(dir, addr)              \
+       ((pte_t *) kmap_atomic(pmd_page(*(dir)), KM_PTE0) + pte_index(addr))
+#define pte_offset_map_nested(dir, addr)       \
+       ((pte_t *) kmap_atomic(pmd_page(*(dir)), KM_PTE1) + pte_index(addr))
+
+#define pte_unmap(pte)         kunmap_atomic(pte, KM_PTE0)
+#define pte_unmap_nested(pte)  kunmap_atomic(pte, KM_PTE1)
+
+/* Encode and decode a nonlinear file mapping entry */
+#define PTE_FILE_MAX_BITS      29
+#define pte_to_pgoff(pte)      (pte_val(pte) >> 3)
+#define pgoff_to_pte(off)      ((pte_t) { ((off) << 3) })
+
+extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
+
+/*
+ * When flushing the tlb entry for a page, we also need to flush the hash
+ * table entry.  flush_hash_page is assembler (for speed) in hashtable.S.
+ */
+extern int flush_hash_page(unsigned context, unsigned long va, pte_t *ptep);
+
+/* Add an HPTE to the hash table */
+extern void add_hash_page(unsigned context, unsigned long va, pte_t *ptep);
+
+/*
+ * Encode and decode a swap entry.
+ * Note that the bits we use in a PTE for representing a swap entry
+ * must not include the _PAGE_PRESENT bit, or the _PAGE_HASHPTE bit
+ * (if used).  -- paulus
+ */
+#define __swp_type(entry)              ((entry).val & 0x3f)
+#define __swp_offset(entry)    ((entry).val >> 6)
+#define __swp_entry(type, offset) \
+               ((swp_entry_t) { (type) | ((offset) << 6) })
+#define __pte_to_swp_entry(pte)        ((swp_entry_t) { pte_val(pte) >> 2 })
+#define __swp_entry_to_pte(x)  ((pte_t) { (x).val << 2 })
+
+
+/* CONFIG_APUS */
+/* For virtual address to physical address conversion */
+extern void cache_clear(__u32 addr, int length);
+extern void cache_push(__u32 addr, int length);
+extern int mm_end_of_chunk(unsigned long addr, int len);
+extern unsigned long iopa(unsigned long addr);
+/* extern unsigned long mm_ptov(unsigned long addr) \
+       __attribute__ ((const)); TBD */
+
+/* Values for nocacheflag and cmode */
+/* These are not used by the APUS kernel_map, but prevents
+ * compilation errors.
+ */
+#define        IOMAP_FULL_CACHING      0
+#define        IOMAP_NOCACHE_SER       1
+#define        IOMAP_NOCACHE_NONSER    2
+#define        IOMAP_NO_COPYBACK       3
+
+/*
+ * Map some physical address range into the kernel address space.
+ */
+extern unsigned long kernel_map(unsigned long paddr, unsigned long size,
+                               int nocacheflag, unsigned long *memavailp);
+
+/*
+ * Set cache mode of (kernel space) address range.
+ */
+extern void kernel_set_cachemode(unsigned long address, unsigned long size,
+                               unsigned int cmode);
+
+/* Needs to be defined here and not in linux/mm.h, as it is arch dependent */
+#define kern_addr_valid(addr)  (1)
+
+#define io_remap_page_range remap_page_range
+
+/*
+ * No page table caches to initialise
+ */
+#define pgtable_cache_init()   do { } while (0)
+
+void do_page_fault(struct pt_regs *regs, unsigned long address,
+                  unsigned long error_code);
+
+void __init io_block_mapping(unsigned long virt, phys_addr_t phys,
+                            unsigned int size, int flags);
+
+void __init adjust_total_lowmem(void);
+void mapin_ram(void);
+int map_page(unsigned long va, phys_addr_t pa, int flags);
+
+extern int mem_init_done;
+extern unsigned long ioremap_base;
+extern unsigned long ioremap_bot;
+
+asmlinkage void __init mmu_init(void);
+
+void __init *early_get_page(void);
+
+void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle);
+void consistent_free(void *vaddr);
+void consistent_sync(void *vaddr, size_t size, int direction);
+void consistent_sync_page(struct page *page, unsigned long offset,
+       size_t size, int direction);
+#endif /* __ASSEMBLY__ */
+#endif /* __KERNEL__ */
+
+#endif /* CONFIG_MMU */
+
 #ifndef __ASSEMBLY__
 #include <asm-generic/pgtable.h>
 
index b4df41c5dde23fc2cf2e51bf4ba4dea84a3d7719..8c758b231f376780dad5b30a0ae8b8b29da54b00 100644 (file)
@@ -16,7 +16,7 @@
  */
 
 typedef unsigned long  __kernel_ino_t;
-typedef unsigned int   __kernel_mode_t;
+typedef unsigned short __kernel_mode_t;
 typedef unsigned int   __kernel_nlink_t;
 typedef long           __kernel_off_t;
 typedef int            __kernel_pid_t;
index 9329029d26148c5c04d10df919c308eee099b1a9..563c6b9453f030a34344cf0e9d63f1a5dd994318 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2008 Michal Simek
- * Copyright (C) 2008 PetaLogix
+ * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2008-2009 PetaLogix
  * Copyright (C) 2006 Atmark Techno, Inc.
  *
  * This file is subject to the terms and conditions of the GNU General Public
@@ -26,14 +26,15 @@ extern const struct seq_operations cpuinfo_op;
 # define cpu_sleep()           do {} while (0)
 # define prepare_to_copy(tsk)  do {} while (0)
 
-# endif /* __ASSEMBLY__ */
-
 #define task_pt_regs(tsk) \
                (((struct pt_regs *)(THREAD_SIZE + task_stack_page(tsk))) - 1)
 
 /* Do necessary setup to start up a newly executed thread. */
 void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long usp);
 
+# endif /* __ASSEMBLY__ */
+
+# ifndef CONFIG_MMU
 /*
  * User space process size: memory size
  *
@@ -85,4 +86,90 @@ extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
 # define KSTK_EIP(tsk) (0)
 # define KSTK_ESP(tsk) (0)
 
+# else /* CONFIG_MMU */
+
+/*
+ * This is used to define STACK_TOP, and with MMU it must be below
+ * kernel base to select the correct PGD when handling MMU exceptions.
+ */
+# define TASK_SIZE     (CONFIG_KERNEL_START)
+
+/*
+ * This decides where the kernel will search for a free chunk of vm
+ * space during mmap's.
+ */
+# define TASK_UNMAPPED_BASE    (TASK_SIZE / 8 * 3)
+
+# define THREAD_KSP    0
+
+#  ifndef __ASSEMBLY__
+
+/*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#  define current_text_addr()  ({ __label__ _l; _l: &&_l; })
+
+/* If you change this, you must change the associated assembly-languages
+ * constants defined below, THREAD_*.
+ */
+struct thread_struct {
+       /* kernel stack pointer (must be first field in structure) */
+       unsigned long   ksp;
+       unsigned long   ksp_limit;      /* if ksp <= ksp_limit stack overflow */
+       void            *pgdir;         /* root of page-table tree */
+       struct pt_regs  *regs;          /* Pointer to saved register state */
+};
+
+#  define INIT_THREAD { \
+       .ksp   = sizeof init_stack + (unsigned long)init_stack, \
+       .pgdir = swapper_pg_dir, \
+}
+
+/* Do necessary setup to start up a newly executed thread.  */
+void start_thread(struct pt_regs *regs,
+               unsigned long pc, unsigned long usp);
+
+/* Free all resources held by a thread. */
+extern inline void release_thread(struct task_struct *dead_task)
+{
+}
+
+extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
+
+/* Free current thread data structures etc.  */
+static inline void exit_thread(void)
+{
+}
+
+/* Return saved (kernel) PC of a blocked thread.  */
+#  define thread_saved_pc(tsk) \
+       ((tsk)->thread.regs ? (tsk)->thread.regs->r15 : 0)
+
+unsigned long get_wchan(struct task_struct *p);
+
+/* The size allocated for kernel stacks. This _must_ be a power of two! */
+# define KERNEL_STACK_SIZE     0x2000
+
+/* Return some info about the user process TASK.  */
+#  define task_tos(task)       ((unsigned long)(task) + KERNEL_STACK_SIZE)
+#  define task_regs(task) ((struct pt_regs *)task_tos(task) - 1)
+
+#  define task_pt_regs_plus_args(tsk) \
+       (((void *)task_pt_regs(tsk)) - STATE_SAVE_ARG_SPACE)
+
+#  define task_sp(task)        (task_regs(task)->r1)
+#  define task_pc(task)        (task_regs(task)->pc)
+/* Grotty old names for some.  */
+#  define KSTK_EIP(task)       (task_pc(task))
+#  define KSTK_ESP(task)       (task_sp(task))
+
+/* FIXME */
+#  define deactivate_mm(tsk, mm)       do { } while (0)
+
+#  define STACK_TOP    TASK_SIZE
+#  define STACK_TOP_MAX        STACK_TOP
+
+#  endif /* __ASSEMBLY__ */
+# endif /* CONFIG_MMU */
 #endif /* _ASM_MICROBLAZE_PROCESSOR_H */
index 55015bce5e47ed0c6ec9bc6fa2eef293ddcfbdbd..a917dc517736dd03715235505606e9be5afdf53e 100644 (file)
@@ -10,7 +10,6 @@
 #define _ASM_MICROBLAZE_PTRACE_H
 
 #ifndef __ASSEMBLY__
-#include <linux/types.h>
 
 typedef unsigned long microblaze_reg_t;
 
index 834142d9356fa810813f1100360177835ed69336..68c3afb738779e389a17ba75b0457233058f8bbb 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2008 Michal Simek
- * Copyright (C) 2008 PetaLogix
+ * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2008-2009 PetaLogix
  * Copyright (C) 2006 Atmark Techno, Inc.
  *
  * This file is subject to the terms and conditions of the GNU General Public
 #define FSR_UF         (1<<1) /* Underflow */
 #define FSR_DO         (1<<0) /* Denormalized operand error */
 
+# ifdef CONFIG_MMU
+/* Machine State Register (MSR) Fields */
+# define MSR_UM                (1<<11) /* User Mode */
+# define MSR_UMS       (1<<12) /* User Mode Save */
+# define MSR_VM                (1<<13) /* Virtual Mode */
+# define MSR_VMS       (1<<14) /* Virtual Mode Save */
+
+# define MSR_KERNEL    (MSR_EE | MSR_VM)
+/* # define MSR_USER   (MSR_KERNEL | MSR_UM | MSR_IE) */
+# define MSR_KERNEL_VMS        (MSR_EE | MSR_VMS)
+/* # define MSR_USER_VMS       (MSR_KERNEL_VMS | MSR_UMS | MSR_IE) */
+
+/* Exception State Register (ESR) Fields */
+# define         ESR_DIZ       (1<<11) /* Zone Protection */
+# define         ESR_S         (1<<10) /* Store instruction */
+
+# endif /* CONFIG_MMU */
 #endif /* _ASM_MICROBLAZE_REGISTERS_H */
index 8434a43e542139e316c5c991dc9fc7da5206037d..4487e150b4555d2815417551cbdafc200234bb5a 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2008-2009 PetaLogix
  * Copyright (C) 2006 Atmark Techno, Inc.
  *
  * This file is subject to the terms and conditions of the GNU General Public
@@ -14,6 +16,7 @@
 # ifndef __ASSEMBLY__
 extern char _ssbss[], _esbss[];
 extern unsigned long __ivt_start[], __ivt_end[];
+extern char _etext[], _stext[];
 
 #  ifdef CONFIG_MTD_UCLINUX
 extern char *_ebss;
index 7f5dcc56eea1b586aa3e0a9357195cd4b217fdef..0e7102c3fb117e2f6ebdef2bad277e5ec2c27eec 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2008 Michal Simek
- * Copyright (C) 2008 PetaLogix
+ * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2008-2009 PetaLogix
  * Copyright (C) 2006 Atmark Techno, Inc.
  *
  * This file is subject to the terms and conditions of the GNU General Public
@@ -11,7 +11,7 @@
 #ifndef _ASM_MICROBLAZE_SEGMENT_H
 #define _ASM_MICROBLAZE_SEGMENT_H
 
-#ifndef __ASSEMBLY__
+# ifndef __ASSEMBLY__
 
 typedef struct {
        unsigned long seg;
@@ -29,15 +29,21 @@ typedef struct {
  *
  * For non-MMU arch like Microblaze, KERNEL_DS and USER_DS is equal.
  */
-#  define KERNEL_DS    ((mm_segment_t){0})
+# define MAKE_MM_SEG(s)       ((mm_segment_t) { (s) })
+
+#  ifndef CONFIG_MMU
+#  define KERNEL_DS    MAKE_MM_SEG(0)
 #  define USER_DS      KERNEL_DS
+#  else
+#  define KERNEL_DS    MAKE_MM_SEG(0xFFFFFFFF)
+#  define USER_DS      MAKE_MM_SEG(TASK_SIZE - 1)
+#  endif
 
 # define get_ds()      (KERNEL_DS)
 # define get_fs()      (current_thread_info()->addr_limit)
-# define set_fs(x) \
-               do { current_thread_info()->addr_limit = (x); } while (0)
+# define set_fs(val)   (current_thread_info()->addr_limit = (val))
 
-# define segment_eq(a, b)              ((a).seg == (b).seg)
+# define segment_eq(a, b)      ((a).seg == (b).seg)
 
 # endif /* __ASSEMBLY__ */
 #endif /* _ASM_MICROBLAZE_SEGMENT_H */
index 9b98e8e6abae8e7cb020af7d5755bb2ba957b8ed..27f8dafd8c34b25f9f2b2463a62b025611d8de14 100644 (file)
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2007-2008 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2007-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2007-2009 PetaLogix
  * Copyright (C) 2006 Atmark Techno, Inc.
  *
  * This file is subject to the terms and conditions of the GNU General Public
@@ -18,7 +19,6 @@
 extern unsigned int boot_cpuid; /* move to smp.h */
 
 extern char cmd_line[COMMAND_LINE_SIZE];
-#  endif/* __KERNEL__ */
 
 void early_printk(const char *fmt, ...);
 
@@ -30,6 +30,11 @@ void setup_heartbeat(void);
 
 unsigned long long sched_clock(void);
 
+#   ifdef CONFIG_MMU
+extern void mmu_reset(void);
+extern void early_console_reg_tlb_alloc(unsigned int addr);
+#   endif /* CONFIG_MMU */
+
 void time_init(void);
 void init_IRQ(void);
 void machine_early_init(const char *cmdline, unsigned int ram,
@@ -40,5 +45,6 @@ void machine_shutdown(void);
 void machine_halt(void);
 void machine_power_off(void);
 
+#  endif/* __KERNEL__ */
 # endif /* __ASSEMBLY__ */
 #endif /* _ASM_MICROBLAZE_SETUP_H */
index 9676fad3486cfcf7c1853217a1c8f17a40948fc9..46bc2267d9497a751de01b75324120b384f4ce5f 100644 (file)
@@ -90,7 +90,7 @@
 
 # ifndef __ASSEMBLY__
 # include <linux/types.h>
-# include <asm-generic/signal.h>
+# include <asm-generic/signal-defs.h>
 
 /* Avoid too many header ordering problems. */
 struct siginfo;
index 5f18b8aed220fcb147c65a2965481640b93f988f..a15f77520bfd74ac2dff80e4780d37c7b3574ed9 100644 (file)
 
 #include <linux/posix_types.h>
 
+#define STAT_HAVE_NSEC 1
+
 struct stat {
-       unsigned int    st_dev;
+       unsigned long   st_dev;
        unsigned long   st_ino;
        unsigned int    st_mode;
        unsigned int    st_nlink;
        unsigned int    st_uid;
        unsigned int    st_gid;
-       unsigned int    st_rdev;
-       unsigned long   st_size;
-       unsigned long   st_blksize;
-       unsigned long   st_blocks;
-       unsigned long   st_atime;
-       unsigned long   __unused1; /* unsigned long  st_atime_nsec */
-       unsigned long   st_mtime;
-       unsigned long   __unused2; /* unsigned long  st_mtime_nsec */
-       unsigned long   st_ctime;
-       unsigned long   __unused3; /* unsigned long  st_ctime_nsec */
+       unsigned long   st_rdev;
+       unsigned long   __pad1;
+       long            st_size;
+       int             st_blksize;
+       int             __pad2;
+       long            st_blocks;
+       int             st_atime;
+       unsigned int    st_atime_nsec;
+       int             st_mtime;
+       unsigned int    st_mtime_nsec;
+       int             st_ctime;
+       unsigned int    st_ctime_nsec;
        unsigned long   __unused4;
        unsigned long   __unused5;
 };
 
 struct stat64 {
-       unsigned long long      st_dev;
-       unsigned long   __unused1;
-
-       unsigned long long      st_ino;
-
-       unsigned int    st_mode;
-       unsigned int    st_nlink;
-
-       unsigned int    st_uid;
-       unsigned int    st_gid;
-
-       unsigned long long      st_rdev;
-       unsigned long   __unused3;
-
-       long long       st_size;
-       unsigned long   st_blksize;
-
-       unsigned long   st_blocks; /* No. of 512-byte blocks allocated */
-       unsigned long   __unused4; /* future possible st_blocks high bits */
-
-       unsigned long   st_atime;
-       unsigned long   st_atime_nsec;
-
-       unsigned long   st_mtime;
-       unsigned long   st_mtime_nsec;
-
-       unsigned long   st_ctime;
-       unsigned long   st_ctime_nsec;
-
-       unsigned long   __unused8;
+       unsigned long long      st_dev;         /* Device.  */
+       unsigned long long      st_ino;         /* File serial number.  */
+       unsigned int            st_mode;        /* File mode.  */
+       unsigned int            st_nlink;       /* Link count.  */
+       unsigned int            st_uid;         /* User ID of the file's owner.  */
+       unsigned int            st_gid;         /* Group ID of the file's group. */
+       unsigned long long      st_rdev;        /* Device number, if device.  */
+       unsigned long long      __pad1;
+       long long               st_size;        /* Size of file, in bytes.  */
+       int                     st_blksize;     /* Optimal block size for I/O.  */
+       int                     __pad2;
+       long long               st_blocks;      /* Number 512-byte blocks allocated. */
+       int                     st_atime;       /* Time of last access.  */
+       unsigned int            st_atime_nsec;
+       int                     st_mtime;       /* Time of last modification.  */
+       unsigned int            st_mtime_nsec;
+       int                     st_ctime;       /* Time of last status change.  */
+       unsigned int            st_ctime_nsec;
+       unsigned int            __unused4;
+       unsigned int            __unused5;
 };
 
 #endif /* _ASM_MICROBLAZE_STAT_H */
+
index f7728c90fc186af92a1005400bf99ed7c9ec2387..aec2f59298b879d063e7f48d04ad20602695143d 100644 (file)
@@ -9,7 +9,7 @@
 #ifndef _ASM_MICROBLAZE_STRING_H
 #define _ASM_MICROBLAZE_STRING_H
 
-#ifndef __KERNEL__
+#ifdef __KERNEL__
 
 #define __HAVE_ARCH_MEMSET
 #define __HAVE_ARCH_MEMCPY
index 9cb4ff0edeb2fdf42a166b5feae54b41a18f51cb..ddea9eb31f8da76aa4a3a820a7ee8a9c0744dae7 100644 (file)
@@ -34,6 +34,9 @@ asmlinkage int sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize,
 asmlinkage int sys_sigaction(int sig, const struct old_sigaction *act,
                struct old_sigaction *oact);
 
+asmlinkage long sys_rt_sigaction(int sig, const struct sigaction __user *act,
+               struct sigaction __user *oact, size_t sigsetsize);
+
 asmlinkage int sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss,
                struct pt_regs *regs);
 
index 102d772586689ff209f342ac3e91d8f9bfb986a6..47a46d1fbe26fa4e1ddd1bf7b66f3290ea1facc8 100644 (file)
@@ -81,7 +81,7 @@ struct termio {
 
 #ifdef __KERNEL__
 
-#include <asm-generic/termios.h>
+#include <asm-generic/termios-base.h>
 
 #endif /* __KERNEL__ */
 
index 4c3943e3f40354809f99767c06f7147da9341522..7fac44498445f82595431e75c28fcf61a6731e21 100644 (file)
@@ -122,6 +122,8 @@ static inline struct thread_info *current_thread_info(void)
 #define TIF_SINGLESTEP         4
 #define TIF_IRET               5 /* return with iret */
 #define TIF_MEMDIE             6
+#define TIF_SYSCALL_AUDIT      9       /* syscall auditing active */
+#define TIF_SECCOMP            10      /* secure computing */
 #define TIF_FREEZE             14      /* Freezing for suspend */
 
 /* FIXME change in entry.S */
@@ -138,10 +140,17 @@ static inline struct thread_info *current_thread_info(void)
 #define _TIF_IRET              (1<<TIF_IRET)
 #define _TIF_POLLING_NRFLAG    (1<<TIF_POLLING_NRFLAG)
 #define _TIF_FREEZE            (1<<TIF_FREEZE)
+#define _TIF_SYSCALL_AUDIT     (1 << TIF_SYSCALL_AUDIT)
+#define _TIF_SECCOMP           (1 << TIF_SECCOMP)
 #define _TIF_KERNEL_TRACE      (1 << TIF_KERNEL_TRACE)
 
+/* work to do in syscall trace */
+#define _TIF_WORK_SYSCALL_MASK  (_TIF_SYSCALL_TRACE | _TIF_SINGLESTEP | \
+                                _TIF_SYSCALL_AUDIT | _TIF_SECCOMP)
+
 /* work to do on interrupt/exception return */
 #define _TIF_WORK_MASK         0x0000FFFE
+
 /* work to do on any return to u-space */
 #define _TIF_ALLWORK_MASK      0x0000FFFF
 
@@ -154,6 +163,17 @@ static inline struct thread_info *current_thread_info(void)
  */
 /* FPU was used by this task this quantum (SMP) */
 #define TS_USEDFPU             0x0001
+#define TS_RESTORE_SIGMASK     0x0002
+
+#ifndef __ASSEMBLY__
+#define HAVE_SET_RESTORE_SIGMASK 1
+static inline void set_restore_sigmask(void)
+{
+       struct thread_info *ti = current_thread_info();
+       ti->status |= TS_RESTORE_SIGMASK;
+       set_bit(TIF_SIGPENDING, (unsigned long *)&ti->flags);
+}
+#endif
 
 #endif /* __KERNEL__ */
 #endif /* _ASM_MICROBLAZE_THREAD_INFO_H */
index d1dfe3791127ba2223f58941e5d18e2328934209..c472d280113285780afb447948ff543bb6a57fd9 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2008-2009 PetaLogix
  * Copyright (C) 2006 Atmark Techno, Inc.
  *
  * This file is subject to the terms and conditions of the GNU General Public
 
 #include <asm-generic/tlb.h>
 
+#ifdef CONFIG_MMU
+#define tlb_start_vma(tlb, vma)                do { } while (0)
+#define tlb_end_vma(tlb, vma)          do { } while (0)
+#define __tlb_remove_tlb_entry(tlb, pte, address) do { } while (0)
+#endif
+
 #endif /* _ASM_MICROBLAZE_TLB_H */
index d7fe7629001b12ed8f52161d52f574a07c8d6806..eb31a0e8a7725cc843a3c19a16886fbae8e55ed6 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2008-2009 PetaLogix
  * Copyright (C) 2006 Atmark Techno, Inc.
  *
  * This file is subject to the terms and conditions of the GNU General Public
@@ -9,6 +11,50 @@
 #ifndef _ASM_MICROBLAZE_TLBFLUSH_H
 #define _ASM_MICROBLAZE_TLBFLUSH_H
 
+#ifdef CONFIG_MMU
+
+#include <linux/sched.h>
+#include <linux/threads.h>
+#include <asm/processor.h>     /* For TASK_SIZE */
+#include <asm/mmu.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+
+extern void _tlbie(unsigned long address);
+extern void _tlbia(void);
+
+#define __tlbia()      _tlbia()
+
+static inline void local_flush_tlb_all(void)
+       { __tlbia(); }
+static inline void local_flush_tlb_mm(struct mm_struct *mm)
+       { __tlbia(); }
+static inline void local_flush_tlb_page(struct vm_area_struct *vma,
+                               unsigned long vmaddr)
+       { _tlbie(vmaddr); }
+static inline void local_flush_tlb_range(struct vm_area_struct *vma,
+               unsigned long start, unsigned long end)
+       { __tlbia(); }
+
+#define flush_tlb_kernel_range(start, end)     do { } while (0)
+
+#define update_mmu_cache(vma, addr, pte)       do { } while (0)
+
+#define flush_tlb_all local_flush_tlb_all
+#define flush_tlb_mm local_flush_tlb_mm
+#define flush_tlb_page local_flush_tlb_page
+#define flush_tlb_range local_flush_tlb_range
+
+/*
+ * This is called in munmap when we have freed up some page-table
+ * pages.  We don't need to do anything here, there's nothing special
+ * about our page-table pages.  -- paulus
+ */
+static inline void flush_tlb_pgtables(struct mm_struct *mm,
+       unsigned long start, unsigned long end) { }
+
+#else /* CONFIG_MMU */
+
 #define flush_tlb()                            BUG()
 #define flush_tlb_all()                                BUG()
 #define flush_tlb_mm(mm)                       BUG()
@@ -17,4 +63,6 @@
 #define flush_tlb_pgtables(mm, start, end)     BUG()
 #define flush_tlb_kernel_range(start, end)     BUG()
 
+#endif /* CONFIG_MMU */
+
 #endif /* _ASM_MICROBLAZE_TLBFLUSH_H */
index 5a3ffc308e127cd76fb099c9f410c774095f7436..65adad61e7e90b4bbf05cc161caaf4a6a6757290 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2008-2009 PetaLogix
  * Copyright (C) 2006 Atmark Techno, Inc.
  *
  * This file is subject to the terms and conditions of the GNU General Public
 #define VERIFY_READ    0
 #define VERIFY_WRITE   1
 
+#define __clear_user(addr, n)  (memset((void *)(addr), 0, (n)), 0)
+
+#ifndef CONFIG_MMU
+
 extern int ___range_ok(unsigned long addr, unsigned long size);
 
 #define __range_ok(addr, size) \
@@ -34,68 +40,68 @@ extern int ___range_ok(unsigned long addr, unsigned long size);
 #define access_ok(type, addr, size) (__range_ok((addr), (size)) == 0)
 #define __access_ok(add, size) (__range_ok((addr), (size)) == 0)
 
-extern inline int bad_user_access_length(void)
-{
-       return 0;
-}
+/* Undefined function to trigger linker error */
+extern int bad_user_access_length(void);
+
 /* FIXME this is function for optimalization -> memcpy */
-#define __get_user(var, ptr)                                   \
-       ({                                                      \
-               int __gu_err = 0;                               \
-               switch (sizeof(*(ptr))) {                       \
-               case 1:                                         \
-               case 2:                                         \
-               case 4:                                         \
-                       (var) = *(ptr);                         \
-                       break;                                  \
-               case 8:                                         \
-                       memcpy((void *) &(var), (ptr), 8);      \
-                       break;                                  \
-               default:                                        \
-                       (var) = 0;                              \
-                       __gu_err = __get_user_bad();            \
-                       break;                                  \
-               }                                               \
-               __gu_err;                                       \
-       })
+#define __get_user(var, ptr)                           \
+({                                                     \
+       int __gu_err = 0;                               \
+       switch (sizeof(*(ptr))) {                       \
+       case 1:                                         \
+       case 2:                                         \
+       case 4:                                         \
+               (var) = *(ptr);                         \
+               break;                                  \
+       case 8:                                         \
+               memcpy((void *) &(var), (ptr), 8);      \
+               break;                                  \
+       default:                                        \
+               (var) = 0;                              \
+               __gu_err = __get_user_bad();            \
+               break;                                  \
+       }                                               \
+       __gu_err;                                       \
+})
 
 #define __get_user_bad()       (bad_user_access_length(), (-EFAULT))
 
+/* FIXME is not there defined __pu_val */
 #define __put_user(var, ptr)                                   \
-       ({                                                      \
-               int __pu_err = 0;                               \
-               switch (sizeof(*(ptr))) {                       \
-               case 1:                                         \
-               case 2:                                         \
-               case 4:                                         \
-                       *(ptr) = (var);                         \
-                       break;                                  \
-               case 8: {                                       \
-                       typeof(*(ptr)) __pu_val = var;          \
-                       memcpy(ptr, &__pu_val, sizeof(__pu_val));\
-                       }                                       \
-                       break;                                  \
-               default:                                        \
-                       __pu_err = __put_user_bad();            \
-                       break;                                  \
-               }                                                       \
-               __pu_err;                                               \
-       })
+({                                                             \
+       int __pu_err = 0;                                       \
+       switch (sizeof(*(ptr))) {                               \
+       case 1:                                                 \
+       case 2:                                                 \
+       case 4:                                                 \
+               *(ptr) = (var);                                 \
+               break;                                          \
+       case 8: {                                               \
+               typeof(*(ptr)) __pu_val = (var);                \
+               memcpy(ptr, &__pu_val, sizeof(__pu_val));       \
+               }                                               \
+               break;                                          \
+       default:                                                \
+               __pu_err = __put_user_bad();                    \
+               break;                                          \
+       }                                                       \
+       __pu_err;                                               \
+})
 
 #define __put_user_bad()       (bad_user_access_length(), (-EFAULT))
 
-#define put_user(x, ptr)       __put_user(x, ptr)
-#define get_user(x, ptr)       __get_user(x, ptr)
+#define put_user(x, ptr)       __put_user((x), (ptr))
+#define get_user(x, ptr)       __get_user((x), (ptr))
 
-#define copy_to_user(to, from, n)              (memcpy(to, from, n), 0)
-#define copy_from_user(to, from, n)            (memcpy(to, from, n), 0)
+#define copy_to_user(to, from, n)      (memcpy((to), (from), (n)), 0)
+#define copy_from_user(to, from, n)    (memcpy((to), (from), (n)), 0)
 
-#define __copy_to_user(to, from, n)            (copy_to_user(to, from, n))
-#define __copy_from_user(to, from, n)          (copy_from_user(to, from, n))
-#define __copy_to_user_inatomic(to, from, n)   (__copy_to_user(to, from, n))
-#define __copy_from_user_inatomic(to, from, n) (__copy_from_user(to, from, n))
-
-#define __clear_user(addr, n)  (memset((void *)addr, 0, n), 0)
+#define __copy_to_user(to, from, n)    (copy_to_user((to), (from), (n)))
+#define __copy_from_user(to, from, n)  (copy_from_user((to), (from), (n)))
+#define __copy_to_user_inatomic(to, from, n) \
+                       (__copy_to_user((to), (from), (n)))
+#define __copy_from_user_inatomic(to, from, n) \
+                       (__copy_from_user((to), (from), (n)))
 
 static inline unsigned long clear_user(void *addr, unsigned long size)
 {
@@ -104,13 +110,200 @@ static inline unsigned long clear_user(void *addr, unsigned long size)
        return size;
 }
 
-/* Returns 0 if exception not found and fixup otherwise. */
+/* Returns 0 if exception not found and fixup otherwise.  */
 extern unsigned long search_exception_table(unsigned long);
 
+extern long strncpy_from_user(char *dst, const char *src, long count);
+extern long strnlen_user(const char *src, long count);
+
+#else /* CONFIG_MMU */
+
+/*
+ * Address is valid if:
+ *  - "addr", "addr + size" and "size" are all below the limit
+ */
+#define access_ok(type, addr, size) \
+       (get_fs().seg > (((unsigned long)(addr)) | \
+               (size) | ((unsigned long)(addr) + (size))))
+
+/* || printk("access_ok failed for %s at 0x%08lx (size %d), seg 0x%08x\n",
+ type?"WRITE":"READ",addr,size,get_fs().seg)) */
+
+/*
+ * All the __XXX versions macros/functions below do not perform
+ * access checking. It is assumed that the necessary checks have been
+ * already performed before the finction (macro) is called.
+ */
+
+#define get_user(x, ptr)                                               \
+({                                                                     \
+       access_ok(VERIFY_READ, (ptr), sizeof(*(ptr)))                   \
+               ? __get_user((x), (ptr)) : -EFAULT;                     \
+})
+
+#define put_user(x, ptr)                                               \
+({                                                                     \
+       access_ok(VERIFY_WRITE, (ptr), sizeof(*(ptr)))                  \
+               ? __put_user((x), (ptr)) : -EFAULT;                     \
+})
+
+#define __get_user(x, ptr)                                             \
+({                                                                     \
+       unsigned long __gu_val;                                         \
+       /*unsigned long __gu_ptr = (unsigned long)(ptr);*/              \
+       long __gu_err;                                                  \
+       switch (sizeof(*(ptr))) {                                       \
+       case 1:                                                         \
+               __get_user_asm("lbu", (ptr), __gu_val, __gu_err);       \
+               break;                                                  \
+       case 2:                                                         \
+               __get_user_asm("lhu", (ptr), __gu_val, __gu_err);       \
+               break;                                                  \
+       case 4:                                                         \
+               __get_user_asm("lw", (ptr), __gu_val, __gu_err);        \
+               break;                                                  \
+       default:                                                        \
+               __gu_val = 0; __gu_err = -EINVAL;                       \
+       }                                                               \
+       x = (__typeof__(*(ptr))) __gu_val;                              \
+       __gu_err;                                                       \
+})
+
+#define __get_user_asm(insn, __gu_ptr, __gu_val, __gu_err)             \
+({                                                                     \
+       __asm__ __volatile__ (                                          \
+                       "1:"    insn    " %1, %2, r0;                   \
+                               addk    %0, r0, r0;                     \
+                       2:                                              \
+                       .section .fixup,\"ax\";                         \
+                       3:      brid    2b;                             \
+                               addik   %0, r0, %3;                     \
+                       .previous;                                      \
+                       .section __ex_table,\"a\";                      \
+                       .word   1b,3b;                                  \
+                       .previous;"                                     \
+               : "=r"(__gu_err), "=r"(__gu_val)                        \
+               : "r"(__gu_ptr), "i"(-EFAULT)                           \
+       );                                                              \
+})
+
+#define __put_user(x, ptr)                                             \
+({                                                                     \
+       __typeof__(*(ptr)) __gu_val = x;                                \
+       long __gu_err = 0;                                              \
+       switch (sizeof(__gu_val)) {                                     \
+       case 1:                                                         \
+               __put_user_asm("sb", (ptr), __gu_val, __gu_err);        \
+               break;                                                  \
+       case 2:                                                         \
+               __put_user_asm("sh", (ptr), __gu_val, __gu_err);        \
+               break;                                                  \
+       case 4:                                                         \
+               __put_user_asm("sw", (ptr), __gu_val, __gu_err);        \
+               break;                                                  \
+       case 8:                                                         \
+               __put_user_asm_8((ptr), __gu_val, __gu_err);            \
+               break;                                                  \
+       default:                                                        \
+               __gu_err = -EINVAL;                                     \
+       }                                                               \
+       __gu_err;                                                       \
+})
+
+#define __put_user_asm_8(__gu_ptr, __gu_val, __gu_err) \
+({                                                     \
+__asm__ __volatile__ ("        lwi     %0, %1, 0;              \
+               1:      swi     %0, %2, 0;              \
+                       lwi     %0, %1, 4;              \
+               2:      swi     %0, %2, 4;              \
+                       addk    %0,r0,r0;               \
+               3:                                      \
+               .section .fixup,\"ax\";                 \
+               4:      brid    3b;                     \
+                       addik   %0, r0, %3;             \
+               .previous;                              \
+               .section __ex_table,\"a\";              \
+               .word   1b,4b,2b,4b;                    \
+               .previous;"                             \
+       : "=&r"(__gu_err)                               \
+       : "r"(&__gu_val),                               \
+       "r"(__gu_ptr), "i"(-EFAULT)                     \
+       );                                              \
+})
+
+#define __put_user_asm(insn, __gu_ptr, __gu_val, __gu_err)     \
+({                                                             \
+       __asm__ __volatile__ (                                  \
+                       "1:"    insn    " %1, %2, r0;           \
+                               addk    %0, r0, r0;             \
+                       2:                                      \
+                       .section .fixup,\"ax\";                 \
+                       3:      brid    2b;                     \
+                               addik   %0, r0, %3;             \
+                       .previous;                              \
+                       .section __ex_table,\"a\";              \
+                       .word   1b,3b;                          \
+                       .previous;"                             \
+               : "=r"(__gu_err)                                \
+               : "r"(__gu_val), "r"(__gu_ptr), "i"(-EFAULT)    \
+       );                                                      \
+})
+
+/*
+ * Return: number of not copied bytes, i.e. 0 if OK or non-zero if fail.
+ */
+static inline int clear_user(char *to, int size)
+{
+       if (size && access_ok(VERIFY_WRITE, to, size)) {
+               __asm__ __volatile__ ("                         \
+                               1:                              \
+                                       sb      r0, %2, r0;     \
+                                       addik   %0, %0, -1;     \
+                                       bneid   %0, 1b;         \
+                                       addik   %2, %2, 1;      \
+                               2:                              \
+                               .section __ex_table,\"a\";      \
+                               .word   1b,2b;                  \
+                               .section .text;"                \
+                       : "=r"(size)                            \
+                       : "0"(size), "r"(to)
+               );
+       }
+       return size;
+}
+
+extern unsigned long __copy_tofrom_user(void __user *to,
+               const void __user *from, unsigned long size);
+
+#define copy_to_user(to, from, n)                                      \
+       (access_ok(VERIFY_WRITE, (to), (n)) ?                           \
+               __copy_tofrom_user((void __user *)(to),                 \
+                       (__force const void __user *)(from), (n))       \
+               : -EFAULT)
+
+#define __copy_to_user(to, from, n)    copy_to_user((to), (from), (n))
+#define __copy_to_user_inatomic(to, from, n)   copy_to_user((to), (from), (n))
+
+#define copy_from_user(to, from, n)                                    \
+       (access_ok(VERIFY_READ, (from), (n)) ?                          \
+               __copy_tofrom_user((__force void __user *)(to),         \
+                       (void __user *)(from), (n))                     \
+               : -EFAULT)
+
+#define __copy_from_user(to, from, n)  copy_from_user((to), (from), (n))
+#define __copy_from_user_inatomic(to, from, n) \
+               copy_from_user((to), (from), (n))
+
+extern int __strncpy_user(char *to, const char __user *from, int len);
+extern int __strnlen_user(const char __user *sstr, int len);
+
+#define strncpy_from_user(to, from, len)       \
+               (access_ok(VERIFY_READ, from, 1) ?      \
+                       __strncpy_user(to, from, len) : -EFAULT)
+#define strnlen_user(str, len) \
+               (access_ok(VERIFY_READ, str, 1) ? __strnlen_user(str, len) : 0)
 
-extern long strncpy_from_user(char *dst, const char __user *src, long count);
-extern long strnlen_user(const char __user *src, long count);
-extern long __strncpy_from_user(char *dst, const char __user *src, long count);
+#endif /* CONFIG_MMU */
 
 /*
  * The exception table consists of pairs of addresses: the first is the
index 9d66b640c910635f5c6baa56ba4e7d2c3ba46d3a..3658d91ac0fb95ead00dbc3bca4fd9e0b8d36457 100644 (file)
@@ -12,7 +12,8 @@
 
 # ifdef __KERNEL__
 
-# include <linux/unaligned/access_ok.h>
+# include <linux/unaligned/be_struct.h>
+# include <linux/unaligned/le_byteshift.h>
 # include <linux/unaligned/generic.h>
 
 # define get_unaligned __get_unaligned_be
index da94bec4ecbaa71b428214b4ad41aa31c7e83371..f4a5e19a20eb98f6848e01f3d2708d8e105446b2 100644 (file)
@@ -15,5 +15,6 @@ obj-$(CONFIG_EARLY_PRINTK)    += early_printk.o
 obj-$(CONFIG_SELFMOD)          += selfmod.o
 obj-$(CONFIG_HEART_BEAT)       += heartbeat.o
 obj-$(CONFIG_MODULES)          += microblaze_ksyms.o module.o
+obj-$(CONFIG_MMU)              += misc.o
 
 obj-y  += entry$(MMUEXT).o
index aabd9e9423a651103c4eb379460446ba82297833..7bc7b68f97db3089952ee59bfe8adcff29c95a0b 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2007-2009 Michal Simek <monstr@monstr.eu>
  * Copyright (C) 2007-2009 PetaLogix
  * Copyright (C) 2006 Atmark Techno, Inc.
  *
@@ -68,16 +69,26 @@ int main(int argc, char *argv[])
 
        /* struct task_struct */
        DEFINE(TS_THREAD_INFO, offsetof(struct task_struct, stack));
+#ifdef CONFIG_MMU
+       DEFINE(TASK_STATE, offsetof(struct task_struct, state));
+       DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags));
+       DEFINE(TASK_PTRACE, offsetof(struct task_struct, ptrace));
+       DEFINE(TASK_BLOCKED, offsetof(struct task_struct, blocked));
+       DEFINE(TASK_MM, offsetof(struct task_struct, mm));
+       DEFINE(TASK_ACTIVE_MM, offsetof(struct task_struct, active_mm));
+       DEFINE(TASK_PID, offsetof(struct task_struct, pid));
+       DEFINE(TASK_THREAD, offsetof(struct task_struct, thread));
+       DEFINE(THREAD_KSP, offsetof(struct thread_struct, ksp));
+       BLANK();
+
+       DEFINE(PGDIR, offsetof(struct thread_struct, pgdir));
+       BLANK();
+#endif
 
        /* struct thread_info */
        DEFINE(TI_TASK, offsetof(struct thread_info, task));
-       DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain));
        DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
-       DEFINE(TI_STATUS, offsetof(struct thread_info, status));
-       DEFINE(TI_CPU, offsetof(struct thread_info, cpu));
-       DEFINE(TI_PRE_COUNT, offsetof(struct thread_info, preempt_count));
        DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit));
-       DEFINE(TI_RESTART_BLOCK, offsetof(struct thread_info, restart_block));
        DEFINE(TI_CPU_CONTEXT, offsetof(struct thread_info, cpu_context));
        BLANK();
 
index 4b0f0fdb9ca0ba1649703cc7b1ae76055825d680..7de84923ba07d90b60406c05a777030c1204b5a6 100644 (file)
@@ -87,6 +87,9 @@ int __init setup_early_printk(char *opt)
        base_addr = early_uartlite_console();
        if (base_addr) {
                early_console_initialized = 1;
+#ifdef CONFIG_MMU
+               early_console_reg_tlb_alloc(base_addr);
+#endif
                early_printk("early_printk_console is enabled at 0x%08x\n",
                                                        base_addr);
 
index f24b1268baaf5c08b834f9c5c5052db5e063694b..1fce6b803f54905b69ffadca0162a4db0593ed66 100644 (file)
@@ -10,7 +10,7 @@
 
 #include <linux/linkage.h>
 #include <asm/thread_info.h>
-#include <asm/errno.h>
+#include <linux/errno.h>
 #include <asm/entry.h>
 #include <asm/asm-offsets.h>
 #include <asm/registers.h>
diff --git a/arch/microblaze/kernel/entry.S b/arch/microblaze/kernel/entry.S
new file mode 100644 (file)
index 0000000..91a0e7b
--- /dev/null
@@ -0,0 +1,1116 @@
+/*
+ * Low-level system-call handling, trap handlers and context-switching
+ *
+ * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2008-2009 PetaLogix
+ * Copyright (C) 2003          John Williams <jwilliams@itee.uq.edu.au>
+ * Copyright (C) 2001,2002     NEC Corporation
+ * Copyright (C) 2001,2002     Miles Bader <miles@gnu.org>
+ *
+ * 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.
+ *
+ * Written by Miles Bader <miles@gnu.org>
+ * Heavily modified by John Williams for Microblaze
+ */
+
+#include <linux/sys.h>
+#include <linux/linkage.h>
+
+#include <asm/entry.h>
+#include <asm/current.h>
+#include <asm/processor.h>
+#include <asm/exceptions.h>
+#include <asm/asm-offsets.h>
+#include <asm/thread_info.h>
+
+#include <asm/page.h>
+#include <asm/unistd.h>
+
+#include <linux/errno.h>
+#include <asm/signal.h>
+
+/* The size of a state save frame. */
+#define STATE_SAVE_SIZE                (PT_SIZE + STATE_SAVE_ARG_SPACE)
+
+/* The offset of the struct pt_regs in a `state save frame' on the stack. */
+#define PTO    STATE_SAVE_ARG_SPACE /* 24 the space for args */
+
+#define C_ENTRY(name)  .globl name; .align 4; name
+
+/*
+ * Various ways of setting and clearing BIP in flags reg.
+ * This is mucky, but necessary using microblaze version that
+ * allows msr ops to write to BIP
+ */
+#if CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR
+       .macro  clear_bip
+       msrclr  r11, MSR_BIP
+       nop
+       .endm
+
+       .macro  set_bip
+       msrset  r11, MSR_BIP
+       nop
+       .endm
+
+       .macro  clear_eip
+       msrclr  r11, MSR_EIP
+       nop
+       .endm
+
+       .macro  set_ee
+       msrset  r11, MSR_EE
+       nop
+       .endm
+
+       .macro  disable_irq
+       msrclr  r11, MSR_IE
+       nop
+       .endm
+
+       .macro  enable_irq
+       msrset  r11, MSR_IE
+       nop
+       .endm
+
+       .macro  set_ums
+       msrset  r11, MSR_UMS
+       nop
+       msrclr  r11, MSR_VMS
+       nop
+       .endm
+
+       .macro  set_vms
+       msrclr  r11, MSR_UMS
+       nop
+       msrset  r11, MSR_VMS
+       nop
+       .endm
+
+       .macro  clear_vms_ums
+       msrclr  r11, MSR_VMS
+       nop
+       msrclr  r11, MSR_UMS
+       nop
+       .endm
+#else
+       .macro  clear_bip
+       mfs     r11, rmsr
+       nop
+       andi    r11, r11, ~MSR_BIP
+       mts     rmsr, r11
+       nop
+       .endm
+
+       .macro  set_bip
+       mfs     r11, rmsr
+       nop
+       ori     r11, r11, MSR_BIP
+       mts     rmsr, r11
+       nop
+       .endm
+
+       .macro  clear_eip
+       mfs     r11, rmsr
+       nop
+       andi    r11, r11, ~MSR_EIP
+       mts     rmsr, r11
+       nop
+       .endm
+
+       .macro  set_ee
+       mfs     r11, rmsr
+       nop
+       ori     r11, r11, MSR_EE
+       mts     rmsr, r11
+       nop
+       .endm
+
+       .macro  disable_irq
+       mfs     r11, rmsr
+       nop
+       andi    r11, r11, ~MSR_IE
+       mts     rmsr, r11
+       nop
+       .endm
+
+       .macro  enable_irq
+       mfs     r11, rmsr
+       nop
+       ori     r11, r11, MSR_IE
+       mts     rmsr, r11
+       nop
+       .endm
+
+       .macro set_ums
+       mfs     r11, rmsr
+       nop
+       ori     r11, r11, MSR_VMS
+       andni   r11, r11, MSR_UMS
+       mts     rmsr, r11
+       nop
+       .endm
+
+       .macro  set_vms
+       mfs     r11, rmsr
+       nop
+       ori     r11, r11, MSR_VMS
+       andni   r11, r11, MSR_UMS
+       mts     rmsr, r11
+       nop
+       .endm
+
+       .macro  clear_vms_ums
+       mfs     r11, rmsr
+       nop
+       andni   r11, r11, (MSR_VMS|MSR_UMS)
+       mts     rmsr,r11
+       nop
+       .endm
+#endif
+
+/* Define how to call high-level functions. With MMU, virtual mode must be
+ * enabled when calling the high-level function. Clobbers R11.
+ * VM_ON, VM_OFF, DO_JUMP_BIPCLR, DO_CALL
+ */
+
+/* turn on virtual protected mode save */
+#define VM_ON          \
+       set_ums;                \
+       rted    r0, 2f; \
+2: nop;
+
+/* turn off virtual protected mode save and user mode save*/
+#define VM_OFF                 \
+       clear_vms_ums;                  \
+       rted    r0, TOPHYS(1f); \
+1: nop;
+
+#define SAVE_REGS \
+       swi     r2, r1, PTO+PT_R2;      /* Save SDA */                  \
+       swi     r5, r1, PTO+PT_R5;                                      \
+       swi     r6, r1, PTO+PT_R6;                                      \
+       swi     r7, r1, PTO+PT_R7;                                      \
+       swi     r8, r1, PTO+PT_R8;                                      \
+       swi     r9, r1, PTO+PT_R9;                                      \
+       swi     r10, r1, PTO+PT_R10;                                    \
+       swi     r11, r1, PTO+PT_R11;    /* save clobbered regs after rval */\
+       swi     r12, r1, PTO+PT_R12;                                    \
+       swi     r13, r1, PTO+PT_R13;    /* Save SDA2 */                 \
+       swi     r14, r1, PTO+PT_PC;     /* PC, before IRQ/trap */       \
+       swi     r15, r1, PTO+PT_R15;    /* Save LP */                   \
+       swi     r18, r1, PTO+PT_R18;    /* Save asm scratch reg */      \
+       swi     r19, r1, PTO+PT_R19;                                    \
+       swi     r20, r1, PTO+PT_R20;                                    \
+       swi     r21, r1, PTO+PT_R21;                                    \
+       swi     r22, r1, PTO+PT_R22;                                    \
+       swi     r23, r1, PTO+PT_R23;                                    \
+       swi     r24, r1, PTO+PT_R24;                                    \
+       swi     r25, r1, PTO+PT_R25;                                    \
+       swi     r26, r1, PTO+PT_R26;                                    \
+       swi     r27, r1, PTO+PT_R27;                                    \
+       swi     r28, r1, PTO+PT_R28;                                    \
+       swi     r29, r1, PTO+PT_R29;                                    \
+       swi     r30, r1, PTO+PT_R30;                                    \
+       swi     r31, r1, PTO+PT_R31;    /* Save current task reg */     \
+       mfs     r11, rmsr;              /* save MSR */                  \
+       nop;                                                            \
+       swi     r11, r1, PTO+PT_MSR;
+
+#define RESTORE_REGS \
+       lwi     r11, r1, PTO+PT_MSR;                                    \
+       mts     rmsr , r11;                                             \
+       nop;                                                            \
+       lwi     r2, r1, PTO+PT_R2;      /* restore SDA */               \
+       lwi     r5, r1, PTO+PT_R5;                                      \
+       lwi     r6, r1, PTO+PT_R6;                                      \
+       lwi     r7, r1, PTO+PT_R7;                                      \
+       lwi     r8, r1, PTO+PT_R8;                                      \
+       lwi     r9, r1, PTO+PT_R9;                                      \
+       lwi     r10, r1, PTO+PT_R10;                                    \
+       lwi     r11, r1, PTO+PT_R11;    /* restore clobbered regs after rval */\
+       lwi     r12, r1, PTO+PT_R12;                                    \
+       lwi     r13, r1, PTO+PT_R13;    /* restore SDA2 */              \
+       lwi     r14, r1, PTO+PT_PC;     /* RESTORE_LINK PC, before IRQ/trap */\
+       lwi     r15, r1, PTO+PT_R15;    /* restore LP */                \
+       lwi     r18, r1, PTO+PT_R18;    /* restore asm scratch reg */   \
+       lwi     r19, r1, PTO+PT_R19;                                    \
+       lwi     r20, r1, PTO+PT_R20;                                    \
+       lwi     r21, r1, PTO+PT_R21;                                    \
+       lwi     r22, r1, PTO+PT_R22;                                    \
+       lwi     r23, r1, PTO+PT_R23;                                    \
+       lwi     r24, r1, PTO+PT_R24;                                    \
+       lwi     r25, r1, PTO+PT_R25;                                    \
+       lwi     r26, r1, PTO+PT_R26;                                    \
+       lwi     r27, r1, PTO+PT_R27;                                    \
+       lwi     r28, r1, PTO+PT_R28;                                    \
+       lwi     r29, r1, PTO+PT_R29;                                    \
+       lwi     r30, r1, PTO+PT_R30;                                    \
+       lwi     r31, r1, PTO+PT_R31;    /* Restore cur task reg */
+
+.text
+
+/*
+ * User trap.
+ *
+ * System calls are handled here.
+ *
+ * Syscall protocol:
+ * Syscall number in r12, args in r5-r10
+ * Return value in r3
+ *
+ * Trap entered via brki instruction, so BIP bit is set, and interrupts
+ * are masked. This is nice, means we don't have to CLI before state save
+ */
+C_ENTRY(_user_exception):
+       swi     r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */
+       addi    r14, r14, 4     /* return address is 4 byte after call */
+       swi     r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* Save r11 */
+
+       lwi     r11, r0, TOPHYS(PER_CPU(KM));/* See if already in kernel mode.*/
+       beqi    r11, 1f;                /* Jump ahead if coming from user */
+/* Kernel-mode state save. */
+       lwi     r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/
+       tophys(r1,r11);
+       swi     r11, r1, (PT_R1-PT_SIZE); /* Save original SP. */
+       lwi     r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */
+
+       addik   r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */
+       SAVE_REGS
+
+       addi    r11, r0, 1;             /* Was in kernel-mode. */
+       swi     r11, r1, PTO+PT_MODE; /* pt_regs -> kernel mode */
+       brid    2f;
+       nop;                            /* Fill delay slot */
+
+/* User-mode state save.  */
+1:
+       lwi     r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */
+       lwi     r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */
+       tophys(r1,r1);
+       lwi     r1, r1, TS_THREAD_INFO; /* get stack from task_struct */
+/* calculate kernel stack pointer from task struct 8k */
+       addik   r1, r1, THREAD_SIZE;
+       tophys(r1,r1);
+
+       addik   r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack.  */
+       SAVE_REGS
+
+       swi     r0, r1, PTO+PT_MODE;                    /* Was in user-mode. */
+       lwi     r11, r0, TOPHYS(PER_CPU(ENTRY_SP));
+       swi     r11, r1, PTO+PT_R1;             /* Store user SP.  */
+       addi    r11, r0, 1;
+       swi     r11, r0, TOPHYS(PER_CPU(KM));   /* Now we're in kernel-mode.  */
+2:     lwi     r31, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */
+       /* Save away the syscall number.  */
+       swi     r12, r1, PTO+PT_R0;
+       tovirt(r1,r1)
+
+       la      r15, r0, ret_from_trap-8
+/* where the trap should return need -8 to adjust for rtsd r15, 8*/
+/* Jump to the appropriate function for the system call number in r12
+ * (r12 is not preserved), or return an error if r12 is not valid. The LP
+ * register should point to the location where
+ * the called function should return.  [note that MAKE_SYS_CALL uses label 1] */
+       /* See if the system call number is valid.  */
+       addi    r11, r12, -__NR_syscalls;
+       bgei    r11,1f;
+       /* Figure out which function to use for this system call.  */
+       /* Note Microblaze barrel shift is optional, so don't rely on it */
+       add     r12, r12, r12;                  /* convert num -> ptr */
+       add     r12, r12, r12;
+
+       /* Trac syscalls and stored them to r0_ram */
+       lwi     r3, r12, 0x400 + TOPHYS(r0_ram)
+       addi    r3, r3, 1
+       swi     r3, r12, 0x400 + TOPHYS(r0_ram)
+
+       lwi     r12, r12, TOPHYS(sys_call_table); /* Function ptr */
+       /* Make the system call.  to r12*/
+       set_vms;
+       rtid    r12, 0;
+       nop;
+       /* The syscall number is invalid, return an error.  */
+1:     VM_ON;  /* RETURN() expects virtual mode*/
+       addi    r3, r0, -ENOSYS;
+       rtsd    r15,8;          /* looks like a normal subroutine return */
+       or      r0, r0, r0
+
+
+/* Entry point used to return from a syscall/trap.  */
+/* We re-enable BIP bit before state restore */
+C_ENTRY(ret_from_trap):
+       set_bip;                        /*  Ints masked for state restore*/
+       lwi     r11, r1, PTO+PT_MODE;
+/* See if returning to kernel mode, if so, skip resched &c.  */
+       bnei    r11, 2f;
+
+       /* We're returning to user mode, so check for various conditions that
+        * trigger rescheduling. */
+       /* Get current task ptr into r11 */
+       add     r11, r0, CURRENT_TASK;  /* Get current task ptr into r11 */
+       lwi     r11, r11, TS_THREAD_INFO;       /* get thread info */
+       lwi     r11, r11, TI_FLAGS;             /* get flags in thread info */
+       andi    r11, r11, _TIF_NEED_RESCHED;
+       beqi    r11, 5f;
+
+       swi     r3, r1, PTO + PT_R3; /* store syscall result */
+       swi     r4, r1, PTO + PT_R4;
+       bralid  r15, schedule;  /* Call scheduler */
+       nop;                            /* delay slot */
+       lwi     r3, r1, PTO + PT_R3; /* restore syscall result */
+       lwi     r4, r1, PTO + PT_R4;
+
+       /* Maybe handle a signal */
+5:     add     r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */
+       lwi     r11, r11, TS_THREAD_INFO;       /* get thread info */
+       lwi     r11, r11, TI_FLAGS;     /* get flags in thread info */
+       andi    r11, r11, _TIF_SIGPENDING;
+       beqi    r11, 1f;                /* Signals to handle, handle them */
+
+       swi     r3, r1, PTO + PT_R3; /* store syscall result */
+       swi     r4, r1, PTO + PT_R4;
+       la      r5, r1, PTO;            /* Arg 1: struct pt_regs *regs */
+       add     r6, r0, r0;             /* Arg 2: sigset_t *oldset */
+       addi    r7, r0, 1;              /* Arg 3: int in_syscall */
+       bralid  r15, do_signal; /* Handle any signals */
+       nop;
+       lwi     r3, r1, PTO + PT_R3; /* restore syscall result */
+       lwi     r4, r1, PTO + PT_R4;
+
+/* Finally, return to user state.  */
+1:     swi     r0, r0, PER_CPU(KM);    /* Now officially in user state. */
+       add     r11, r0, CURRENT_TASK;  /* Get current task ptr into r11 */
+       swi     r11, r0, PER_CPU(CURRENT_SAVE); /* save current */
+       VM_OFF;
+       tophys(r1,r1);
+       RESTORE_REGS;
+       addik   r1, r1, STATE_SAVE_SIZE         /* Clean up stack space.  */
+       lwi     r1, r1, PT_R1 - PT_SIZE;/* Restore user stack pointer. */
+       bri     6f;
+
+/* Return to kernel state.  */
+2:     VM_OFF;
+       tophys(r1,r1);
+       RESTORE_REGS;
+       addik   r1, r1, STATE_SAVE_SIZE         /* Clean up stack space.  */
+       tovirt(r1,r1);
+6:
+TRAP_return:           /* Make global symbol for debugging */
+       rtbd    r14, 0; /* Instructions to return from an IRQ */
+       nop;
+
+
+/* These syscalls need access to the struct pt_regs on the stack, so we
+   implement them in assembly (they're basically all wrappers anyway).  */
+
+C_ENTRY(sys_fork_wrapper):
+       addi    r5, r0, SIGCHLD                 /* Arg 0: flags */
+       lwi     r6, r1, PTO+PT_R1       /* Arg 1: child SP (use parent's) */
+       la      r7, r1, PTO                     /* Arg 2: parent context */
+       add     r8. r0, r0                      /* Arg 3: (unused) */
+       add     r9, r0, r0;                     /* Arg 4: (unused) */
+       add     r10, r0, r0;                    /* Arg 5: (unused) */
+       brid    do_fork         /* Do real work (tail-call) */
+       nop;
+
+/* This the initial entry point for a new child thread, with an appropriate
+   stack in place that makes it look the the child is in the middle of an
+   syscall.  This function is actually `returned to' from switch_thread
+   (copy_thread makes ret_from_fork the return address in each new thread's
+   saved context).  */
+C_ENTRY(ret_from_fork):
+       bralid  r15, schedule_tail; /* ...which is schedule_tail's arg */
+       add     r3, r5, r0;     /* switch_thread returns the prev task */
+                               /* ( in the delay slot ) */
+       add     r3, r0, r0;     /* Child's fork call should return 0. */
+       brid    ret_from_trap;  /* Do normal trap return */
+       nop;
+
+C_ENTRY(sys_vfork_wrapper):
+       la      r5, r1, PTO
+       brid    sys_vfork       /* Do real work (tail-call) */
+       nop
+
+C_ENTRY(sys_clone_wrapper):
+       bnei    r6, 1f;                 /* See if child SP arg (arg 1) is 0. */
+       lwi     r6, r1, PTO+PT_R1;      /* If so, use paret's stack ptr */
+1:     la      r7, r1, PTO;                    /* Arg 2: parent context */
+       add     r8, r0, r0;                     /* Arg 3: (unused) */
+       add     r9, r0, r0;                     /* Arg 4: (unused) */
+       add     r10, r0, r0;                    /* Arg 5: (unused) */
+       brid    do_fork         /* Do real work (tail-call) */
+       nop;
+
+C_ENTRY(sys_execve_wrapper):
+       la      r8, r1, PTO;            /* add user context as 4th arg */
+       brid    sys_execve;     /* Do real work (tail-call).*/
+       nop;
+
+C_ENTRY(sys_sigsuspend_wrapper):
+       swi     r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */
+       swi     r4, r1, PTO+PT_R4;
+       la      r6, r1, PTO;            /* add user context as 2nd arg */
+       bralid  r15, sys_sigsuspend; /* Do real work.*/
+       nop;
+       lwi     r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */
+       lwi     r4, r1, PTO+PT_R4;
+       bri ret_from_trap /* fall through will not work here due to align */
+       nop;
+
+C_ENTRY(sys_rt_sigsuspend_wrapper):
+       swi     r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */
+       swi     r4, r1, PTO+PT_R4;
+       la      r7, r1, PTO;            /* add user context as 3rd arg */
+       brlid   r15, sys_rt_sigsuspend; /* Do real work.*/
+       nop;
+       lwi     r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */
+       lwi     r4, r1, PTO+PT_R4;
+       bri ret_from_trap /* fall through will not work here due to align */
+       nop;
+
+
+C_ENTRY(sys_sigreturn_wrapper):
+       swi     r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */
+       swi     r4, r1, PTO+PT_R4;
+       la      r5, r1, PTO;            /* add user context as 1st arg */
+       brlid   r15, sys_sigreturn;     /* Do real work.*/
+       nop;
+       lwi     r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */
+       lwi     r4, r1, PTO+PT_R4;
+       bri ret_from_trap /* fall through will not work here due to align */
+       nop;
+
+C_ENTRY(sys_rt_sigreturn_wrapper):
+       swi     r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */
+       swi     r4, r1, PTO+PT_R4;
+       la      r5, r1, PTO;            /* add user context as 1st arg */
+       brlid   r15, sys_rt_sigreturn   /* Do real work */
+       nop;
+       lwi     r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */
+       lwi     r4, r1, PTO+PT_R4;
+       bri ret_from_trap /* fall through will not work here due to align */
+       nop;
+
+/*
+ * HW EXCEPTION rutine start
+ */
+
+#define SAVE_STATE     \
+       swi     r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* Save r11 */  \
+       set_bip;        /*equalize initial state for all possible entries*/\
+       clear_eip;                                                      \
+       enable_irq;                                                     \
+       set_ee;                                                         \
+       /* See if already in kernel mode.*/                             \
+       lwi     r11, r0, TOPHYS(PER_CPU(KM));                           \
+       beqi    r11, 1f;                /* Jump ahead if coming from user */\
+       /* Kernel-mode state save.  */                                  \
+       /* Reload kernel stack-ptr. */                                  \
+       lwi     r11, r0, TOPHYS(PER_CPU(ENTRY_SP));                     \
+       tophys(r1,r11);                                                 \
+       swi     r11, r1, (PT_R1-PT_SIZE); /* Save original SP. */       \
+       lwi     r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */\
+       addik   r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack.  */\
+       /* store return registers separately because                    \
+        * this macros is use for others exceptions */                  \
+       swi     r3, r1, PTO + PT_R3;                                    \
+       swi     r4, r1, PTO + PT_R4;                                    \
+       SAVE_REGS                                                       \
+       /* PC, before IRQ/trap - this is one instruction above */       \
+       swi     r17, r1, PTO+PT_PC;                                     \
+                                                                       \
+       addi    r11, r0, 1;             /* Was in kernel-mode.  */      \
+       swi     r11, r1, PTO+PT_MODE;                                   \
+       brid    2f;                                                     \
+       nop;                            /* Fill delay slot */           \
+1:     /* User-mode state save.  */                                    \
+       lwi     r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */\
+       lwi     r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */\
+       tophys(r1,r1);                                                  \
+       lwi     r1, r1, TS_THREAD_INFO; /* get the thread info */       \
+       addik   r1, r1, THREAD_SIZE;    /* calculate kernel stack pointer */\
+       tophys(r1,r1);                                                  \
+                                                                       \
+       addik   r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack.  */\
+       /* store return registers separately because this macros        \
+        * is use for others exceptions */                              \
+       swi     r3, r1, PTO + PT_R3;                                    \
+       swi     r4, r1, PTO + PT_R4;                                    \
+       SAVE_REGS                                                       \
+       /* PC, before IRQ/trap - this is one instruction above FIXME*/  \
+       swi     r17, r1, PTO+PT_PC;                                     \
+                                                                       \
+       swi     r0, r1, PTO+PT_MODE; /* Was in user-mode.  */           \
+       lwi     r11, r0, TOPHYS(PER_CPU(ENTRY_SP));                     \
+       swi     r11, r1, PTO+PT_R1; /* Store user SP.  */               \
+       addi    r11, r0, 1;                                             \
+       swi     r11, r0, TOPHYS(PER_CPU(KM)); /* Now we're in kernel-mode.*/\
+2:     lwi     r31, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */\
+       /* Save away the syscall number.  */                            \
+       swi     r0, r1, PTO+PT_R0;                                      \
+       tovirt(r1,r1)
+
+C_ENTRY(full_exception_trap):
+       swi     r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */
+       /* adjust exception address for privileged instruction
+        * for finding where is it */
+       addik   r17, r17, -4
+       SAVE_STATE /* Save registers */
+       /* FIXME this can be store directly in PT_ESR reg.
+        * I tested it but there is a fault */
+       /* where the trap should return need -8 to adjust for rtsd r15, 8 */
+       la      r15, r0, ret_from_exc - 8
+       la      r5, r1, PTO              /* parameter struct pt_regs * regs */
+       mfs     r6, resr
+       nop
+       mfs     r7, rfsr;               /* save FSR */
+       nop
+       la      r12, r0, full_exception
+       set_vms;
+       rtbd    r12, 0;
+       nop;
+
+/*
+ * Unaligned data trap.
+ *
+ * Unaligned data trap last on 4k page is handled here.
+ *
+ * Trap entered via exception, so EE bit is set, and interrupts
+ * are masked.  This is nice, means we don't have to CLI before state save
+ *
+ * The assembler routine is in "arch/microblaze/kernel/hw_exception_handler.S"
+ */
+C_ENTRY(unaligned_data_trap):
+       swi     r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */
+       SAVE_STATE              /* Save registers.*/
+       /* where the trap should return need -8 to adjust for rtsd r15, 8 */
+       la      r15, r0, ret_from_exc-8
+       mfs     r3, resr                /* ESR */
+       nop
+       mfs     r4, rear                /* EAR */
+       nop
+       la      r7, r1, PTO             /* parameter struct pt_regs * regs */
+       la      r12, r0, _unaligned_data_exception
+       set_vms;
+       rtbd    r12, 0; /* interrupts enabled */
+       nop;
+
+/*
+ * Page fault traps.
+ *
+ * If the real exception handler (from hw_exception_handler.S) didn't find
+ * the mapping for the process, then we're thrown here to handle such situation.
+ *
+ * Trap entered via exceptions, so EE bit is set, and interrupts
+ * are masked.  This is nice, means we don't have to CLI before state save
+ *
+ * Build a standard exception frame for TLB Access errors.  All TLB exceptions
+ * will bail out to this point if they can't resolve the lightweight TLB fault.
+ *
+ * The C function called is in "arch/microblaze/mm/fault.c", declared as:
+ * void do_page_fault(struct pt_regs *regs,
+ *                             unsigned long address,
+ *                             unsigned long error_code)
+ */
+/* data and intruction trap - which is choose is resolved int fault.c */
+C_ENTRY(page_fault_data_trap):
+       swi     r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */
+       SAVE_STATE              /* Save registers.*/
+       /* where the trap should return need -8 to adjust for rtsd r15, 8 */
+       la      r15, r0, ret_from_exc-8
+       la      r5, r1, PTO             /* parameter struct pt_regs * regs */
+       mfs     r6, rear                /* parameter unsigned long address */
+       nop
+       mfs     r7, resr                /* parameter unsigned long error_code */
+       nop
+       la      r12, r0, do_page_fault
+       set_vms;
+       rtbd    r12, 0; /* interrupts enabled */
+       nop;
+
+C_ENTRY(page_fault_instr_trap):
+       swi     r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */
+       SAVE_STATE              /* Save registers.*/
+       /* where the trap should return need -8 to adjust for rtsd r15, 8 */
+       la      r15, r0, ret_from_exc-8
+       la      r5, r1, PTO             /* parameter struct pt_regs * regs */
+       mfs     r6, rear                /* parameter unsigned long address */
+       nop
+       ori     r7, r0, 0               /* parameter unsigned long error_code */
+       la      r12, r0, do_page_fault
+       set_vms;
+       rtbd    r12, 0; /* interrupts enabled */
+       nop;
+
+/* Entry point used to return from an exception.  */
+C_ENTRY(ret_from_exc):
+       set_bip;                        /*  Ints masked for state restore*/
+       lwi     r11, r1, PTO+PT_MODE;
+       bnei    r11, 2f;                /* See if returning to kernel mode, */
+                                       /* ... if so, skip resched &c.  */
+
+       /* We're returning to user mode, so check for various conditions that
+          trigger rescheduling. */
+       /* Get current task ptr into r11 */
+       add     r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */
+       lwi     r11, r11, TS_THREAD_INFO;       /* get thread info */
+       lwi     r11, r11, TI_FLAGS;     /* get flags in thread info */
+       andi    r11, r11, _TIF_NEED_RESCHED;
+       beqi    r11, 5f;
+
+/* Call the scheduler before returning from a syscall/trap. */
+       bralid  r15, schedule;  /* Call scheduler */
+       nop;                            /* delay slot */
+
+       /* Maybe handle a signal */
+5:     add     r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */
+       lwi     r11, r11, TS_THREAD_INFO;       /* get thread info */
+       lwi     r11, r11, TI_FLAGS;     /* get flags in thread info */
+       andi    r11, r11, _TIF_SIGPENDING;
+       beqi    r11, 1f;                /* Signals to handle, handle them */
+
+       /*
+        * Handle a signal return; Pending signals should be in r18.
+        *
+        * Not all registers are saved by the normal trap/interrupt entry
+        * points (for instance, call-saved registers (because the normal
+        * C-compiler calling sequence in the kernel makes sure they're
+        * preserved), and call-clobbered registers in the case of
+        * traps), but signal handlers may want to examine or change the
+        * complete register state.  Here we save anything not saved by
+        * the normal entry sequence, so that it may be safely restored
+        * (in a possibly modified form) after do_signal returns.
+        * store return registers separately because this macros is use
+        * for others exceptions */
+       swi     r3, r1, PTO + PT_R3;
+       swi     r4, r1, PTO + PT_R4;
+       la      r5, r1, PTO;            /* Arg 1: struct pt_regs *regs */
+       add     r6, r0, r0;             /* Arg 2: sigset_t *oldset */
+       addi    r7, r0, 0;              /* Arg 3: int in_syscall */
+       bralid  r15, do_signal; /* Handle any signals */
+       nop;
+       lwi     r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */
+       lwi     r4, r1, PTO+PT_R4;
+
+/* Finally, return to user state.  */
+1:     swi     r0, r0, PER_CPU(KM);    /* Now officially in user state. */
+       add     r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */
+       swi     r11, r0, PER_CPU(CURRENT_SAVE); /* save current */
+       VM_OFF;
+       tophys(r1,r1);
+
+       lwi     r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */
+       lwi     r4, r1, PTO+PT_R4;
+       RESTORE_REGS;
+       addik   r1, r1, STATE_SAVE_SIZE         /* Clean up stack space.  */
+
+       lwi     r1, r1, PT_R1 - PT_SIZE; /* Restore user stack pointer. */
+       bri     6f;
+/* Return to kernel state.  */
+2:     VM_OFF;
+       tophys(r1,r1);
+       lwi     r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */
+       lwi     r4, r1, PTO+PT_R4;
+       RESTORE_REGS;
+       addik   r1, r1, STATE_SAVE_SIZE         /* Clean up stack space.  */
+
+       tovirt(r1,r1);
+6:
+EXC_return:            /* Make global symbol for debugging */
+       rtbd    r14, 0; /* Instructions to return from an IRQ */
+       nop;
+
+/*
+ * HW EXCEPTION rutine end
+ */
+
+/*
+ * Hardware maskable interrupts.
+ *
+ * The stack-pointer (r1) should have already been saved to the memory
+ * location PER_CPU(ENTRY_SP).
+ */
+C_ENTRY(_interrupt):
+/* MS: we are in physical address */
+/* Save registers, switch to proper stack, convert SP to virtual.*/
+       swi     r1, r0, TOPHYS(PER_CPU(ENTRY_SP))
+       swi     r11, r0, TOPHYS(PER_CPU(R11_SAVE));
+       /* MS: See if already in kernel mode. */
+       lwi     r11, r0, TOPHYS(PER_CPU(KM));
+       beqi    r11, 1f; /* MS: Jump ahead if coming from user */
+
+/* Kernel-mode state save. */
+       or      r11, r1, r0
+       tophys(r1,r11); /* MS: I have in r1 physical address where stack is */
+/* MS: Save original SP - position PT_R1 to next stack frame 4 *1 - 152*/
+       swi     r11, r1, (PT_R1 - PT_SIZE);
+/* MS: restore r11 because of saving in SAVE_REGS */
+       lwi     r11, r0, TOPHYS(PER_CPU(R11_SAVE));
+       /* save registers */
+/* MS: Make room on the stack -> activation record */
+       addik   r1, r1, -STATE_SAVE_SIZE;
+/* MS: store return registers separately because
+ * this macros is use for others exceptions */
+       swi     r3, r1, PTO + PT_R3;
+       swi     r4, r1, PTO + PT_R4;
+       SAVE_REGS
+       /* MS: store mode */
+       addi    r11, r0, 1; /* MS: Was in kernel-mode. */
+       swi     r11, r1, PTO + PT_MODE; /* MS: and save it */
+       brid    2f;
+       nop; /* MS: Fill delay slot */
+
+1:
+/* User-mode state save. */
+/* MS: restore r11 -> FIXME move before SAVE_REG */
+       lwi     r11, r0, TOPHYS(PER_CPU(R11_SAVE));
+ /* MS: get the saved current */
+       lwi     r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE));
+       tophys(r1,r1);
+       lwi     r1, r1, TS_THREAD_INFO;
+       addik   r1, r1, THREAD_SIZE;
+       tophys(r1,r1);
+       /* save registers */
+       addik   r1, r1, -STATE_SAVE_SIZE;
+       swi     r3, r1, PTO+PT_R3;
+       swi     r4, r1, PTO+PT_R4;
+       SAVE_REGS
+       /* calculate mode */
+       swi     r0, r1, PTO + PT_MODE;
+       lwi     r11, r0, TOPHYS(PER_CPU(ENTRY_SP));
+       swi     r11, r1, PTO+PT_R1;
+       /* setup kernel mode to KM */
+       addi    r11, r0, 1;
+       swi     r11, r0, TOPHYS(PER_CPU(KM));
+
+2:
+       lwi     r31, r0, TOPHYS(PER_CPU(CURRENT_SAVE));
+       swi     r0, r1, PTO + PT_R0;
+       tovirt(r1,r1)
+       la      r5, r1, PTO;
+       set_vms;
+       la      r11, r0, do_IRQ;
+       la      r15, r0, irq_call;
+irq_call:rtbd  r11, 0;
+       nop;
+
+/* MS: we are in virtual mode */
+ret_from_irq:
+       lwi     r11, r1, PTO + PT_MODE;
+       bnei    r11, 2f;
+
+       add     r11, r0, CURRENT_TASK;
+       lwi     r11, r11, TS_THREAD_INFO;
+       lwi     r11, r11, TI_FLAGS; /* MS: get flags from thread info */
+       andi    r11, r11, _TIF_NEED_RESCHED;
+       beqi    r11, 5f
+       bralid  r15, schedule;
+       nop; /* delay slot */
+
+    /* Maybe handle a signal */
+5:     add     r11, r0, CURRENT_TASK;
+       lwi     r11, r11, TS_THREAD_INFO; /* MS: get thread info */
+       lwi     r11, r11, TI_FLAGS; /* get flags in thread info */
+       andi    r11, r11, _TIF_SIGPENDING;
+       beqid   r11, no_intr_resched
+/* Handle a signal return; Pending signals should be in r18. */
+       addi    r7, r0, 0; /* Arg 3: int in_syscall */
+       la      r5, r1, PTO; /* Arg 1: struct pt_regs *regs */
+       bralid  r15, do_signal; /* Handle any signals */
+       add     r6, r0, r0; /* Arg 2: sigset_t *oldset */
+
+/* Finally, return to user state. */
+no_intr_resched:
+    /* Disable interrupts, we are now committed to the state restore */
+       disable_irq
+       swi     r0, r0, PER_CPU(KM); /* MS: Now officially in user state. */
+       add     r11, r0, CURRENT_TASK;
+       swi     r11, r0, PER_CPU(CURRENT_SAVE);
+       VM_OFF;
+       tophys(r1,r1);
+       lwi     r3, r1, PTO + PT_R3; /* MS: restore saved r3, r4 registers */
+       lwi     r4, r1, PTO + PT_R4;
+       RESTORE_REGS
+       addik   r1, r1, STATE_SAVE_SIZE /* MS: Clean up stack space. */
+       lwi     r1, r1, PT_R1 - PT_SIZE;
+       bri     6f;
+/* MS: Return to kernel state. */
+2:     VM_OFF /* MS: turn off MMU */
+       tophys(r1,r1)
+       lwi     r3, r1, PTO + PT_R3; /* MS: restore saved r3, r4 registers */
+       lwi     r4, r1, PTO + PT_R4;
+       RESTORE_REGS
+       addik   r1, r1, STATE_SAVE_SIZE /* MS: Clean up stack space. */
+       tovirt(r1,r1);
+6:
+IRQ_return: /* MS: Make global symbol for debugging */
+       rtid    r14, 0
+       nop
+
+/*
+ * `Debug' trap
+ *  We enter dbtrap in "BIP" (breakpoint) mode.
+ *  So we exit the breakpoint mode with an 'rtbd' and proceed with the
+ *  original dbtrap.
+ *  however, wait to save state first
+ */
+C_ENTRY(_debug_exception):
+       /* BIP bit is set on entry, no interrupts can occur */
+       swi     r1, r0, TOPHYS(PER_CPU(ENTRY_SP))
+
+       swi     r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* Save r11 */
+       set_bip;        /*equalize initial state for all possible entries*/
+       clear_eip;
+       enable_irq;
+       lwi     r11, r0, TOPHYS(PER_CPU(KM));/* See if already in kernel mode.*/
+       beqi    r11, 1f;                /* Jump ahead if coming from user */
+       /* Kernel-mode state save.  */
+       lwi     r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/
+       tophys(r1,r11);
+       swi     r11, r1, (PT_R1-PT_SIZE); /* Save original SP. */
+       lwi     r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */
+
+       addik   r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack.  */
+       swi     r3, r1, PTO + PT_R3;
+       swi     r4, r1, PTO + PT_R4;
+       SAVE_REGS;
+
+       addi    r11, r0, 1;             /* Was in kernel-mode.  */
+       swi     r11, r1, PTO + PT_MODE;
+       brid    2f;
+       nop;                            /* Fill delay slot */
+1:      /* User-mode state save.  */
+       lwi     r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */
+       lwi     r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */
+       tophys(r1,r1);
+       lwi     r1, r1, TS_THREAD_INFO; /* get the thread info */
+       addik   r1, r1, THREAD_SIZE;    /* calculate kernel stack pointer */
+       tophys(r1,r1);
+
+       addik   r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack.  */
+       swi     r3, r1, PTO + PT_R3;
+       swi     r4, r1, PTO + PT_R4;
+       SAVE_REGS;
+
+       swi     r0, r1, PTO+PT_MODE; /* Was in user-mode.  */
+       lwi     r11, r0, TOPHYS(PER_CPU(ENTRY_SP));
+       swi     r11, r1, PTO+PT_R1; /* Store user SP.  */
+       addi    r11, r0, 1;
+       swi     r11, r0, TOPHYS(PER_CPU(KM));   /* Now we're in kernel-mode.  */
+2:     lwi     r31, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */
+       /* Save away the syscall number.  */
+       swi     r0, r1, PTO+PT_R0;
+       tovirt(r1,r1)
+
+       addi    r5, r0, SIGTRAP              /* send the trap signal */
+       add     r6, r0, CURRENT_TASK; /* Get current task ptr into r11 */
+       addk    r7, r0, r0                   /* 3rd param zero */
+
+       set_vms;
+       la      r11, r0, send_sig;
+       la      r15, r0, dbtrap_call;
+dbtrap_call:   rtbd    r11, 0;
+       nop;
+
+       set_bip;                        /*  Ints masked for state restore*/
+       lwi     r11, r1, PTO+PT_MODE;
+       bnei    r11, 2f;
+
+       /* Get current task ptr into r11 */
+       add     r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */
+       lwi     r11, r11, TS_THREAD_INFO;       /* get thread info */
+       lwi     r11, r11, TI_FLAGS;     /* get flags in thread info */
+       andi    r11, r11, _TIF_NEED_RESCHED;
+       beqi    r11, 5f;
+
+/* Call the scheduler before returning from a syscall/trap. */
+
+       bralid  r15, schedule;  /* Call scheduler */
+       nop;                            /* delay slot */
+       /* XXX Is PT_DTRACE handling needed here? */
+       /* XXX m68knommu also checks TASK_STATE & TASK_COUNTER here.  */
+
+       /* Maybe handle a signal */
+5:     add     r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */
+       lwi     r11, r11, TS_THREAD_INFO;       /* get thread info */
+       lwi     r11, r11, TI_FLAGS;     /* get flags in thread info */
+       andi    r11, r11, _TIF_SIGPENDING;
+       beqi    r11, 1f;                /* Signals to handle, handle them */
+
+/* Handle a signal return; Pending signals should be in r18.  */
+       /* Not all registers are saved by the normal trap/interrupt entry
+          points (for instance, call-saved registers (because the normal
+          C-compiler calling sequence in the kernel makes sure they're
+          preserved), and call-clobbered registers in the case of
+          traps), but signal handlers may want to examine or change the
+          complete register state.  Here we save anything not saved by
+          the normal entry sequence, so that it may be safely restored
+          (in a possibly modified form) after do_signal returns.  */
+
+       la      r5, r1, PTO;            /* Arg 1: struct pt_regs *regs */
+       add     r6, r0, r0;             /* Arg 2: sigset_t *oldset */
+       addi  r7, r0, 0;        /* Arg 3: int in_syscall */
+       bralid  r15, do_signal; /* Handle any signals */
+       nop;
+
+
+/* Finally, return to user state.  */
+1:     swi     r0, r0, PER_CPU(KM);    /* Now officially in user state. */
+       add     r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */
+       swi     r11, r0, PER_CPU(CURRENT_SAVE); /* save current */
+       VM_OFF;
+       tophys(r1,r1);
+
+       lwi     r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */
+       lwi     r4, r1, PTO+PT_R4;
+       RESTORE_REGS
+       addik   r1, r1, STATE_SAVE_SIZE         /* Clean up stack space.  */
+
+
+       lwi     r1, r1, PT_R1 - PT_SIZE;
+                                       /* Restore user stack pointer. */
+       bri     6f;
+
+/* Return to kernel state.  */
+2:     VM_OFF;
+       tophys(r1,r1);
+       lwi     r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */
+       lwi     r4, r1, PTO+PT_R4;
+       RESTORE_REGS
+       addik   r1, r1, STATE_SAVE_SIZE         /* Clean up stack space.  */
+
+       tovirt(r1,r1);
+6:
+DBTRAP_return:         /* Make global symbol for debugging */
+       rtbd    r14, 0; /* Instructions to return from an IRQ */
+       nop;
+
+
+
+ENTRY(_switch_to)
+       /* prepare return value */
+       addk    r3, r0, r31
+
+       /* save registers in cpu_context */
+       /* use r11 and r12, volatile registers, as temp register */
+       /* give start of cpu_context for previous process */
+       addik   r11, r5, TI_CPU_CONTEXT
+       swi     r1, r11, CC_R1
+       swi     r2, r11, CC_R2
+       /* skip volatile registers.
+        * they are saved on stack when we jumped to _switch_to() */
+       /* dedicated registers */
+       swi     r13, r11, CC_R13
+       swi     r14, r11, CC_R14
+       swi     r15, r11, CC_R15
+       swi     r16, r11, CC_R16
+       swi     r17, r11, CC_R17
+       swi     r18, r11, CC_R18
+       /* save non-volatile registers */
+       swi     r19, r11, CC_R19
+       swi     r20, r11, CC_R20
+       swi     r21, r11, CC_R21
+       swi     r22, r11, CC_R22
+       swi     r23, r11, CC_R23
+       swi     r24, r11, CC_R24
+       swi     r25, r11, CC_R25
+       swi     r26, r11, CC_R26
+       swi     r27, r11, CC_R27
+       swi     r28, r11, CC_R28
+       swi     r29, r11, CC_R29
+       swi     r30, r11, CC_R30
+       /* special purpose registers */
+       mfs     r12, rmsr
+       nop
+       swi     r12, r11, CC_MSR
+       mfs     r12, rear
+       nop
+       swi     r12, r11, CC_EAR
+       mfs     r12, resr
+       nop
+       swi     r12, r11, CC_ESR
+       mfs     r12, rfsr
+       nop
+       swi     r12, r11, CC_FSR
+
+       /* update r31, the current */
+       lwi     r31, r6, TI_TASK/* give me pointer to task which will be next */
+       /* stored it to current_save too */
+       swi     r31, r0, PER_CPU(CURRENT_SAVE)
+
+       /* get new process' cpu context and restore */
+       /* give me start where start context of next task */
+       addik   r11, r6, TI_CPU_CONTEXT
+
+       /* non-volatile registers */
+       lwi     r30, r11, CC_R30
+       lwi     r29, r11, CC_R29
+       lwi     r28, r11, CC_R28
+       lwi     r27, r11, CC_R27
+       lwi     r26, r11, CC_R26
+       lwi     r25, r11, CC_R25
+       lwi     r24, r11, CC_R24
+       lwi     r23, r11, CC_R23
+       lwi     r22, r11, CC_R22
+       lwi     r21, r11, CC_R21
+       lwi     r20, r11, CC_R20
+       lwi     r19, r11, CC_R19
+       /* dedicated registers */
+       lwi     r18, r11, CC_R18
+       lwi     r17, r11, CC_R17
+       lwi     r16, r11, CC_R16
+       lwi     r15, r11, CC_R15
+       lwi     r14, r11, CC_R14
+       lwi     r13, r11, CC_R13
+       /* skip volatile registers */
+       lwi     r2, r11, CC_R2
+       lwi     r1, r11, CC_R1
+
+       /* special purpose registers */
+       lwi     r12, r11, CC_FSR
+       mts     rfsr, r12
+       nop
+       lwi     r12, r11, CC_MSR
+       mts     rmsr, r12
+       nop
+
+       rtsd    r15, 8
+       nop
+
+ENTRY(_reset)
+       brai    0x70; /* Jump back to FS-boot */
+
+ENTRY(_break)
+       mfs     r5, rmsr
+       nop
+       swi     r5, r0, 0x250 + TOPHYS(r0_ram)
+       mfs     r5, resr
+       nop
+       swi     r5, r0, 0x254 + TOPHYS(r0_ram)
+       bri     0
+
+       /* These are compiled and loaded into high memory, then
+        * copied into place in mach_early_setup */
+       .section        .init.ivt, "ax"
+       .org    0x0
+       /* this is very important - here is the reset vector */
+       /* in current MMU branch you don't care what is here - it is
+        * used from bootloader site - but this is correct for FS-BOOT */
+       brai    0x70
+       nop
+       brai    TOPHYS(_user_exception); /* syscall handler */
+       brai    TOPHYS(_interrupt);     /* Interrupt handler */
+       brai    TOPHYS(_break);         /* nmi trap handler */
+       brai    TOPHYS(_hw_exception_handler);  /* HW exception handler */
+
+       .org    0x60
+       brai    TOPHYS(_debug_exception);       /* debug trap handler*/
+
+.section .rodata,"a"
+#include "syscall_table.S"
+
+syscall_table_size=(.-sys_call_table)
+
index 4a8a4064c7ee8919b0a47d3bc2e69774afcd995a..0cb64a31e89a78870319b3f78ac07ca18d16aa80 100644 (file)
@@ -21,9 +21,9 @@
 
 #include <asm/exceptions.h>
 #include <asm/entry.h>         /* For KM CPU var */
-#include <asm/uaccess.h>
-#include <asm/errno.h>
-#include <asm/ptrace.h>
+#include <linux/uaccess.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
 #include <asm/current.h>
 
 #define MICROBLAZE_ILL_OPCODE_EXCEPTION        0x02
@@ -31,7 +31,7 @@
 #define MICROBLAZE_DBUS_EXCEPTION      0x04
 #define MICROBLAZE_DIV_ZERO_EXCEPTION  0x05
 #define MICROBLAZE_FPU_EXCEPTION       0x06
-#define MICROBLAZE_PRIVILEG_EXCEPTION  0x07
+#define MICROBLAZE_PRIVILEGED_EXCEPTION        0x07
 
 static DEFINE_SPINLOCK(die_lock);
 
@@ -66,6 +66,11 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
 asmlinkage void full_exception(struct pt_regs *regs, unsigned int type,
                                                        int fsr, int addr)
 {
+#ifdef CONFIG_MMU
+       int code;
+       addr = regs->pc;
+#endif
+
 #if 0
        printk(KERN_WARNING "Exception %02x in %s mode, FSR=%08x PC=%08x ESR=%08x\n",
                        type, user_mode(regs) ? "user" : "kernel", fsr,
@@ -74,7 +79,13 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type,
 
        switch (type & 0x1F) {
        case MICROBLAZE_ILL_OPCODE_EXCEPTION:
-               _exception(SIGILL, regs, ILL_ILLOPC, addr);
+               if (user_mode(regs)) {
+                       printk(KERN_WARNING "Illegal opcode exception in user mode.\n");
+                       _exception(SIGILL, regs, ILL_ILLOPC, addr);
+                       return;
+               }
+               printk(KERN_WARNING "Illegal opcode exception in kernel mode.\n");
+               die("opcode exception", regs, SIGBUS);
                break;
        case MICROBLAZE_IBUS_EXCEPTION:
                if (user_mode(regs)) {
@@ -95,11 +106,16 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type,
                die("bus exception", regs, SIGBUS);
                break;
        case MICROBLAZE_DIV_ZERO_EXCEPTION:
-               printk(KERN_WARNING "Divide by zero exception\n");
-               _exception(SIGILL, regs, ILL_ILLOPC, addr);
+               if (user_mode(regs)) {
+                       printk(KERN_WARNING "Divide by zero exception in user mode\n");
+                       _exception(SIGILL, regs, ILL_ILLOPC, addr);
+                       return;
+               }
+               printk(KERN_WARNING "Divide by zero exception in kernel mode.\n");
+               die("Divide by exception", regs, SIGBUS);
                break;
-
        case MICROBLAZE_FPU_EXCEPTION:
+               printk(KERN_WARNING "FPU exception\n");
                /* IEEE FP exception */
                /* I removed fsr variable and use code var for storing fsr */
                if (fsr & FSR_IO)
@@ -115,7 +131,20 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type,
                _exception(SIGFPE, regs, fsr, addr);
                break;
 
+#ifdef CONFIG_MMU
+       case MICROBLAZE_PRIVILEGED_EXCEPTION:
+               printk(KERN_WARNING "Privileged exception\n");
+               /* "brk r0,r0" - used as debug breakpoint */
+               if (get_user(code, (unsigned long *)regs->pc) == 0
+                       && code == 0x980c0000) {
+                       _exception(SIGTRAP, regs, TRAP_BRKPT, addr);
+               } else {
+                       _exception(SIGILL, regs, ILL_PRVOPC, addr);
+               }
+               break;
+#endif
        default:
+       /* FIXME what to do in unexpected exception */
                printk(KERN_WARNING "Unexpected exception %02x "
                        "PC=%08x in %s mode\n", type, (unsigned int) addr,
                        kernel_mode(regs) ? "kernel" : "user");
index 319dc35fc922e630b471598142f1a98c3564b5d8..e568d6ec621bfa400bc5b506b9ff6afbe327c002 100644 (file)
@@ -3,6 +3,26 @@
  * Copyright (C) 2007-2009 PetaLogix
  * Copyright (C) 2006 Atmark Techno, Inc.
  *
+ * MMU code derived from arch/ppc/kernel/head_4xx.S:
+ *    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
+ *
  * 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 <asm/thread_info.h>
 #include <asm/page.h>
 
+#ifdef CONFIG_MMU
+#include <asm/setup.h> /* COMMAND_LINE_SIZE */
+#include <asm/mmu.h>
+#include <asm/processor.h>
+
+.data
+.global empty_zero_page
+.align 12
+empty_zero_page:
+       .space  4096
+.global swapper_pg_dir
+swapper_pg_dir:
+       .space  4096
+
+#endif /* CONFIG_MMU */
+
        .text
 ENTRY(_start)
        mfs     r1, rmsr
@@ -32,6 +68,123 @@ _copy_fdt:
        addik   r3, r3, -4 /* descrement loop */
 no_fdt_arg:
 
+#ifdef CONFIG_MMU
+
+#ifndef CONFIG_CMDLINE_BOOL
+/*
+ * handling command line
+ * copy command line to __init_end. There is space for storing command line.
+ */
+       or      r6, r0, r0              /* incremment */
+       ori     r4, r0, __init_end      /* load address of command line */
+       tophys(r4,r4)                   /* convert to phys address */
+       ori     r3, r0, COMMAND_LINE_SIZE - 1 /* number of loops */
+_copy_command_line:
+       lbu     r7, r5, r6 /* r7=r5+r6 - r5 contain pointer to command line */
+       sb      r7, r4, r6              /* addr[r4+r6]= r7*/
+       addik   r6, r6, 1               /* increment counting */
+       bgtid   r3, _copy_command_line  /* loop for all entries       */
+       addik   r3, r3, -1              /* descrement loop */
+       addik   r5, r4, 0               /* add new space for command line */
+       tovirt(r5,r5)
+#endif /* CONFIG_CMDLINE_BOOL */
+
+#ifdef NOT_COMPILE
+/* save bram context */
+       or      r6, r0, r0                              /* incremment */
+       ori     r4, r0, TOPHYS(_bram_load_start)        /* save bram context */
+       ori     r3, r0, (LMB_SIZE - 4)
+_copy_bram:
+       lw      r7, r0, r6              /* r7 = r0 + r6 */
+       sw      r7, r4, r6              /* addr[r4 + r6] = r7*/
+       addik   r6, r6, 4               /* increment counting */
+       bgtid   r3, _copy_bram          /* loop for all entries */
+       addik   r3, r3, -4              /* descrement loop */
+#endif
+       /* We have to turn on the MMU right away. */
+
+       /*
+        * Set up the initial MMU state so we can do the first level of
+        * kernel initialization.  This maps the first 16 MBytes of memory 1:1
+        * virtual to physical.
+        */
+       nop
+       addik   r3, r0, 63              /* Invalidate all TLB entries */
+_invalidate:
+       mts     rtlbx, r3
+       mts     rtlbhi, r0                      /* flush: ensure V is clear   */
+       bgtid   r3, _invalidate         /* loop for all entries       */
+       addik   r3, r3, -1
+       /* sync */
+
+       /*
+        * We should still be executing code at physical address area
+        * RAM_BASEADDR at this point. However, kernel code is at
+        * a virtual address. So, set up a TLB mapping to cover this once
+        * translation is enabled.
+        */
+
+       addik   r3,r0, CONFIG_KERNEL_START /* Load the kernel virtual address */
+       tophys(r4,r3)                   /* Load the kernel physical address */
+
+       mts     rpid,r0                 /* Load the kernel PID */
+       nop
+       bri     4
+
+       /*
+        * Configure and load two entries into TLB slots 0 and 1.
+        * In case we are pinning TLBs, these are reserved in by the
+        * other TLB functions.  If not reserving, then it doesn't
+        * matter where they are loaded.
+        */
+       andi    r4,r4,0xfffffc00        /* Mask off the real page number */
+       ori     r4,r4,(TLB_WR | TLB_EX) /* Set the write and execute bits */
+
+       andi    r3,r3,0xfffffc00        /* Mask off the effective page number */
+       ori     r3,r3,(TLB_VALID | TLB_PAGESZ(PAGESZ_16M))
+
+       mts     rtlbx,r0                /* TLB slow 0 */
+
+       mts     rtlblo,r4               /* Load the data portion of the entry */
+       mts     rtlbhi,r3               /* Load the tag portion of the entry */
+
+       addik   r4, r4, 0x01000000      /* Map next 16 M entries */
+       addik   r3, r3, 0x01000000
+
+       ori     r6,r0,1                 /* TLB slot 1 */
+       mts     rtlbx,r6
+
+       mts     rtlblo,r4               /* Load the data portion of the entry */
+       mts     rtlbhi,r3               /* Load the tag portion of the entry */
+
+       /*
+        * Load a TLB entry for LMB, since we need access to
+        * the exception vectors, using a 4k real==virtual mapping.
+        */
+       ori     r6,r0,3                 /* TLB slot 3 */
+       mts     rtlbx,r6
+
+       ori     r4,r0,(TLB_WR | TLB_EX)
+       ori     r3,r0,(TLB_VALID | TLB_PAGESZ(PAGESZ_4K))
+
+       mts     rtlblo,r4               /* Load the data portion of the entry */
+       mts     rtlbhi,r3               /* Load the tag portion of the entry */
+
+       /*
+        * We now have the lower 16 Meg of RAM mapped into TLB entries, and the
+        * caches ready to work.
+        */
+turn_on_mmu:
+       ori     r15,r0,start_here
+       ori     r4,r0,MSR_KERNEL_VMS
+       mts     rmsr,r4
+       nop
+       rted    r15,0                   /* enables MMU */
+       nop
+
+start_here:
+#endif /* CONFIG_MMU */
+
        /* Initialize small data anchors */
        la      r13, r0, _KERNEL_SDA_BASE_
        la      r2, r0, _KERNEL_SDA2_BASE_
@@ -51,6 +204,43 @@ no_fdt_arg:
        brald   r15, r8
        nop
 
+#ifndef CONFIG_MMU
        la      r15, r0, machine_halt
        braid   start_kernel
        nop
+#else
+       /*
+        * Initialize the MMU.
+        */
+       bralid  r15, mmu_init
+       nop
+
+       /* Go back to running unmapped so we can load up new values
+        * and change to using our exception vectors.
+        * On the MicroBlaze, all we invalidate the used TLB entries to clear
+        * the old 16M byte TLB mappings.
+        */
+       ori     r15,r0,TOPHYS(kernel_load_context)
+       ori     r4,r0,MSR_KERNEL
+       mts     rmsr,r4
+       nop
+       bri     4
+       rted    r15,0
+       nop
+
+       /* Load up the kernel context */
+kernel_load_context:
+       # Keep entry 0 and 1 valid. Entry 3 mapped to LMB can go away.
+       ori     r5,r0,3
+       mts     rtlbx,r5
+       nop
+       mts     rtlbhi,r0
+       nop
+       addi    r15, r0, machine_halt
+       ori     r17, r0, start_kernel
+       ori     r4, r0, MSR_KERNEL_VMS
+       mts     rmsr, r4
+       nop
+       rted    r17, 0          /* enable MMU and jump to start_kernel */
+       nop
+#endif /* CONFIG_MMU */
index cf9486d998380df08d95f5e8e413491fd583a92a..9d591cd74fc221293fa1d647f530c0e29120dbe4 100644 (file)
  *   - Illegal instruction opcode
  *   - Divide-by-zero
  *
+ *   - Privileged instruction exception (MMU)
+ *   - Data storage exception (MMU)
+ *   - Instruction storage exception (MMU)
+ *   - Data TLB miss exception (MMU)
+ *   - Instruction TLB miss exception (MMU)
+ *
  * Note we disable interrupts during exception handling, otherwise we will
  * possibly get multiple re-entrancy if interrupt handles themselves cause
  * exceptions. JW
 #include <asm/asm-offsets.h>
 
 /* Helpful Macros */
+#ifndef CONFIG_MMU
 #define EX_HANDLER_STACK_SIZ   (4*19)
+#endif
 #define NUM_TO_REG(num)                r ## num
 
+#ifdef CONFIG_MMU
+/* FIXME you can't change first load of MSR because there is
+ * hardcoded jump bri 4 */
+       #define RESTORE_STATE                   \
+               lwi     r3, r1, PT_R3;          \
+               lwi     r4, r1, PT_R4;          \
+               lwi     r5, r1, PT_R5;          \
+               lwi     r6, r1, PT_R6;          \
+               lwi     r11, r1, PT_R11;        \
+               lwi     r31, r1, PT_R31;        \
+               lwi     r1, r0, TOPHYS(r0_ram + 0);
+#endif /* CONFIG_MMU */
+
 #define LWREG_NOP                      \
        bri     ex_handler_unhandled;   \
        nop;
        or      r3, r0, NUM_TO_REG (regnum);            \
        bri     ex_sw_tail;
 
+#ifdef CONFIG_MMU
+       #define R3_TO_LWREG_VM_V(regnum)                \
+               brid    ex_lw_end_vm;                   \
+               swi     r3, r7, 4 * regnum;
+
+       #define R3_TO_LWREG_VM(regnum)                  \
+               brid    ex_lw_end_vm;                   \
+               or      NUM_TO_REG (regnum), r0, r3;
+
+       #define SWREG_TO_R3_VM_V(regnum)                \
+               brid    ex_sw_tail_vm;                  \
+               lwi     r3, r7, 4 * regnum;
+
+       #define SWREG_TO_R3_VM(regnum)                  \
+               brid    ex_sw_tail_vm;                  \
+               or      r3, r0, NUM_TO_REG (regnum);
+
+       /* Shift right instruction depending on available configuration */
+       #if CONFIG_XILINX_MICROBLAZE0_USE_BARREL > 0
+       #define BSRLI(rD, rA, imm)      \
+               bsrli rD, rA, imm
+       #elif CONFIG_XILINX_MICROBLAZE0_USE_DIV > 0
+       #define BSRLI(rD, rA, imm)      \
+               ori rD, r0, (1 << imm); \
+               idivu rD, rD, rA
+       #else
+       #define BSRLI(rD, rA, imm) BSRLI ## imm (rD, rA)
+       /* Only the used shift constants defined here - add more if needed */
+       #define BSRLI2(rD, rA)                          \
+               srl rD, rA;             /* << 1 */      \
+               srl rD, rD;             /* << 2 */
+       #define BSRLI10(rD, rA)                         \
+               srl rD, rA;             /* << 1 */      \
+               srl rD, rD;             /* << 2 */      \
+               srl rD, rD;             /* << 3 */      \
+               srl rD, rD;             /* << 4 */      \
+               srl rD, rD;             /* << 5 */      \
+               srl rD, rD;             /* << 6 */      \
+               srl rD, rD;             /* << 7 */      \
+               srl rD, rD;             /* << 8 */      \
+               srl rD, rD;             /* << 9 */      \
+               srl rD, rD              /* << 10 */
+       #define BSRLI20(rD, rA)         \
+               BSRLI10(rD, rA);        \
+               BSRLI10(rD, rD)
+       #endif
+#endif /* CONFIG_MMU */
+
 .extern other_exception_handler /* Defined in exception.c */
 
 /*
 
 /* wrappers to restore state before coming to entry.S */
 
+#ifdef CONFIG_MMU
+.section .rodata
+.align 4
+_MB_HW_ExceptionVectorTable:
+/*  0 - Undefined */
+       .long   TOPHYS(ex_handler_unhandled)
+/*  1 - Unaligned data access exception */
+       .long   TOPHYS(handle_unaligned_ex)
+/*  2 - Illegal op-code exception */
+       .long   TOPHYS(full_exception_trapw)
+/*  3 - Instruction bus error exception */
+       .long   TOPHYS(full_exception_trapw)
+/*  4 - Data bus error exception */
+       .long   TOPHYS(full_exception_trapw)
+/*  5 - Divide by zero exception */
+       .long   TOPHYS(full_exception_trapw)
+/*  6 - Floating point unit exception */
+       .long   TOPHYS(full_exception_trapw)
+/*  7 - Privileged instruction exception */
+       .long   TOPHYS(full_exception_trapw)
+/*  8 - 15 - Undefined */
+       .long   TOPHYS(ex_handler_unhandled)
+       .long   TOPHYS(ex_handler_unhandled)
+       .long   TOPHYS(ex_handler_unhandled)
+       .long   TOPHYS(ex_handler_unhandled)
+       .long   TOPHYS(ex_handler_unhandled)
+       .long   TOPHYS(ex_handler_unhandled)
+       .long   TOPHYS(ex_handler_unhandled)
+       .long   TOPHYS(ex_handler_unhandled)
+/* 16 - Data storage exception */
+       .long   TOPHYS(handle_data_storage_exception)
+/* 17 - Instruction storage exception */
+       .long   TOPHYS(handle_instruction_storage_exception)
+/* 18 - Data TLB miss exception */
+       .long   TOPHYS(handle_data_tlb_miss_exception)
+/* 19 - Instruction TLB miss exception */
+       .long   TOPHYS(handle_instruction_tlb_miss_exception)
+/* 20 - 31 - Undefined */
+       .long   TOPHYS(ex_handler_unhandled)
+       .long   TOPHYS(ex_handler_unhandled)
+       .long   TOPHYS(ex_handler_unhandled)
+       .long   TOPHYS(ex_handler_unhandled)
+       .long   TOPHYS(ex_handler_unhandled)
+       .long   TOPHYS(ex_handler_unhandled)
+       .long   TOPHYS(ex_handler_unhandled)
+       .long   TOPHYS(ex_handler_unhandled)
+       .long   TOPHYS(ex_handler_unhandled)
+       .long   TOPHYS(ex_handler_unhandled)
+       .long   TOPHYS(ex_handler_unhandled)
+       .long   TOPHYS(ex_handler_unhandled)
+#endif
+
 .global _hw_exception_handler
 .section .text
 .align 4
 .ent _hw_exception_handler
 _hw_exception_handler:
+#ifndef CONFIG_MMU
        addik   r1, r1, -(EX_HANDLER_STACK_SIZ); /* Create stack frame */
+#else
+       swi     r1, r0, TOPHYS(r0_ram + 0); /* GET_SP */
+       /* Save date to kernel memory. Here is the problem
+        * when you came from user space */
+       ori     r1, r0, TOPHYS(r0_ram + 28);
+#endif
        swi     r3, r1, PT_R3
        swi     r4, r1, PT_R4
        swi     r5, r1, PT_R5
        swi     r6, r1, PT_R6
 
-       mfs     r5, rmsr;
-       nop
-       swi     r5, r1, 0;
-       mfs     r4, rbtr        /* Save BTR before jumping to handler */
-       nop
+#ifdef CONFIG_MMU
+       swi     r11, r1, PT_R11
+       swi     r31, r1, PT_R31
+       lwi     r31, r0, TOPHYS(PER_CPU(CURRENT_SAVE)) /* get saved current */
+#endif
+
        mfs     r3, resr
        nop
+       mfs     r4, rear;
+       nop
 
+#ifndef CONFIG_MMU
        andi    r5, r3, 0x1000;         /* Check ESR[DS] */
        beqi    r5, not_in_delay_slot;  /* Branch if ESR[DS] not set */
        mfs     r17, rbtr;      /* ESR[DS] set - return address in BTR */
        nop
 not_in_delay_slot:
        swi     r17, r1, PT_R17
+#endif
 
        andi    r5, r3, 0x1F;           /* Extract ESR[EXC] */
 
+#ifdef CONFIG_MMU
+       /* Calculate exception vector offset = r5 << 2 */
+       addk    r6, r5, r5; /* << 1 */
+       addk    r6, r6, r6; /* << 2 */
+
+/* counting which exception happen */
+       lwi     r5, r0, 0x200 + TOPHYS(r0_ram)
+       addi    r5, r5, 1
+       swi     r5, r0, 0x200 + TOPHYS(r0_ram)
+       lwi     r5, r6, 0x200 + TOPHYS(r0_ram)
+       addi    r5, r5, 1
+       swi     r5, r6, 0x200 + TOPHYS(r0_ram)
+/* end */
+       /* Load the HW Exception vector */
+       lwi     r6, r6, TOPHYS(_MB_HW_ExceptionVectorTable)
+       bra     r6
+
+full_exception_trapw:
+       RESTORE_STATE
+       bri     full_exception_trap
+#else
        /* Exceptions enabled here. This will allow nested exceptions */
        mfs     r6, rmsr;
        nop
@@ -254,6 +408,7 @@ handle_other_ex: /* Handle Other exceptions here */
        lwi     r18, r1, PT_R18
 
        bri     ex_handler_done; /* Complete exception handling */
+#endif
 
 /* 0x01 - Unaligned data access exception
  * This occurs when a word access is not aligned on a word boundary,
@@ -265,11 +420,28 @@ handle_other_ex: /* Handle Other exceptions here */
 handle_unaligned_ex:
        /* Working registers already saved: R3, R4, R5, R6
         *  R3 = ESR
-        *  R4 = BTR
+        *  R4 = EAR
         */
-       mfs     r4, rear;
+#ifdef CONFIG_MMU
+       andi    r6, r3, 0x1000                  /* Check ESR[DS] */
+       beqi    r6, _no_delayslot               /* Branch if ESR[DS] not set */
+       mfs     r17, rbtr;      /* ESR[DS] set - return address in BTR */
        nop
+_no_delayslot:
+#endif
+
+#ifdef CONFIG_MMU
+       /* Check if unaligned address is last on a 4k page */
+               andi    r5, r4, 0xffc
+               xori    r5, r5, 0xffc
+               bnei    r5, _unaligned_ex2
+       _unaligned_ex1:
+               RESTORE_STATE;
+/* Another page must be accessed or physical address not in page table */
+               bri     unaligned_data_trap
 
+       _unaligned_ex2:
+#endif
        andi    r6, r3, 0x3E0; /* Mask and extract the register operand */
        srl     r6, r6; /* r6 >> 5 */
        srl     r6, r6;
@@ -278,6 +450,45 @@ handle_unaligned_ex:
        srl     r6, r6;
        /* Store the register operand in a temporary location */
        sbi     r6, r0, TOPHYS(ex_reg_op);
+#ifdef CONFIG_MMU
+       /* Get physical address */
+       /* If we are faulting a kernel address, we have to use the
+        * kernel page tables.
+        */
+       ori     r5, r0, CONFIG_KERNEL_START
+       cmpu    r5, r4, r5
+       bgti    r5, _unaligned_ex3
+       ori     r5, r0, swapper_pg_dir
+       bri     _unaligned_ex4
+
+       /* Get the PGD for the current thread. */
+_unaligned_ex3: /* user thread */
+       addi    r5 ,CURRENT_TASK, TOPHYS(0); /* get current task address */
+       lwi     r5, r5, TASK_THREAD + PGDIR
+_unaligned_ex4:
+       tophys(r5,r5)
+       BSRLI(r6,r4,20)                 /* Create L1 (pgdir/pmd) address */
+       andi    r6, r6, 0xffc
+/* Assume pgdir aligned on 4K boundary, no need for "andi r5,r5,0xfffff003" */
+       or      r5, r5, r6
+       lwi     r6, r5, 0               /* Get L1 entry */
+       andi    r5, r6, 0xfffff000      /* Extract L2 (pte) base address. */
+       beqi    r5, _unaligned_ex1      /* Bail if no table */
+
+       tophys(r5,r5)
+       BSRLI(r6,r4,10)                 /* Compute PTE address */
+       andi    r6, r6, 0xffc
+       andi    r5, r5, 0xfffff003
+       or      r5, r5, r6
+       lwi     r5, r5, 0               /* Get Linux PTE */
+
+       andi    r6, r5, _PAGE_PRESENT
+       beqi    r6, _unaligned_ex1      /* Bail if no page */
+
+       andi    r5, r5, 0xfffff000      /* Extract RPN */
+       andi    r4, r4, 0x00000fff      /* Extract offset */
+       or      r4, r4, r5              /* Create physical address */
+#endif /* CONFIG_MMU */
 
        andi    r6, r3, 0x400; /* Extract ESR[S] */
        bnei    r6, ex_sw;
@@ -355,6 +566,7 @@ ex_shw:
 ex_sw_end: /* Exception handling of store word, ends. */
 
 ex_handler_done:
+#ifndef CONFIG_MMU
        lwi     r5, r1, 0 /* RMSR */
        mts     rmsr, r5
        nop
@@ -366,13 +578,455 @@ ex_handler_done:
 
        rted    r17, 0
        addik   r1, r1, (EX_HANDLER_STACK_SIZ); /* Restore stack frame */
+#else
+       RESTORE_STATE;
+       rted    r17, 0
+       nop
+#endif
+
+#ifdef CONFIG_MMU
+       /* Exception vector entry code. This code runs with address translation
+        * turned off (i.e. using physical addresses). */
+
+       /* Exception vectors. */
+
+       /* 0x10 - Data Storage Exception
+        * This happens for just a few reasons. U0 set (but we don't do that),
+        * or zone protection fault (user violation, write to protected page).
+        * If this is just an update of modified status, we do that quickly
+        * and exit. Otherwise, we call heavyweight functions to do the work.
+        */
+       handle_data_storage_exception:
+               /* Working registers already saved: R3, R4, R5, R6
+                * R3 = ESR
+                */
+               mfs     r11, rpid
+               nop
+               bri     4
+               mfs     r3, rear                /* Get faulting address */
+               nop
+               /* If we are faulting a kernel address, we have to use the
+                * kernel page tables.
+                */
+               ori     r4, r0, CONFIG_KERNEL_START
+               cmpu    r4, r3, r4
+               bgti    r4, ex3
+               /* First, check if it was a zone fault (which means a user
+                * tried to access a kernel or read-protected page - always
+                * a SEGV). All other faults here must be stores, so no
+                * need to check ESR_S as well. */
+               mfs     r4, resr
+               nop
+               andi    r4, r4, 0x800           /* ESR_Z - zone protection */
+               bnei    r4, ex2
+
+               ori     r4, r0, swapper_pg_dir
+               mts     rpid, r0                /* TLB will have 0 TID */
+               nop
+               bri     ex4
+
+               /* Get the PGD for the current thread. */
+       ex3:
+               /* First, check if it was a zone fault (which means a user
+                * tried to access a kernel or read-protected page - always
+                * a SEGV). All other faults here must be stores, so no
+                * need to check ESR_S as well. */
+               mfs     r4, resr
+               nop
+               andi    r4, r4, 0x800           /* ESR_Z */
+               bnei    r4, ex2
+               /* get current task address */
+               addi    r4 ,CURRENT_TASK, TOPHYS(0);
+               lwi     r4, r4, TASK_THREAD+PGDIR
+       ex4:
+               tophys(r4,r4)
+               BSRLI(r5,r3,20)         /* Create L1 (pgdir/pmd) address */
+               andi    r5, r5, 0xffc
+/* Assume pgdir aligned on 4K boundary, no need for "andi r4,r4,0xfffff003" */
+               or      r4, r4, r5
+               lwi     r4, r4, 0               /* Get L1 entry */
+               andi    r5, r4, 0xfffff000 /* Extract L2 (pte) base address */
+               beqi    r5, ex2                 /* Bail if no table */
+
+               tophys(r5,r5)
+               BSRLI(r6,r3,10)                 /* Compute PTE address */
+               andi    r6, r6, 0xffc
+               andi    r5, r5, 0xfffff003
+               or      r5, r5, r6
+               lwi     r4, r5, 0               /* Get Linux PTE */
+
+               andi    r6, r4, _PAGE_RW        /* Is it writeable? */
+               beqi    r6, ex2                 /* Bail if not */
+
+               /* Update 'changed' */
+               ori     r4, r4, _PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_HWWRITE
+               swi     r4, r5, 0               /* Update Linux page table */
+
+               /* Most of the Linux PTE is ready to load into the TLB LO.
+                * We set ZSEL, where only the LS-bit determines user access.
+                * We set execute, because we don't have the granularity to
+                * properly set this at the page level (Linux problem).
+                * If shared is set, we cause a zero PID->TID load.
+                * Many of these bits are software only. Bits we don't set
+                * here we (properly should) assume have the appropriate value.
+                */
+               andni   r4, r4, 0x0ce2          /* Make sure 20, 21 are zero */
+               ori     r4, r4, _PAGE_HWEXEC    /* make it executable */
+
+               /* find the TLB index that caused the fault. It has to be here*/
+               mts     rtlbsx, r3
+               nop
+               mfs     r5, rtlbx               /* DEBUG: TBD */
+               nop
+               mts     rtlblo, r4              /* Load TLB LO */
+               nop
+                                               /* Will sync shadow TLBs */
+
+               /* Done...restore registers and get out of here. */
+               mts     rpid, r11
+               nop
+               bri 4
+
+               RESTORE_STATE;
+               rted    r17, 0
+               nop
+       ex2:
+               /* The bailout. Restore registers to pre-exception conditions
+                * and call the heavyweights to help us out. */
+               mts     rpid, r11
+               nop
+               bri 4
+               RESTORE_STATE;
+               bri     page_fault_data_trap
+
+
+       /* 0x11 - Instruction Storage Exception
+        * This is caused by a fetch from non-execute or guarded pages. */
+       handle_instruction_storage_exception:
+               /* Working registers already saved: R3, R4, R5, R6
+                * R3 = ESR
+                */
+
+               mfs     r3, rear                /* Get faulting address */
+               nop
+               RESTORE_STATE;
+               bri     page_fault_instr_trap
+
+       /* 0x12 - Data TLB Miss Exception
+        * As the name implies, translation is not in the MMU, so search the
+        * page tables and fix it. The only purpose of this function is to
+        * load TLB entries from the page table if they exist.
+        */
+       handle_data_tlb_miss_exception:
+               /* Working registers already saved: R3, R4, R5, R6
+                * R3 = ESR
+                */
+               mfs     r11, rpid
+               nop
+               bri     4
+               mfs     r3, rear                /* Get faulting address */
+               nop
+
+               /* If we are faulting a kernel address, we have to use the
+                * kernel page tables. */
+               ori     r4, r0, CONFIG_KERNEL_START
+               cmpu    r4, r3, r4
+               bgti    r4, ex5
+               ori     r4, r0, swapper_pg_dir
+               mts     rpid, r0                /* TLB will have 0 TID */
+               nop
+               bri     ex6
 
+               /* Get the PGD for the current thread. */
+       ex5:
+               /* get current task address */
+               addi    r4 ,CURRENT_TASK, TOPHYS(0);
+               lwi     r4, r4, TASK_THREAD+PGDIR
+       ex6:
+               tophys(r4,r4)
+               BSRLI(r5,r3,20)         /* Create L1 (pgdir/pmd) address */
+               andi    r5, r5, 0xffc
+/* Assume pgdir aligned on 4K boundary, no need for "andi r4,r4,0xfffff003" */
+               or      r4, r4, r5
+               lwi     r4, r4, 0               /* Get L1 entry */
+               andi    r5, r4, 0xfffff000 /* Extract L2 (pte) base address */
+               beqi    r5, ex7                 /* Bail if no table */
+
+               tophys(r5,r5)
+               BSRLI(r6,r3,10)                 /* Compute PTE address */
+               andi    r6, r6, 0xffc
+               andi    r5, r5, 0xfffff003
+               or      r5, r5, r6
+               lwi     r4, r5, 0               /* Get Linux PTE */
+
+               andi    r6, r4, _PAGE_PRESENT
+               beqi    r6, ex7
+
+               ori     r4, r4, _PAGE_ACCESSED
+               swi     r4, r5, 0
+
+               /* Most of the Linux PTE is ready to load into the TLB LO.
+                * We set ZSEL, where only the LS-bit determines user access.
+                * We set execute, because we don't have the granularity to
+                * properly set this at the page level (Linux problem).
+                * If shared is set, we cause a zero PID->TID load.
+                * Many of these bits are software only. Bits we don't set
+                * here we (properly should) assume have the appropriate value.
+                */
+               andni   r4, r4, 0x0ce2          /* Make sure 20, 21 are zero */
+
+               bri     finish_tlb_load
+       ex7:
+               /* The bailout. Restore registers to pre-exception conditions
+                * and call the heavyweights to help us out.
+                */
+               mts     rpid, r11
+               nop
+               bri     4
+               RESTORE_STATE;
+               bri     page_fault_data_trap
+
+       /* 0x13 - Instruction TLB Miss Exception
+        * Nearly the same as above, except we get our information from
+        * different registers and bailout to a different point.
+        */
+       handle_instruction_tlb_miss_exception:
+               /* Working registers already saved: R3, R4, R5, R6
+                *  R3 = ESR
+                */
+               mfs     r11, rpid
+               nop
+               bri     4
+               mfs     r3, rear                /* Get faulting address */
+               nop
+
+               /* If we are faulting a kernel address, we have to use the
+                * kernel page tables.
+                */
+               ori     r4, r0, CONFIG_KERNEL_START
+               cmpu    r4, r3, r4
+               bgti    r4, ex8
+               ori     r4, r0, swapper_pg_dir
+               mts     rpid, r0                /* TLB will have 0 TID */
+               nop
+               bri     ex9
+
+               /* Get the PGD for the current thread. */
+       ex8:
+               /* get current task address */
+               addi    r4 ,CURRENT_TASK, TOPHYS(0);
+               lwi     r4, r4, TASK_THREAD+PGDIR
+       ex9:
+               tophys(r4,r4)
+               BSRLI(r5,r3,20)         /* Create L1 (pgdir/pmd) address */
+               andi    r5, r5, 0xffc
+/* Assume pgdir aligned on 4K boundary, no need for "andi r4,r4,0xfffff003" */
+               or      r4, r4, r5
+               lwi     r4, r4, 0               /* Get L1 entry */
+               andi    r5, r4, 0xfffff000 /* Extract L2 (pte) base address */
+               beqi    r5, ex10                /* Bail if no table */
+
+               tophys(r5,r5)
+               BSRLI(r6,r3,10)                 /* Compute PTE address */
+               andi    r6, r6, 0xffc
+               andi    r5, r5, 0xfffff003
+               or      r5, r5, r6
+               lwi     r4, r5, 0               /* Get Linux PTE */
+
+               andi    r6, r4, _PAGE_PRESENT
+               beqi    r6, ex7
+
+               ori     r4, r4, _PAGE_ACCESSED
+               swi     r4, r5, 0
+
+               /* Most of the Linux PTE is ready to load into the TLB LO.
+                * We set ZSEL, where only the LS-bit determines user access.
+                * We set execute, because we don't have the granularity to
+                * properly set this at the page level (Linux problem).
+                * If shared is set, we cause a zero PID->TID load.
+                * Many of these bits are software only. Bits we don't set
+                * here we (properly should) assume have the appropriate value.
+                */
+               andni   r4, r4, 0x0ce2          /* Make sure 20, 21 are zero */
+
+               bri     finish_tlb_load
+       ex10:
+               /* The bailout. Restore registers to pre-exception conditions
+                * and call the heavyweights to help us out.
+                */
+               mts     rpid, r11
+               nop
+               bri 4
+               RESTORE_STATE;
+               bri     page_fault_instr_trap
+
+/* Both the instruction and data TLB miss get to this point to load the TLB.
+ *     r3 - EA of fault
+ *     r4 - TLB LO (info from Linux PTE)
+ *     r5, r6 - available to use
+ *     PID - loaded with proper value when we get here
+ *     Upon exit, we reload everything and RFI.
+ * A common place to load the TLB.
+ */
+       tlb_index:
+               .long   1 /* MS: storing last used tlb index */
+       finish_tlb_load:
+               /* MS: load the last used TLB index. */
+               lwi     r5, r0, TOPHYS(tlb_index)
+               addik   r5, r5, 1 /* MS: inc tlb_index -> use next one */
+
+/* MS: FIXME this is potential fault, because this is mask not count */
+               andi    r5, r5, (MICROBLAZE_TLB_SIZE-1)
+               ori     r6, r0, 1
+               cmp     r31, r5, r6
+               blti    r31, sem
+               addik   r5, r6, 1
+       sem:
+               /* MS: save back current TLB index */
+               swi     r5, r0, TOPHYS(tlb_index)
+
+               ori     r4, r4, _PAGE_HWEXEC    /* make it executable */
+               mts     rtlbx, r5               /* MS: save current TLB */
+               nop
+               mts     rtlblo, r4              /* MS: save to TLB LO */
+               nop
+
+               /* Create EPN. This is the faulting address plus a static
+                * set of bits. These are size, valid, E, U0, and ensure
+                * bits 20 and 21 are zero.
+                */
+               andi    r3, r3, 0xfffff000
+               ori     r3, r3, 0x0c0
+               mts     rtlbhi, r3              /* Load TLB HI */
+               nop
+
+               /* Done...restore registers and get out of here. */
+       ex12:
+               mts     rpid, r11
+               nop
+               bri 4
+               RESTORE_STATE;
+               rted    r17, 0
+               nop
+
+       /* extern void giveup_fpu(struct task_struct *prev)
+        *
+        * The MicroBlaze processor may have an FPU, so this should not just
+        * return: TBD.
+        */
+       .globl giveup_fpu;
+       .align 4;
+       giveup_fpu:
+               bralid  r15,0                   /* TBD */
+               nop
+
+       /* At present, this routine just hangs. - extern void abort(void) */
+       .globl abort;
+       .align 4;
+       abort:
+               br      r0
+
+       .globl set_context;
+       .align 4;
+       set_context:
+               mts     rpid, r5        /* Shadow TLBs are automatically */
+               nop
+               bri     4               /* flushed by changing PID */
+               rtsd    r15,8
+               nop
+
+#endif
 .end _hw_exception_handler
 
+#ifdef CONFIG_MMU
+/* Unaligned data access exception last on a 4k page for MMU.
+ * When this is called, we are in virtual mode with exceptions enabled
+ * and registers 1-13,15,17,18 saved.
+ *
+ * R3 = ESR
+ * R4 = EAR
+ * R7 = pointer to saved registers (struct pt_regs *regs)
+ *
+ * This handler perform the access, and returns via ret_from_exc.
+ */
+.global _unaligned_data_exception
+.ent _unaligned_data_exception
+_unaligned_data_exception:
+       andi    r8, r3, 0x3E0;  /* Mask and extract the register operand */
+       BSRLI(r8,r8,2);         /* r8 >> 2 = register operand * 8 */
+       andi    r6, r3, 0x400;  /* Extract ESR[S] */
+       bneid   r6, ex_sw_vm;
+       andi    r6, r3, 0x800;  /* Extract ESR[W] - delay slot */
+ex_lw_vm:
+       beqid   r6, ex_lhw_vm;
+       lbui    r5, r4, 0;      /* Exception address in r4 - delay slot */
+/* Load a word, byte-by-byte from destination address and save it in tmp space*/
+       la      r6, r0, ex_tmp_data_loc_0;
+       sbi     r5, r6, 0;
+       lbui    r5, r4, 1;
+       sbi     r5, r6, 1;
+       lbui    r5, r4, 2;
+       sbi     r5, r6, 2;
+       lbui    r5, r4, 3;
+       sbi     r5, r6, 3;
+       brid    ex_lw_tail_vm;
+/* Get the destination register value into r3 - delay slot */
+       lwi     r3, r6, 0;
+ex_lhw_vm:
+       /* Load a half-word, byte-by-byte from destination address and
+        * save it in tmp space */
+       la      r6, r0, ex_tmp_data_loc_0;
+       sbi     r5, r6, 0;
+       lbui    r5, r4, 1;
+       sbi     r5, r6, 1;
+       lhui    r3, r6, 0;      /* Get the destination register value into r3 */
+ex_lw_tail_vm:
+       /* Form load_word jump table offset (lw_table_vm + (8 * regnum)) */
+       addik   r5, r8, lw_table_vm;
+       bra     r5;
+ex_lw_end_vm:                  /* Exception handling of load word, ends */
+       brai    ret_from_exc;
+ex_sw_vm:
+/* Form store_word jump table offset (sw_table_vm + (8 * regnum)) */
+       addik   r5, r8, sw_table_vm;
+       bra     r5;
+ex_sw_tail_vm:
+       la      r5, r0, ex_tmp_data_loc_0;
+       beqid   r6, ex_shw_vm;
+       swi     r3, r5, 0;      /* Get the word - delay slot */
+       /* Store the word, byte-by-byte into destination address */
+       lbui    r3, r5, 0;
+       sbi     r3, r4, 0;
+       lbui    r3, r5, 1;
+       sbi     r3, r4, 1;
+       lbui    r3, r5, 2;
+       sbi     r3, r4, 2;
+       lbui    r3, r5, 3;
+       brid    ret_from_exc;
+       sbi     r3, r4, 3;      /* Delay slot */
+ex_shw_vm:
+       /* Store the lower half-word, byte-by-byte into destination address */
+       lbui    r3, r5, 2;
+       sbi     r3, r4, 0;
+       lbui    r3, r5, 3;
+       brid    ret_from_exc;
+       sbi     r3, r4, 1;      /* Delay slot */
+ex_sw_end_vm:                  /* Exception handling of store word, ends. */
+.end _unaligned_data_exception
+#endif /* CONFIG_MMU */
+
 ex_handler_unhandled:
 /* FIXME add handle function for unhandled exception - dump register */
        bri 0
 
+/*
+ * hw_exception_handler Jump Table
+ * - Contains code snippets for each register that caused the unalign exception
+ * - Hence exception handler is NOT self-modifying
+ * - Separate table for load exceptions and store exceptions.
+ * - Each table is of size: (8 * 32) = 256 bytes
+ */
+
 .section .text
 .align 4
 lw_table:
@@ -407,7 +1061,11 @@ lw_r27:           R3_TO_LWREG     (27);
 lw_r28:                R3_TO_LWREG     (28);
 lw_r29:                R3_TO_LWREG     (29);
 lw_r30:                R3_TO_LWREG     (30);
+#ifdef CONFIG_MMU
+lw_r31:        R3_TO_LWREG_V   (31);
+#else
 lw_r31:                R3_TO_LWREG     (31);
+#endif
 
 sw_table:
 sw_r0:         SWREG_TO_R3     (0);
@@ -441,7 +1099,81 @@ sw_r27:           SWREG_TO_R3     (27);
 sw_r28:                SWREG_TO_R3     (28);
 sw_r29:                SWREG_TO_R3     (29);
 sw_r30:                SWREG_TO_R3     (30);
+#ifdef CONFIG_MMU
+sw_r31:                SWREG_TO_R3_V   (31);
+#else
 sw_r31:                SWREG_TO_R3     (31);
+#endif
+
+#ifdef CONFIG_MMU
+lw_table_vm:
+lw_r0_vm:      R3_TO_LWREG_VM          (0);
+lw_r1_vm:      R3_TO_LWREG_VM_V        (1);
+lw_r2_vm:      R3_TO_LWREG_VM_V        (2);
+lw_r3_vm:      R3_TO_LWREG_VM_V        (3);
+lw_r4_vm:      R3_TO_LWREG_VM_V        (4);
+lw_r5_vm:      R3_TO_LWREG_VM_V        (5);
+lw_r6_vm:      R3_TO_LWREG_VM_V        (6);
+lw_r7_vm:      R3_TO_LWREG_VM_V        (7);
+lw_r8_vm:      R3_TO_LWREG_VM_V        (8);
+lw_r9_vm:      R3_TO_LWREG_VM_V        (9);
+lw_r10_vm:     R3_TO_LWREG_VM_V        (10);
+lw_r11_vm:     R3_TO_LWREG_VM_V        (11);
+lw_r12_vm:     R3_TO_LWREG_VM_V        (12);
+lw_r13_vm:     R3_TO_LWREG_VM_V        (13);
+lw_r14_vm:     R3_TO_LWREG_VM          (14);
+lw_r15_vm:     R3_TO_LWREG_VM_V        (15);
+lw_r16_vm:     R3_TO_LWREG_VM          (16);
+lw_r17_vm:     R3_TO_LWREG_VM_V        (17);
+lw_r18_vm:     R3_TO_LWREG_VM_V        (18);
+lw_r19_vm:     R3_TO_LWREG_VM          (19);
+lw_r20_vm:     R3_TO_LWREG_VM          (20);
+lw_r21_vm:     R3_TO_LWREG_VM          (21);
+lw_r22_vm:     R3_TO_LWREG_VM          (22);
+lw_r23_vm:     R3_TO_LWREG_VM          (23);
+lw_r24_vm:     R3_TO_LWREG_VM          (24);
+lw_r25_vm:     R3_TO_LWREG_VM          (25);
+lw_r26_vm:     R3_TO_LWREG_VM          (26);
+lw_r27_vm:     R3_TO_LWREG_VM          (27);
+lw_r28_vm:     R3_TO_LWREG_VM          (28);
+lw_r29_vm:     R3_TO_LWREG_VM          (29);
+lw_r30_vm:     R3_TO_LWREG_VM          (30);
+lw_r31_vm:     R3_TO_LWREG_VM_V        (31);
+
+sw_table_vm:
+sw_r0_vm:      SWREG_TO_R3_VM          (0);
+sw_r1_vm:      SWREG_TO_R3_VM_V        (1);
+sw_r2_vm:      SWREG_TO_R3_VM_V        (2);
+sw_r3_vm:      SWREG_TO_R3_VM_V        (3);
+sw_r4_vm:      SWREG_TO_R3_VM_V        (4);
+sw_r5_vm:      SWREG_TO_R3_VM_V        (5);
+sw_r6_vm:      SWREG_TO_R3_VM_V        (6);
+sw_r7_vm:      SWREG_TO_R3_VM_V        (7);
+sw_r8_vm:      SWREG_TO_R3_VM_V        (8);
+sw_r9_vm:      SWREG_TO_R3_VM_V        (9);
+sw_r10_vm:     SWREG_TO_R3_VM_V        (10);
+sw_r11_vm:     SWREG_TO_R3_VM_V        (11);
+sw_r12_vm:     SWREG_TO_R3_VM_V        (12);
+sw_r13_vm:     SWREG_TO_R3_VM_V        (13);
+sw_r14_vm:     SWREG_TO_R3_VM          (14);
+sw_r15_vm:     SWREG_TO_R3_VM_V        (15);
+sw_r16_vm:     SWREG_TO_R3_VM          (16);
+sw_r17_vm:     SWREG_TO_R3_VM_V        (17);
+sw_r18_vm:     SWREG_TO_R3_VM_V        (18);
+sw_r19_vm:     SWREG_TO_R3_VM          (19);
+sw_r20_vm:     SWREG_TO_R3_VM          (20);
+sw_r21_vm:     SWREG_TO_R3_VM          (21);
+sw_r22_vm:     SWREG_TO_R3_VM          (22);
+sw_r23_vm:     SWREG_TO_R3_VM          (23);
+sw_r24_vm:     SWREG_TO_R3_VM          (24);
+sw_r25_vm:     SWREG_TO_R3_VM          (25);
+sw_r26_vm:     SWREG_TO_R3_VM          (26);
+sw_r27_vm:     SWREG_TO_R3_VM          (27);
+sw_r28_vm:     SWREG_TO_R3_VM          (28);
+sw_r29_vm:     SWREG_TO_R3_VM          (29);
+sw_r30_vm:     SWREG_TO_R3_VM          (30);
+sw_r31_vm:     SWREG_TO_R3_VM_V        (31);
+#endif /* CONFIG_MMU */
 
 /* Temporary data structures used in the handler */
 .section .data
index 5f71790e3c3ce68c953c65e33e970a2ff947ef6a..59ff20e33e0cde5b22b7f95fb0517323066c4440 100644 (file)
@@ -45,3 +45,5 @@ extern void __udivsi3(void);
 EXPORT_SYMBOL(__udivsi3);
 extern void __umodsi3(void);
 EXPORT_SYMBOL(__umodsi3);
+extern char *_ebss;
+EXPORT_SYMBOL_GPL(_ebss);
diff --git a/arch/microblaze/kernel/misc.S b/arch/microblaze/kernel/misc.S
new file mode 100644 (file)
index 0000000..df16c62
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Miscellaneous low-level MMU functions.
+ *
+ * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2008-2009 PetaLogix
+ * Copyright (C) 2007 Xilinx, Inc.  All rights reserved.
+ *
+ * Derived from arch/ppc/kernel/misc.S
+ *
+ * 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/linkage.h>
+#include <linux/sys.h>
+#include <asm/unistd.h>
+#include <linux/errno.h>
+#include <asm/mmu.h>
+#include <asm/page.h>
+
+       .text
+/*
+ * Flush MMU TLB
+ *
+ * We avoid flushing the pinned 0, 1 and possibly 2 entries.
+ */
+.globl _tlbia;
+.align 4;
+_tlbia:
+       addik   r12, r0, 63 /* flush all entries (63 - 3) */
+       /* isync */
+_tlbia_1:
+       mts     rtlbx, r12
+       nop
+       mts     rtlbhi, r0 /* flush: ensure V is clear */
+       nop
+       addik   r11, r12, -2
+       bneid   r11, _tlbia_1 /* loop for all entries */
+       addik   r12, r12, -1
+       /* sync */
+       rtsd    r15, 8
+       nop
+
+/*
+ * Flush MMU TLB for a particular address (in r5)
+ */
+.globl _tlbie;
+.align 4;
+_tlbie:
+       mts     rtlbsx, r5 /* look up the address in TLB */
+       nop
+       mfs     r12, rtlbx /* Retrieve index */
+       nop
+       blti    r12, _tlbie_1 /* Check if found */
+       mts     rtlbhi, r0 /* flush: ensure V is clear */
+       nop
+_tlbie_1:
+       rtsd    r15, 8
+       nop
+
+/*
+ * Allocate TLB entry for early console
+ */
+.globl early_console_reg_tlb_alloc;
+.align 4;
+early_console_reg_tlb_alloc:
+       /*
+        * Load a TLB entry for the UART, so that microblaze_progress() can use
+        * the UARTs nice and early.  We use a 4k real==virtual mapping.
+        */
+       ori     r4, r0, 63
+       mts     rtlbx, r4 /* TLB slot 2 */
+
+       or      r4,r5,r0
+       andi    r4,r4,0xfffff000
+       ori     r4,r4,(TLB_WR|TLB_I|TLB_M|TLB_G)
+
+       andi    r5,r5,0xfffff000
+       ori     r5,r5,(TLB_VALID | TLB_PAGESZ(PAGESZ_4K))
+
+       mts     rtlblo,r4 /* Load the data portion of the entry */
+       nop
+       mts     rtlbhi,r5 /* Load the tag portion of the entry */
+       nop
+       rtsd    r15, 8
+       nop
+
+/*
+ * Copy a whole page (4096 bytes).
+ */
+#define COPY_16_BYTES          \
+       lwi     r7, r6, 0;      \
+       lwi     r8, r6, 4;      \
+       lwi     r9, r6, 8;      \
+       lwi     r10, r6, 12;    \
+       swi     r7, r5, 0;      \
+       swi     r8, r5, 4;      \
+       swi     r9, r5, 8;      \
+       swi     r10, r5, 12
+
+
+/* FIXME DCACHE_LINE_BYTES (CONFIG_XILINX_MICROBLAZE0_DCACHE_LINE_LEN * 4)*/
+#define DCACHE_LINE_BYTES (4 * 4)
+
+.globl copy_page;
+.align 4;
+copy_page:
+       ori     r11, r0, (PAGE_SIZE/DCACHE_LINE_BYTES) - 1
+_copy_page_loop:
+       COPY_16_BYTES
+#if DCACHE_LINE_BYTES >= 32
+       COPY_16_BYTES
+#endif
+       addik   r6, r6, DCACHE_LINE_BYTES
+       addik   r5, r5, DCACHE_LINE_BYTES
+       bneid   r11, _copy_page_loop
+       addik   r11, r11, -1
+       rtsd    r15, 8
+       nop
index 07d4fa339eda8c6c0384c1c0b70b8ba09b55781d..00b12c6d5326767fc9260df77801a699050cdee7 100644 (file)
@@ -126,9 +126,54 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
        else
                childregs->r1 = ((unsigned long) ti) + THREAD_SIZE;
 
+#ifndef CONFIG_MMU
        memset(&ti->cpu_context, 0, sizeof(struct cpu_context));
        ti->cpu_context.r1 = (unsigned long)childregs;
        ti->cpu_context.msr = (unsigned long)childregs->msr;
+#else
+
+       /* if creating a kernel thread then update the current reg (we don't
+        * want to use the parent's value when restoring by POP_STATE) */
+       if (kernel_mode(regs))
+               /* save new current on stack to use POP_STATE */
+               childregs->CURRENT_TASK = (unsigned long)p;
+       /* if returning to user then use the parent's value of this register */
+
+       /* if we're creating a new kernel thread then just zeroing all
+        * the registers. That's OK for a brand new thread.*/
+       /* Pls. note that some of them will be restored in POP_STATE */
+       if (kernel_mode(regs))
+               memset(&ti->cpu_context, 0, sizeof(struct cpu_context));
+       /* if this thread is created for fork/vfork/clone, then we want to
+        * restore all the parent's context */
+       /* in addition to the registers which will be restored by POP_STATE */
+       else {
+               ti->cpu_context = *(struct cpu_context *)regs;
+               childregs->msr |= MSR_UMS;
+       }
+
+       /* FIXME STATE_SAVE_PT_OFFSET; */
+       ti->cpu_context.r1  = (unsigned long)childregs - STATE_SAVE_ARG_SPACE;
+       /* we should consider the fact that childregs is a copy of the parent
+        * regs which were saved immediately after entering the kernel state
+        * before enabling VM. This MSR will be restored in switch_to and
+        * RETURN() and we want to have the right machine state there
+        * specifically this state must have INTs disabled before and enabled
+        * after performing rtbd
+        * compose the right MSR for RETURN(). It will work for switch_to also
+        * excepting for VM and UMS
+        * don't touch UMS , CARRY and cache bits
+        * right now MSR is a copy of parent one */
+       childregs->msr |= MSR_BIP;
+       childregs->msr &= ~MSR_EIP;
+       childregs->msr |= MSR_IE;
+       childregs->msr &= ~MSR_VM;
+       childregs->msr |= MSR_VMS;
+       childregs->msr |= MSR_EE; /* exceptions will be enabled*/
+
+       ti->cpu_context.msr = (childregs->msr|MSR_VM);
+       ti->cpu_context.msr &= ~MSR_UMS; /* switch_to to kernel mode */
+#endif
        ti->cpu_context.r15 = (unsigned long)ret_from_fork - 8;
 
        if (clone_flags & CLONE_SETTLS)
@@ -137,6 +182,7 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
        return 0;
 }
 
+#ifndef CONFIG_MMU
 /*
  * Return saved PC of a blocked thread.
  */
@@ -151,6 +197,7 @@ unsigned long thread_saved_pc(struct task_struct *tsk)
        else
                return ctx->r14;
 }
+#endif
 
 static void kernel_thread_helper(int (*fn)(void *), void *arg)
 {
@@ -173,6 +220,7 @@ int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
        return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0,
                        &regs, 0, NULL, NULL);
 }
+EXPORT_SYMBOL_GPL(kernel_thread);
 
 unsigned long get_wchan(struct task_struct *p)
 {
@@ -188,3 +236,14 @@ void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long usp)
        regs->r1 = usp;
        regs->pt_mode = 0;
 }
+
+#ifdef CONFIG_MMU
+#include <linux/elfcore.h>
+/*
+ * Set up a thread for executing a new program
+ */
+int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs)
+{
+       return 0; /* MicroBlaze has no separate FPU registers */
+}
+#endif /* CONFIG_MMU */
index 34c48718061afcdd7781e7160dcd998e0c45c025..c005cc6f1aaf7f5703fb58219ddba588dc4127e4 100644 (file)
@@ -509,12 +509,13 @@ static void __init early_init_dt_check_for_initrd(unsigned long node)
 
        prop = of_get_flat_dt_prop(node, "linux,initrd-start", &l);
        if (prop) {
-               initrd_start = (unsigned long)__va(of_read_ulong(prop, l/4));
+               initrd_start = (unsigned long)
+                                       __va((u32)of_read_ulong(prop, l/4));
 
                prop = of_get_flat_dt_prop(node, "linux,initrd-end", &l);
                if (prop) {
                        initrd_end = (unsigned long)
-                                       __va(of_read_ulong(prop, l/4));
+                                       __va((u32)of_read_ulong(prop, 1/4));
                        initrd_below_start_ok = 1;
                } else {
                        initrd_start = 0;
@@ -563,7 +564,9 @@ static int __init early_init_dt_scan_chosen(unsigned long node,
                strlcpy(cmd_line, p, min((int)l, COMMAND_LINE_SIZE));
 
 #ifdef CONFIG_CMDLINE
+#ifndef CONFIG_CMDLINE_FORCE
        if (p == NULL || l == 0 || (l == 1 && (*p) == 0))
+#endif
                strlcpy(cmd_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
 #endif /* CONFIG_CMDLINE */
 
index eb6b41758e2301fd0d24ca4eebcb9d004257bad3..8709bea09604585958cd95d72c107c65d1396855 100644 (file)
@@ -42,10 +42,6 @@ char cmd_line[COMMAND_LINE_SIZE];
 
 void __init setup_arch(char **cmdline_p)
 {
-#ifdef CONFIG_CMDLINE_FORCE
-       strlcpy(cmd_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
-       strlcpy(boot_command_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
-#endif
        *cmdline_p = cmd_line;
 
        console_verbose();
@@ -102,14 +98,34 @@ void __init machine_early_init(const char *cmdline, unsigned int ram,
 {
        unsigned long *src, *dst = (unsigned long *)0x0;
 
+       /* If CONFIG_MTD_UCLINUX is defined, assume ROMFS is at the
+        * end of kernel. There are two position which we want to check.
+        * The first is __init_end and the second __bss_start.
+        */
+#ifdef CONFIG_MTD_UCLINUX
+       int romfs_size;
+       unsigned int romfs_base;
+       char *old_klimit = klimit;
+
+       romfs_base = (ram ? ram : (unsigned int)&__init_end);
+       romfs_size = PAGE_ALIGN(get_romfs_len((unsigned *)romfs_base));
+       if (!romfs_size) {
+               romfs_base = (unsigned int)&__bss_start;
+               romfs_size = PAGE_ALIGN(get_romfs_len((unsigned *)romfs_base));
+       }
+
+       /* Move ROMFS out of BSS before clearing it */
+       if (romfs_size > 0) {
+               memmove(&_ebss, (int *)romfs_base, romfs_size);
+               klimit += romfs_size;
+       }
+#endif
+
 /* clearing bss section */
        memset(__bss_start, 0, __bss_stop-__bss_start);
        memset(_ssbss, 0, _esbss-_ssbss);
 
-       /*
-        * Copy command line passed from bootloader, or use default
-        * if none provided, or forced
-        */
+       /* Copy command line passed from bootloader */
 #ifndef CONFIG_CMDLINE_BOOL
        if (cmdline && cmdline[0] != '\0')
                strlcpy(cmd_line, cmdline, COMMAND_LINE_SIZE);
@@ -126,27 +142,15 @@ void __init machine_early_init(const char *cmdline, unsigned int ram,
        printk(KERN_NOTICE "Found FDT at 0x%08x\n", fdt);
 
 #ifdef CONFIG_MTD_UCLINUX
-       {
-               int size;
-               unsigned int romfs_base;
-               romfs_base = (ram ? ram : (unsigned int)&__init_end);
-               /* if CONFIG_MTD_UCLINUX_EBSS is defined, assume ROMFS is at the
-                * end of kernel, which is ROMFS_LOCATION defined above. */
-               size = PAGE_ALIGN(get_romfs_len((unsigned *)romfs_base));
-               early_printk("Found romfs @ 0x%08x (0x%08x)\n",
-                               romfs_base, size);
-               early_printk("#### klimit %p ####\n", klimit);
-               BUG_ON(size < 0); /* What else can we do? */
-
-               /* Use memmove to handle likely case of memory overlap */
-               early_printk("Moving 0x%08x bytes from 0x%08x to 0x%08x\n",
-                       size, romfs_base, (unsigned)&_ebss);
-               memmove(&_ebss, (int *)romfs_base, size);
-
-               /* update klimit */
-               klimit += PAGE_ALIGN(size);
-               early_printk("New klimit: 0x%08x\n", (unsigned)klimit);
-       }
+       early_printk("Found romfs @ 0x%08x (0x%08x)\n",
+                       romfs_base, romfs_size);
+       early_printk("#### klimit %p ####\n", old_klimit);
+       BUG_ON(romfs_size < 0); /* What else can we do? */
+
+       early_printk("Moved 0x%08x bytes from 0x%08x to 0x%08x\n",
+                       romfs_size, romfs_base, (unsigned)&_ebss);
+
+       early_printk("New klimit: 0x%08x\n", (unsigned)klimit);
 #endif
 
        for (src = __ivt_start; src < __ivt_end; src++, dst++)
index 40d36931e363eccefd2857383e60fb162f6bf492..4c0e6521b1140e6d92183fb165b03076253a889d 100644 (file)
@@ -152,8 +152,8 @@ struct rt_sigframe {
        unsigned long tramp[2]; /* signal trampoline */
 };
 
-static int
-restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc, int *rval_p)
+static int restore_sigcontext(struct pt_regs *regs,
+                               struct sigcontext __user *sc, int *rval_p)
 {
        unsigned int err = 0;
 
@@ -211,11 +211,10 @@ badframe:
 
 asmlinkage int sys_rt_sigreturn(struct pt_regs *regs)
 {
-       struct rt_sigframe *frame =
-                       (struct rt_sigframe *)(regs->r1 + STATE_SAVE_ARG_SPACE);
+       struct rt_sigframe __user *frame =
+               (struct rt_sigframe __user *)(regs->r1 + STATE_SAVE_ARG_SPACE);
 
        sigset_t set;
-       stack_t st;
        int rval;
 
        if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
@@ -233,11 +232,10 @@ asmlinkage int sys_rt_sigreturn(struct pt_regs *regs)
        if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &rval))
                goto badframe;
 
-       if (__copy_from_user((void *)&st, &frame->uc.uc_stack, sizeof(st)))
-               goto badframe;
        /* It is more difficult to avoid calling this function than to
         call it and ignore errors. */
-       do_sigaltstack(&st, NULL, regs->r1);
+       if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->r1))
+               goto badframe;
 
        return rval;
 
@@ -251,7 +249,7 @@ badframe:
  */
 
 static int
-setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs,
+setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
                unsigned long mask)
 {
        int err = 0;
@@ -278,7 +276,7 @@ setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs,
 /*
  * Determine which stack to use..
  */
-static inline void *
+static inline void __user *
 get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size)
 {
        /* Default to using normal stack */
@@ -287,87 +285,13 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size)
        if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && !on_sig_stack(sp))
                sp = current->sas_ss_sp + current->sas_ss_size;
 
-       return (void *)((sp - frame_size) & -8UL);
-}
-
-static void setup_frame(int sig, struct k_sigaction *ka,
-                       sigset_t *set, struct pt_regs *regs)
-{
-       struct sigframe *frame;
-       int err = 0;
-       int signal;
-
-       frame = get_sigframe(ka, regs, sizeof(*frame));
-
-       if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
-               goto give_sigsegv;
-
-       signal = current_thread_info()->exec_domain
-               && current_thread_info()->exec_domain->signal_invmap
-               && sig < 32
-               ? current_thread_info()->exec_domain->signal_invmap[sig]
-               : sig;
-
-       err |= setup_sigcontext(&frame->sc, regs, set->sig[0]);
-
-       if (_NSIG_WORDS > 1) {
-               err |= __copy_to_user(frame->extramask, &set->sig[1],
-                                       sizeof(frame->extramask));
-       }
-
-       /* Set up to return from userspace. If provided, use a stub
-        already in userspace. */
-       /* minus 8 is offset to cater for "rtsd r15,8" offset */
-       if (ka->sa.sa_flags & SA_RESTORER) {
-               regs->r15 = ((unsigned long)ka->sa.sa_restorer)-8;
-       } else {
-               /* Note, these encodings are _big endian_! */
-
-               /* addi r12, r0, __NR_sigreturn */
-               err |= __put_user(0x31800000 | __NR_sigreturn ,
-                               frame->tramp + 0);
-               /* brki r14, 0x8 */
-               err |= __put_user(0xb9cc0008, frame->tramp + 1);
-
-               /* Return from sighandler will jump to the tramp.
-                Negative 8 offset because return is rtsd r15, 8 */
-               regs->r15 = ((unsigned long)frame->tramp)-8;
-
-               __invalidate_cache_sigtramp((unsigned long)frame->tramp);
-       }
-
-       if (err)
-               goto give_sigsegv;
-
-       /* Set up registers for signal handler */
-       regs->r1 = (unsigned long) frame - STATE_SAVE_ARG_SPACE;
-
-       /* Signal handler args: */
-       regs->r5 = signal; /* Arg 0: signum */
-       regs->r6 = (unsigned long) &frame->sc; /* arg 1: sigcontext */
-
-       /* Offset of 4 to handle microblaze rtid r14, 0 */
-       regs->pc = (unsigned long)ka->sa.sa_handler;
-
-       set_fs(USER_DS);
-
-#ifdef DEBUG_SIG
-       printk(KERN_INFO "SIG deliver (%s:%d): sp=%p pc=%08lx\n",
-               current->comm, current->pid, frame, regs->pc);
-#endif
-
-       return;
-
-give_sigsegv:
-       if (sig == SIGSEGV)
-               ka->sa.sa_handler = SIG_DFL;
-       force_sig(SIGSEGV, current);
+       return (void __user *)((sp - frame_size) & -8UL);
 }
 
 static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
                        sigset_t *set, struct pt_regs *regs)
 {
-       struct rt_sigframe *frame;
+       struct rt_sigframe __user *frame;
        int err = 0;
        int signal;
 
@@ -382,7 +306,8 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
                ? current_thread_info()->exec_domain->signal_invmap[sig]
                : sig;
 
-       err |= copy_siginfo_to_user(&frame->info, info);
+       if (info)
+               err |= copy_siginfo_to_user(&frame->info, info);
 
        /* Create the ucontext. */
        err |= __put_user(0, &frame->uc.uc_flags);
@@ -463,7 +388,15 @@ handle_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler)
        case -ERESTARTNOINTR:
 do_restart:
                /* offset of 4 bytes to re-execute trap (brki) instruction */
+#ifndef CONFIG_MMU
                regs->pc -= 4;
+#else
+               /* offset of 8 bytes required = 4 for rtbd
+                  offset, plus 4 for size of
+                       "brki r14,8"
+                  instruction. */
+               regs->pc -= 8;
+#endif
                break;
        }
 }
@@ -480,7 +413,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
        if (ka->sa.sa_flags & SA_SIGINFO)
                setup_rt_frame(sig, ka, info, oldset, regs);
        else
-               setup_frame(sig, ka, oldset, regs);
+               setup_rt_frame(sig, ka, NULL, oldset, regs);
 
        if (ka->sa.sa_flags & SA_ONESHOT)
                ka->sa.sa_handler = SIG_DFL;
index 3bb42ec924c2ccc299004ab7b525724ab5cd8c9c..376d1789f7c065e436fdf39d7b3e2f8f2318494f 100644 (file)
@@ -2,7 +2,11 @@ ENTRY(sys_call_table)
        .long sys_restart_syscall       /* 0 - old "setup()" system call,
                                         * used for restarting */
        .long sys_exit
-       .long sys_ni_syscall            /* was fork */
+#ifdef CONFIG_MMU
+       .long sys_fork_wrapper
+#else
+       .long sys_ni_syscall
+#endif
        .long sys_read
        .long sys_write
        .long sys_open                  /* 5 */
index 293ef486013aa9fa65b8f2204221615af22ffb38..eaaaf805f31b6d32039376df4f0e9ffdec777b5d 100644 (file)
@@ -22,14 +22,6 @@ void trap_init(void)
        __enable_hw_exceptions();
 }
 
-void __bad_xchg(volatile void *ptr, int size)
-{
-       printk(KERN_INFO "xchg: bad data size: pc 0x%p, ptr 0x%p, size %d\n",
-               __builtin_return_address(0), ptr, size);
-       BUG();
-}
-EXPORT_SYMBOL(__bad_xchg);
-
 static int kstack_depth_to_print = 24;
 
 static int __init kstack_setup(char *s)
@@ -105,3 +97,37 @@ void dump_stack(void)
        show_stack(NULL, NULL);
 }
 EXPORT_SYMBOL(dump_stack);
+
+#ifdef CONFIG_MMU
+void __bug(const char *file, int line, void *data)
+{
+       if (data)
+               printk(KERN_CRIT "kernel BUG at %s:%d (data = %p)!\n",
+                       file, line, data);
+       else
+               printk(KERN_CRIT "kernel BUG at %s:%d!\n", file, line);
+
+       machine_halt();
+}
+
+int bad_trap(int trap_num, struct pt_regs *regs)
+{
+       printk(KERN_CRIT
+               "unimplemented trap %d called at 0x%08lx, pid %d!\n",
+               trap_num, regs->pc, current->pid);
+       return -ENOSYS;
+}
+
+int debug_trap(struct pt_regs *regs)
+{
+       int i;
+       printk(KERN_CRIT "debug trap\n");
+       for (i = 0; i < 32; i++) {
+               /* printk("r%i:%08X\t",i,regs->gpr[i]); */
+               if ((i % 4) == 3)
+                       printk(KERN_CRIT "\n");
+       }
+       printk(KERN_CRIT "pc:%08lX\tmsr:%08lX\n", regs->pc, regs->msr);
+       return -ENOSYS;
+}
+#endif
index 840385e51291f507b9560f1ac36c43585f232870..8ae807ab7a51bd568709c2c57b9b5b189cfeb951 100644 (file)
@@ -17,8 +17,7 @@ ENTRY(_start)
 jiffies = jiffies_64 + 4;
 
 SECTIONS {
-       . = CONFIG_KERNEL_BASE_ADDR;
-
+       . = CONFIG_KERNEL_START;
        .text : {
                _text = . ;
                _stext = . ;
@@ -132,6 +131,8 @@ SECTIONS {
                __con_initcall_end = .;
        }
 
+       SECURITY_INIT
+
        __init_end_before_initramfs = .;
 
        .init.ramfs ALIGN(4096) : {
index d27126bf306a61e08ec9b1664298fcb9d1caab39..71c8cb6c9e43767a4ebd43f9e4c54d3e9820928b 100644 (file)
@@ -10,4 +10,5 @@ else
 lib-y += memcpy.o memmove.o
 endif
 
-lib-y +=  uaccess.o
+lib-$(CONFIG_NO_MMU) += uaccess.o
+lib-$(CONFIG_MMU) += uaccess_old.o
index 809340070a13c1142567ba814384f7632b0d9952..f08e74591418dc4d15563620eaff7bddbc0de95c 100644 (file)
 /* Revised by Kenneth Albanowski for m68knommu. Basic problem: unaligned access
  kills, so most of the assembly has to go. */
 
-#include <net/checksum.h>
-#include <asm/checksum.h>
 #include <linux/module.h>
+#include <net/checksum.h>
+
+#include <asm/byteorder.h>
 
 static inline unsigned short from32to16(unsigned long x)
 {
@@ -102,6 +103,7 @@ __sum16 ip_fast_csum(const void *iph, unsigned int ihl)
 {
        return (__force __sum16)~do_csum(iph, ihl*4);
 }
+EXPORT_SYMBOL(ip_fast_csum);
 
 /*
  * computes the checksum of a memory block at buff, length len,
@@ -115,15 +117,16 @@ __sum16 ip_fast_csum(const void *iph, unsigned int ihl)
  *
  * it's best to have buff aligned on a 32-bit boundary
  */
-__wsum csum_partial(const void *buff, int len, __wsum sum)
+__wsum csum_partial(const void *buff, int len, __wsum wsum)
 {
+       unsigned int sum = (__force unsigned int)wsum;
        unsigned int result = do_csum(buff, len);
 
        /* add in old sum, and carry.. */
        result += sum;
        if (sum > result)
                result += 1;
-       return result;
+       return (__force __wsum)result;
 }
 EXPORT_SYMBOL(csum_partial);
 
@@ -131,9 +134,9 @@ EXPORT_SYMBOL(csum_partial);
  * this routine is used for miscellaneous IP-like checksums, mainly
  * in icmp.c
  */
-__sum16 ip_compute_csum(const unsigned char *buff, int len)
+__sum16 ip_compute_csum(const void *buff, int len)
 {
-       return ~do_csum(buff, len);
+       return (__force __sum16)~do_csum(buff, len);
 }
 EXPORT_SYMBOL(ip_compute_csum);
 
@@ -141,12 +144,18 @@ EXPORT_SYMBOL(ip_compute_csum);
  * copy from fs while checksumming, otherwise like csum_partial
  */
 __wsum
-csum_partial_copy_from_user(const char __user *src, char *dst, int len,
-                                               int sum, int *csum_err)
+csum_partial_copy_from_user(const void __user *src, void *dst, int len,
+                                               __wsum sum, int *csum_err)
 {
-       if (csum_err)
+       int missing;
+
+       missing = __copy_from_user(dst, src, len);
+       if (missing) {
+               memset(dst + len - missing, 0, missing);
+               *csum_err = -EFAULT;
+       } else
                *csum_err = 0;
-       memcpy(dst, src, len);
+
        return csum_partial(dst, len, sum);
 }
 EXPORT_SYMBOL(csum_partial_copy_from_user);
@@ -155,7 +164,7 @@ EXPORT_SYMBOL(csum_partial_copy_from_user);
  * copy from ds while checksumming, otherwise like csum_partial
  */
 __wsum
-csum_partial_copy(const char *src, char *dst, int len, int sum)
+csum_partial_copy(const void *src, void *dst, int len, __wsum sum)
 {
        memcpy(dst, src, len);
        return csum_partial(dst, len, sum);
index 5880119c448721e9064b3a315cba6810907b6d31..6a907c58a4bc075ae84aa91797d34ad7565d9d54 100644 (file)
@@ -154,8 +154,3 @@ void *memcpy(void *v_dst, const void *v_src, __kernel_size_t c)
 }
 EXPORT_SYMBOL(memcpy);
 #endif /* __HAVE_ARCH_MEMCPY */
-
-void *cacheable_memcpy(void *d, const void *s, __kernel_size_t c)
-{
-       return memcpy(d, s, c);
-}
diff --git a/arch/microblaze/lib/uaccess_old.S b/arch/microblaze/lib/uaccess_old.S
new file mode 100644 (file)
index 0000000..67f991c
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2009 PetaLogix
+ * Copyright (C) 2007 LynuxWorks, 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.
+ */
+
+#include <linux/errno.h>
+#include <linux/linkage.h>
+
+/*
+ * int __strncpy_user(char *to, char *from, int len);
+ *
+ * Returns:
+ *  -EFAULT  for an exception
+ *  len      if we hit the buffer limit
+ *  bytes copied
+ */
+
+       .text
+.globl __strncpy_user;
+.align 4;
+__strncpy_user:
+
+       /*
+        * r5 - to
+        * r6 - from
+        * r7 - len
+        * r3 - temp count
+        * r4 - temp val
+        */
+       addik   r3,r7,0         /* temp_count = len */
+       beqi    r3,3f
+1:
+       lbu     r4,r6,r0
+       sb      r4,r5,r0
+
+       addik   r3,r3,-1
+       beqi    r3,2f           /* break on len */
+
+       addik   r5,r5,1
+       bneid   r4,1b
+       addik   r6,r6,1         /* delay slot */
+       addik   r3,r3,1         /* undo "temp_count--" */
+2:
+       rsubk   r3,r3,r7        /* temp_count = len - temp_count */
+3:
+       rtsd    r15,8
+       nop
+
+
+       .section        .fixup, "ax"
+       .align  2
+4:
+       brid    3b
+       addik   r3,r0, -EFAULT
+
+       .section        __ex_table, "a"
+       .word   1b,4b
+
+/*
+ * int __strnlen_user(char __user *str, int maxlen);
+ *
+ * Returns:
+ *  0 on error
+ *  maxlen + 1  if no NUL byte found within maxlen bytes
+ *  size of the string (including NUL byte)
+ */
+
+       .text
+.globl __strnlen_user;
+.align 4;
+__strnlen_user:
+       addik   r3,r6,0
+       beqi    r3,3f
+1:
+       lbu     r4,r5,r0
+       beqid   r4,2f           /* break on NUL */
+       addik   r3,r3,-1        /* delay slot */
+
+       bneid   r3,1b
+       addik   r5,r5,1         /* delay slot */
+
+       addik   r3,r3,-1        /* for break on len */
+2:
+       rsubk   r3,r3,r6
+3:
+       rtsd    r15,8
+       nop
+
+
+       .section        .fixup,"ax"
+4:
+       brid    3b
+       addk    r3,r0,r0
+
+       .section        __ex_table,"a"
+       .word   1b,4b
+
+/*
+ * int __copy_tofrom_user(char *to, char *from, int len)
+ * Return:
+ *   0 on success
+ *   number of not copied bytes on error
+ */
+       .text
+.globl __copy_tofrom_user;
+.align 4;
+__copy_tofrom_user:
+       /*
+        * r5 - to
+        * r6 - from
+        * r7, r3 - count
+        * r4 - tempval
+        */
+       addik   r3,r7,0
+       beqi    r3,3f
+1:
+       lbu     r4,r6,r0
+       addik   r6,r6,1
+2:
+       sb      r4,r5,r0
+       addik   r3,r3,-1
+       bneid   r3,1b
+       addik   r5,r5,1         /* delay slot */
+3:
+       rtsd    r15,8
+       nop
+
+
+       .section        __ex_table,"a"
+       .word   1b,3b,2b,3b
index bf9e4479a1fdb82fe142043db8072de44b854551..6c8a924d9e266f8313b7107abc844c853968f633 100644 (file)
@@ -3,3 +3,5 @@
 #
 
 obj-y := init.o
+
+obj-$(CONFIG_MMU) += pgtable.o mmu_context.o fault.o
diff --git a/arch/microblaze/mm/fault.c b/arch/microblaze/mm/fault.c
new file mode 100644 (file)
index 0000000..5e67cd1
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ *  arch/microblaze/mm/fault.c
+ *
+ *    Copyright (C) 2007 Xilinx, Inc.  All rights reserved.
+ *
+ *  Derived from "arch/ppc/mm/fault.c"
+ *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
+ *
+ *  Derived from "arch/i386/mm/fault.c"
+ *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
+ *
+ *  Modified by Cort Dougan and Paul Mackerras.
+ *
+ * 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/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/mmu.h>
+#include <asm/mmu_context.h>
+#include <asm/system.h>
+#include <linux/uaccess.h>
+#include <asm/exceptions.h>
+
+#if defined(CONFIG_KGDB)
+int debugger_kernel_faults = 1;
+#endif
+
+static unsigned long pte_misses;       /* updated by do_page_fault() */
+static unsigned long pte_errors;       /* updated by do_page_fault() */
+
+/*
+ * Check whether the instruction at regs->pc is a store using
+ * an update addressing form which will update r1.
+ */
+static int store_updates_sp(struct pt_regs *regs)
+{
+       unsigned int inst;
+
+       if (get_user(inst, (unsigned int *)regs->pc))
+               return 0;
+       /* check for 1 in the rD field */
+       if (((inst >> 21) & 0x1f) != 1)
+               return 0;
+       /* check for store opcodes */
+       if ((inst & 0xd0000000) == 0xd0000000)
+               return 1;
+       return 0;
+}
+
+
+/*
+ * bad_page_fault is called when we have a bad access from the kernel.
+ * It is called from do_page_fault above and from some of the procedures
+ * in traps.c.
+ */
+static void bad_page_fault(struct pt_regs *regs, unsigned long address, int sig)
+{
+       const struct exception_table_entry *fixup;
+/* MS: no context */
+       /* Are we prepared to handle this fault?  */
+       fixup = search_exception_tables(regs->pc);
+       if (fixup) {
+               regs->pc = fixup->fixup;
+               return;
+       }
+
+       /* kernel has accessed a bad area */
+#if defined(CONFIG_KGDB)
+       if (debugger_kernel_faults)
+               debugger(regs);
+#endif
+       die("kernel access of bad area", regs, sig);
+}
+
+/*
+ * The error_code parameter is ESR for a data fault,
+ * 0 for an instruction fault.
+ */
+void do_page_fault(struct pt_regs *regs, unsigned long address,
+                  unsigned long error_code)
+{
+       struct vm_area_struct *vma;
+       struct mm_struct *mm = current->mm;
+       siginfo_t info;
+       int code = SEGV_MAPERR;
+       int is_write = error_code & ESR_S;
+       int fault;
+
+       regs->ear = address;
+       regs->esr = error_code;
+
+       /* On a kernel SLB miss we can only check for a valid exception entry */
+       if (kernel_mode(regs) && (address >= TASK_SIZE)) {
+               printk(KERN_WARNING "kernel task_size exceed");
+               _exception(SIGSEGV, regs, code, address);
+       }
+
+       /* for instr TLB miss and instr storage exception ESR_S is undefined */
+       if ((error_code & 0x13) == 0x13 || (error_code & 0x11) == 0x11)
+               is_write = 0;
+
+#if defined(CONFIG_KGDB)
+       if (debugger_fault_handler && regs->trap == 0x300) {
+               debugger_fault_handler(regs);
+               return;
+       }
+#endif /* CONFIG_KGDB */
+
+       if (in_atomic() || mm == NULL) {
+               /* FIXME */
+               if (kernel_mode(regs)) {
+                       printk(KERN_EMERG
+                               "Page fault in kernel mode - Oooou!!! pid %d\n",
+                               current->pid);
+                       _exception(SIGSEGV, regs, code, address);
+                       return;
+               }
+               /* in_atomic() in user mode is really bad,
+                  as is current->mm == NULL. */
+               printk(KERN_EMERG "Page fault in user mode with "
+                      "in_atomic(), mm = %p\n", mm);
+               printk(KERN_EMERG "r15 = %lx  MSR = %lx\n",
+                      regs->r15, regs->msr);
+               die("Weird page fault", regs, SIGSEGV);
+       }
+
+       /* When running in the kernel we expect faults to occur only to
+        * addresses in user space.  All other faults represent errors in the
+        * kernel and should generate an OOPS.  Unfortunately, in the case of an
+        * erroneous fault occurring in a code path which already holds mmap_sem
+        * we will deadlock attempting to validate the fault against the
+        * address space.  Luckily the kernel only validly references user
+        * space from well defined areas of code, which are listed in the
+        * exceptions table.
+        *
+        * As the vast majority of faults will be valid we will only perform
+        * the source reference check when there is a possibility of a deadlock.
+        * Attempt to lock the address space, if we cannot we then validate the
+        * source.  If this is invalid we can skip the address space check,
+        * thus avoiding the deadlock.
+        */
+       if (!down_read_trylock(&mm->mmap_sem)) {
+               if (kernel_mode(regs) && !search_exception_tables(regs->pc))
+                       goto bad_area_nosemaphore;
+
+               down_read(&mm->mmap_sem);
+       }
+
+       vma = find_vma(mm, address);
+       if (!vma)
+               goto bad_area;
+
+       if (vma->vm_start <= address)
+               goto good_area;
+
+       if (!(vma->vm_flags & VM_GROWSDOWN))
+               goto bad_area;
+
+       if (!is_write)
+               goto bad_area;
+
+       /*
+        * N.B. The ABI allows programs to access up to
+        * a few hundred bytes below the stack pointer (TBD).
+        * The kernel signal delivery code writes up to about 1.5kB
+        * below the stack pointer (r1) before decrementing it.
+        * The exec code can write slightly over 640kB to the stack
+        * before setting the user r1.  Thus we allow the stack to
+        * expand to 1MB without further checks.
+        */
+       if (address + 0x100000 < vma->vm_end) {
+
+               /* get user regs even if this fault is in kernel mode */
+               struct pt_regs *uregs = current->thread.regs;
+               if (uregs == NULL)
+                       goto bad_area;
+
+               /*
+                * A user-mode access to an address a long way below
+                * the stack pointer is only valid if the instruction
+                * is one which would update the stack pointer to the
+                * address accessed if the instruction completed,
+                * i.e. either stwu rs,n(r1) or stwux rs,r1,rb
+                * (or the byte, halfword, float or double forms).
+                *
+                * If we don't check this then any write to the area
+                * between the last mapped region and the stack will
+                * expand the stack rather than segfaulting.
+                */
+               if (address + 2048 < uregs->r1
+                       && (kernel_mode(regs) || !store_updates_sp(regs)))
+                               goto bad_area;
+       }
+       if (expand_stack(vma, address))
+               goto bad_area;
+
+good_area:
+       code = SEGV_ACCERR;
+
+       /* a write */
+       if (is_write) {
+               if (!(vma->vm_flags & VM_WRITE))
+                       goto bad_area;
+       /* a read */
+       } else {
+               /* protection fault */
+               if (error_code & 0x08000000)
+                       goto bad_area;
+               if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
+                       goto bad_area;
+       }
+
+       /*
+        * If for any reason at all we couldn't handle the fault,
+        * make sure we exit gracefully rather than endlessly redo
+        * the fault.
+        */
+survive:
+       fault = handle_mm_fault(mm, vma, address, is_write);
+       if (unlikely(fault & VM_FAULT_ERROR)) {
+               if (fault & VM_FAULT_OOM)
+                       goto out_of_memory;
+               else if (fault & VM_FAULT_SIGBUS)
+                       goto do_sigbus;
+               BUG();
+       }
+       if (fault & VM_FAULT_MAJOR)
+               current->maj_flt++;
+       else
+               current->min_flt++;
+       up_read(&mm->mmap_sem);
+       /*
+        * keep track of tlb+htab misses that are good addrs but
+        * just need pte's created via handle_mm_fault()
+        * -- Cort
+        */
+       pte_misses++;
+       return;
+
+bad_area:
+       up_read(&mm->mmap_sem);
+
+bad_area_nosemaphore:
+       pte_errors++;
+
+       /* User mode accesses cause a SIGSEGV */
+       if (user_mode(regs)) {
+               _exception(SIGSEGV, regs, code, address);
+/*             info.si_signo = SIGSEGV;
+               info.si_errno = 0;
+               info.si_code = code;
+               info.si_addr = (void *) address;
+               force_sig_info(SIGSEGV, &info, current);*/
+               return;
+       }
+
+       bad_page_fault(regs, address, SIGSEGV);
+       return;
+
+/*
+ * We ran out of memory, or some other thing happened to us that made
+ * us unable to handle the page fault gracefully.
+ */
+out_of_memory:
+       if (current->pid == 1) {
+               yield();
+               down_read(&mm->mmap_sem);
+               goto survive;
+       }
+       up_read(&mm->mmap_sem);
+       printk(KERN_WARNING "VM: killing process %s\n", current->comm);
+       if (user_mode(regs))
+               do_exit(SIGKILL);
+       bad_page_fault(regs, address, SIGKILL);
+       return;
+
+do_sigbus:
+       up_read(&mm->mmap_sem);
+       if (user_mode(regs)) {
+               info.si_signo = SIGBUS;
+               info.si_errno = 0;
+               info.si_code = BUS_ADRERR;
+               info.si_addr = (void __user *)address;
+               force_sig_info(SIGBUS, &info, current);
+               return;
+       }
+       bad_page_fault(regs, address, SIGBUS);
+}
index b0c8213cd6cf3c4322aa261e54f7c29e7b704766..b5a701cd71e08d86ceec067f011c98b4abe0f419 100644 (file)
 #include <asm/sections.h>
 #include <asm/tlb.h>
 
+#ifndef CONFIG_MMU
 unsigned int __page_offset;
-/* EXPORT_SYMBOL(__page_offset); */
+EXPORT_SYMBOL(__page_offset);
+
+#else
+DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
+
+int mem_init_done;
+static int init_bootmem_done;
+#endif /* CONFIG_MMU */
 
 char *klimit = _end;
 
@@ -32,28 +40,26 @@ char *klimit = _end;
  * Initialize the bootmem system and give it all the memory we
  * have available.
  */
-unsigned int memory_start;
-unsigned int memory_end; /* due to mm/nommu.c */
-unsigned int memory_size;
+unsigned long memory_start;
+unsigned long memory_end; /* due to mm/nommu.c */
+unsigned long memory_size;
 
 /*
  * paging_init() sets up the page tables - in fact we've already done this.
  */
 static void __init paging_init(void)
 {
-       int i;
        unsigned long zones_size[MAX_NR_ZONES];
 
+       /* Clean every zones */
+       memset(zones_size, 0, sizeof(zones_size));
+
        /*
         * old: we can DMA to/from any address.put all page into ZONE_DMA
         * We use only ZONE_NORMAL
         */
        zones_size[ZONE_NORMAL] = max_mapnr;
 
-       /* every other zones are empty */
-       for (i = 1; i < MAX_NR_ZONES; i++)
-               zones_size[i] = 0;
-
        free_area_init(zones_size);
 }
 
@@ -61,6 +67,7 @@ void __init setup_memory(void)
 {
        int i;
        unsigned long map_size;
+#ifndef CONFIG_MMU
        u32 kernel_align_start, kernel_align_size;
 
        /* Find main memory where is the kernel */
@@ -93,6 +100,7 @@ void __init setup_memory(void)
                __func__, kernel_align_start, kernel_align_start
                        + kernel_align_size, kernel_align_size);
 
+#endif
        /*
         * Kernel:
         * start: base phys address of kernel - page align
@@ -121,9 +129,13 @@ void __init setup_memory(void)
         * for 4GB of memory, using 4kB pages), plus 1 page
         * (in case the address isn't page-aligned).
         */
+#ifndef CONFIG_MMU
        map_size = init_bootmem_node(NODE_DATA(0), PFN_UP(TOPHYS((u32)_end)),
                                        min_low_pfn, max_low_pfn);
-
+#else
+       map_size = init_bootmem_node(&contig_page_data,
+               PFN_UP(TOPHYS((u32)_end)), min_low_pfn, max_low_pfn);
+#endif
        lmb_reserve(PFN_UP(TOPHYS((u32)_end)) << PAGE_SHIFT, map_size);
 
        /* free bootmem is whole main memory */
@@ -137,6 +149,9 @@ void __init setup_memory(void)
                reserve_bootmem(lmb.reserved.region[i].base,
                        lmb_size_bytes(&lmb.reserved, i) - 1, BOOTMEM_DEFAULT);
        }
+#ifdef CONFIG_MMU
+       init_bootmem_done = 1;
+#endif
        paging_init();
 }
 
@@ -191,11 +206,145 @@ void __init mem_init(void)
        printk(KERN_INFO "Memory: %luk/%luk available\n",
               (unsigned long) nr_free_pages() << (PAGE_SHIFT-10),
               num_physpages << (PAGE_SHIFT-10));
+#ifdef CONFIG_MMU
+       mem_init_done = 1;
+#endif
 }
 
+#ifndef CONFIG_MMU
 /* Check against bounds of physical memory */
 int ___range_ok(unsigned long addr, unsigned long size)
 {
        return ((addr < memory_start) ||
                ((addr + size) > memory_end));
 }
+EXPORT_SYMBOL(___range_ok);
+
+#else
+int page_is_ram(unsigned long pfn)
+{
+       return pfn < max_low_pfn;
+}
+
+/*
+ * Check for command-line options that affect what MMU_init will do.
+ */
+static void mm_cmdline_setup(void)
+{
+       unsigned long maxmem = 0;
+       char *p = cmd_line;
+
+       /* Look for mem= option on command line */
+       p = strstr(cmd_line, "mem=");
+       if (p) {
+               p += 4;
+               maxmem = memparse(p, &p);
+               if (maxmem && memory_size > maxmem) {
+                       memory_size = maxmem;
+                       memory_end = memory_start + memory_size;
+                       lmb.memory.region[0].size = memory_size;
+               }
+       }
+}
+
+/*
+ * MMU_init_hw does the chip-specific initialization of the MMU hardware.
+ */
+static void __init mmu_init_hw(void)
+{
+       /*
+        * The Zone Protection Register (ZPR) defines how protection will
+        * be applied to every page which is a member of a given zone. At
+        * present, we utilize only two of the zones.
+        * The zone index bits (of ZSEL) in the PTE are used for software
+        * indicators, except the LSB.  For user access, zone 1 is used,
+        * for kernel access, zone 0 is used.  We set all but zone 1
+        * to zero, allowing only kernel access as indicated in the PTE.
+        * For zone 1, we set a 01 binary (a value of 10 will not work)
+        * to allow user access as indicated in the PTE.  This also allows
+        * kernel access as indicated in the PTE.
+        */
+       __asm__ __volatile__ ("ori r11, r0, 0x10000000;" \
+                       "mts rzpr, r11;"
+                       : : : "r11");
+}
+
+/*
+ * MMU_init sets up the basic memory mappings for the kernel,
+ * including both RAM and possibly some I/O regions,
+ * and sets up the page tables and the MMU hardware ready to go.
+ */
+
+/* called from head.S */
+asmlinkage void __init mmu_init(void)
+{
+       unsigned int kstart, ksize;
+
+       if (!lmb.reserved.cnt) {
+               printk(KERN_EMERG "Error memory count\n");
+               machine_restart(NULL);
+       }
+
+       if ((u32) lmb.memory.region[0].size < 0x1000000) {
+               printk(KERN_EMERG "Memory must be greater than 16MB\n");
+               machine_restart(NULL);
+       }
+       /* Find main memory where the kernel is */
+       memory_start = (u32) lmb.memory.region[0].base;
+       memory_end = (u32) lmb.memory.region[0].base +
+                               (u32) lmb.memory.region[0].size;
+       memory_size = memory_end - memory_start;
+
+       mm_cmdline_setup(); /* FIXME parse args from command line - not used */
+
+       /*
+        * Map out the kernel text/data/bss from the available physical
+        * memory.
+        */
+       kstart = __pa(CONFIG_KERNEL_START); /* kernel start */
+       /* kernel size */
+       ksize = PAGE_ALIGN(((u32)_end - (u32)CONFIG_KERNEL_START));
+       lmb_reserve(kstart, ksize);
+
+#if defined(CONFIG_BLK_DEV_INITRD)
+       /* Remove the init RAM disk from the available memory. */
+/*     if (initrd_start) {
+               mem_pieces_remove(&phys_avail, __pa(initrd_start),
+                                 initrd_end - initrd_start, 1);
+       }*/
+#endif /* CONFIG_BLK_DEV_INITRD */
+
+       /* Initialize the MMU hardware */
+       mmu_init_hw();
+
+       /* Map in all of RAM starting at CONFIG_KERNEL_START */
+       mapin_ram();
+
+#ifdef HIGHMEM_START_BOOL
+       ioremap_base = HIGHMEM_START;
+#else
+       ioremap_base = 0xfe000000UL;    /* for now, could be 0xfffff000 */
+#endif /* CONFIG_HIGHMEM */
+       ioremap_bot = ioremap_base;
+
+       /* Initialize the context management stuff */
+       mmu_context_init();
+}
+
+/* This is only called until mem_init is done. */
+void __init *early_get_page(void)
+{
+       void *p;
+       if (init_bootmem_done) {
+               p = alloc_bootmem_pages(PAGE_SIZE);
+       } else {
+               /*
+                * Mem start + 32MB -> here is limit
+                * because of mem mapping from head.S
+                */
+               p = __va(lmb_alloc_base(PAGE_SIZE, PAGE_SIZE,
+                                       memory_start + 0x2000000));
+       }
+       return p;
+}
+#endif /* CONFIG_MMU */
diff --git a/arch/microblaze/mm/mmu_context.c b/arch/microblaze/mm/mmu_context.c
new file mode 100644 (file)
index 0000000..26ff82f
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * This file contains the routines for handling the MMU.
+ *
+ *    Copyright (C) 2007 Xilinx, Inc.  All rights reserved.
+ *
+ *  Derived from arch/ppc/mm/4xx_mmu.c:
+ *  -- paulus
+ *
+ *  Derived from arch/ppc/mm/init.c:
+ *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
+ *
+ *  Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
+ *  and Cort Dougan (PReP) (cort@cs.nmt.edu)
+ *    Copyright (C) 1996 Paul Mackerras
+ *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
+ *
+ *  Derived from "arch/i386/mm/init.c"
+ *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/mm.h>
+#include <linux/init.h>
+
+#include <asm/tlbflush.h>
+#include <asm/mmu_context.h>
+
+mm_context_t next_mmu_context;
+unsigned long context_map[LAST_CONTEXT / BITS_PER_LONG + 1];
+atomic_t nr_free_contexts;
+struct mm_struct *context_mm[LAST_CONTEXT+1];
+
+/*
+ * Initialize the context management stuff.
+ */
+void __init mmu_context_init(void)
+{
+       /*
+        * The use of context zero is reserved for the kernel.
+        * This code assumes FIRST_CONTEXT < 32.
+        */
+       context_map[0] = (1 << FIRST_CONTEXT) - 1;
+       next_mmu_context = FIRST_CONTEXT;
+       atomic_set(&nr_free_contexts, LAST_CONTEXT - FIRST_CONTEXT + 1);
+}
+
+/*
+ * Steal a context from a task that has one at the moment.
+ *
+ * This isn't an LRU system, it just frees up each context in
+ * turn (sort-of pseudo-random replacement :).  This would be the
+ * place to implement an LRU scheme if anyone were motivated to do it.
+ */
+void steal_context(void)
+{
+       struct mm_struct *mm;
+
+       /* free up context `next_mmu_context' */
+       /* if we shouldn't free context 0, don't... */
+       if (next_mmu_context < FIRST_CONTEXT)
+               next_mmu_context = FIRST_CONTEXT;
+       mm = context_mm[next_mmu_context];
+       flush_tlb_mm(mm);
+       destroy_context(mm);
+}
diff --git a/arch/microblaze/mm/pgtable.c b/arch/microblaze/mm/pgtable.c
new file mode 100644 (file)
index 0000000..46c4ca5
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ *  This file contains the routines setting up the linux page tables.
+ *
+ * Copyright (C) 2008 Michal Simek
+ * Copyright (C) 2008 PetaLogix
+ *
+ *    Copyright (C) 2007 Xilinx, Inc.  All rights reserved.
+ *
+ *  Derived from arch/ppc/mm/pgtable.c:
+ *    -- paulus
+ *
+ *  Derived from arch/ppc/mm/init.c:
+ *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
+ *
+ *  Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
+ *  and Cort Dougan (PReP) (cort@cs.nmt.edu)
+ *    Copyright (C) 1996 Paul Mackerras
+ *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
+ *
+ *  Derived from "arch/i386/mm/init.c"
+ *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
+ *
+ *  This file is subject to the terms and conditions of the GNU General
+ *  Public License.  See the file COPYING in the main directory of this
+ *  archive for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <linux/io.h>
+#include <asm/mmu.h>
+#include <asm/sections.h>
+
+#define flush_HPTE(X, va, pg)  _tlbie(va)
+
+unsigned long ioremap_base;
+unsigned long ioremap_bot;
+
+/* The maximum lowmem defaults to 768Mb, but this can be configured to
+ * another value.
+ */
+#define MAX_LOW_MEM    CONFIG_LOWMEM_SIZE
+
+#ifndef CONFIG_SMP
+struct pgtable_cache_struct quicklists;
+#endif
+
+static void __iomem *__ioremap(phys_addr_t addr, unsigned long size,
+               unsigned long flags)
+{
+       unsigned long v, i;
+       phys_addr_t p;
+       int err;
+
+       /*
+        * Choose an address to map it to.
+        * Once the vmalloc system is running, we use it.
+        * Before then, we use space going down from ioremap_base
+        * (ioremap_bot records where we're up to).
+        */
+       p = addr & PAGE_MASK;
+       size = PAGE_ALIGN(addr + size) - p;
+
+       /*
+        * Don't allow anybody to remap normal RAM that we're using.
+        * mem_init() sets high_memory so only do the check after that.
+        *
+        * However, allow remap of rootfs: TBD
+        */
+       if (mem_init_done &&
+               p >= memory_start && p < virt_to_phys(high_memory) &&
+               !(p >= virt_to_phys((unsigned long)&__bss_stop) &&
+               p < virt_to_phys((unsigned long)__bss_stop))) {
+               printk(KERN_WARNING "__ioremap(): phys addr "PTE_FMT
+                       " is RAM lr %p\n", (unsigned long)p,
+                       __builtin_return_address(0));
+               return NULL;
+       }
+
+       if (size == 0)
+               return NULL;
+
+       /*
+        * Is it already mapped? If the whole area is mapped then we're
+        * done, otherwise remap it since we want to keep the virt addrs for
+        * each request contiguous.
+        *
+        * We make the assumption here that if the bottom and top
+        * of the range we want are mapped then it's mapped to the
+        * same virt address (and this is contiguous).
+        *  -- Cort
+        */
+
+       if (mem_init_done) {
+               struct vm_struct *area;
+               area = get_vm_area(size, VM_IOREMAP);
+               if (area == NULL)
+                       return NULL;
+               v = VMALLOC_VMADDR(area->addr);
+       } else {
+               v = (ioremap_bot -= size);
+       }
+
+       if ((flags & _PAGE_PRESENT) == 0)
+               flags |= _PAGE_KERNEL;
+       if (flags & _PAGE_NO_CACHE)
+               flags |= _PAGE_GUARDED;
+
+       err = 0;
+       for (i = 0; i < size && err == 0; i += PAGE_SIZE)
+               err = map_page(v + i, p + i, flags);
+       if (err) {
+               if (mem_init_done)
+                       vfree((void *)v);
+               return NULL;
+       }
+
+       return (void __iomem *) (v + ((unsigned long)addr & ~PAGE_MASK));
+}
+
+void __iomem *ioremap(phys_addr_t addr, unsigned long size)
+{
+       return __ioremap(addr, size, _PAGE_NO_CACHE);
+}
+EXPORT_SYMBOL(ioremap);
+
+void iounmap(void *addr)
+{
+       if (addr > high_memory && (unsigned long) addr < ioremap_bot)
+               vfree((void *) (PAGE_MASK & (unsigned long) addr));
+}
+EXPORT_SYMBOL(iounmap);
+
+
+int map_page(unsigned long va, phys_addr_t pa, int flags)
+{
+       pmd_t *pd;
+       pte_t *pg;
+       int err = -ENOMEM;
+       /* spin_lock(&init_mm.page_table_lock); */
+       /* Use upper 10 bits of VA to index the first level map */
+       pd = pmd_offset(pgd_offset_k(va), va);
+       /* Use middle 10 bits of VA to index the second-level map */
+       pg = pte_alloc_kernel(pd, va); /* from powerpc - pgtable.c */
+       /* pg = pte_alloc_kernel(&init_mm, pd, va); */
+
+       if (pg != NULL) {
+               err = 0;
+               set_pte_at(&init_mm, va, pg, pfn_pte(pa >> PAGE_SHIFT,
+                               __pgprot(flags)));
+               if (mem_init_done)
+                       flush_HPTE(0, va, pmd_val(*pd));
+                       /* flush_HPTE(0, va, pg); */
+
+       }
+       /* spin_unlock(&init_mm.page_table_lock); */
+       return err;
+}
+
+void __init adjust_total_lowmem(void)
+{
+/* TBD */
+#if 0
+       unsigned long max_low_mem = MAX_LOW_MEM;
+
+       if (total_lowmem > max_low_mem) {
+               total_lowmem = max_low_mem;
+#ifndef CONFIG_HIGHMEM
+               printk(KERN_INFO "Warning, memory limited to %ld Mb, use "
+                               "CONFIG_HIGHMEM to reach %ld Mb\n",
+                               max_low_mem >> 20, total_memory >> 20);
+               total_memory = total_lowmem;
+#endif /* CONFIG_HIGHMEM */
+       }
+#endif
+}
+
+static void show_tmem(unsigned long tmem)
+{
+       volatile unsigned long a;
+       a = a + tmem;
+}
+
+/*
+ * Map in all of physical memory starting at CONFIG_KERNEL_START.
+ */
+void __init mapin_ram(void)
+{
+       unsigned long v, p, s, f;
+
+       v = CONFIG_KERNEL_START;
+       p = memory_start;
+       show_tmem(memory_size);
+       for (s = 0; s < memory_size; s += PAGE_SIZE) {
+               f = _PAGE_PRESENT | _PAGE_ACCESSED |
+                               _PAGE_SHARED | _PAGE_HWEXEC;
+               if ((char *) v < _stext || (char *) v >= _etext)
+                       f |= _PAGE_WRENABLE;
+               else
+                       /* On the MicroBlaze, no user access
+                          forces R/W kernel access */
+                       f |= _PAGE_USER;
+               map_page(v, p, f);
+               v += PAGE_SIZE;
+               p += PAGE_SIZE;
+       }
+}
+
+/* is x a power of 2? */
+#define is_power_of_2(x)       ((x) != 0 && (((x) & ((x) - 1)) == 0))
+
+/*
+ * Set up a mapping for a block of I/O.
+ * virt, phys, size must all be page-aligned.
+ * This should only be called before ioremap is called.
+ */
+void __init io_block_mapping(unsigned long virt, phys_addr_t phys,
+                            unsigned int size, int flags)
+{
+       int i;
+
+       if (virt > CONFIG_KERNEL_START && virt < ioremap_bot)
+               ioremap_bot = ioremap_base = virt;
+
+       /* Put it in the page tables. */
+       for (i = 0; i < size; i += PAGE_SIZE)
+               map_page(virt + i, phys + i, flags);
+}
+
+/* Scan the real Linux page tables and return a PTE pointer for
+ * a virtual address in a context.
+ * Returns true (1) if PTE was found, zero otherwise.  The pointer to
+ * the PTE pointer is unmodified if PTE is not found.
+ */
+static int get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep)
+{
+       pgd_t   *pgd;
+       pmd_t   *pmd;
+       pte_t   *pte;
+       int     retval = 0;
+
+       pgd = pgd_offset(mm, addr & PAGE_MASK);
+       if (pgd) {
+               pmd = pmd_offset(pgd, addr & PAGE_MASK);
+               if (pmd_present(*pmd)) {
+                       pte = pte_offset_kernel(pmd, addr & PAGE_MASK);
+                       if (pte) {
+                               retval = 1;
+                               *ptep = pte;
+                       }
+               }
+       }
+       return retval;
+}
+
+/* Find physical address for this virtual address.  Normally used by
+ * I/O functions, but anyone can call it.
+ */
+unsigned long iopa(unsigned long addr)
+{
+       unsigned long pa;
+
+       pte_t *pte;
+       struct mm_struct *mm;
+
+       /* Allow mapping of user addresses (within the thread)
+        * for DMA if necessary.
+        */
+       if (addr < TASK_SIZE)
+               mm = current->mm;
+       else
+               mm = &init_mm;
+
+       pa = 0;
+       if (get_pteptr(mm, addr, &pte))
+               pa = (pte_val(*pte) & PAGE_MASK) | (addr & ~PAGE_MASK);
+
+       return pa;
+}
index 1b332e15ab528522c19e4769c7691435e843dce1..eb7f01cfd1acb906e2dd1a4ffd02bbd4893bf9f8 100644 (file)
@@ -793,6 +793,6 @@ static __inline__ int atomic64_add_unless(atomic64_t *v, long a, long u)
 #define smp_mb__before_atomic_inc()    smp_llsc_mb()
 #define smp_mb__after_atomic_inc()     smp_llsc_mb()
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 
 #endif /* _ASM_ATOMIC_H */
diff --git a/arch/mips/include/asm/bitsperlong.h b/arch/mips/include/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..3e4c10a
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __ASM_MIPS_BITSPERLONG_H
+#define __ASM_MIPS_BITSPERLONG_H
+
+#define __BITS_PER_LONG _MIPS_SZLONG
+
+#include <asm-generic/bitsperlong.h>
+
+#endif /* __ASM_MIPS_BITSPERLONG_H */
index 9f946e4ca0574fbdc3ab376554d7ba7497493fce..72c80d2034c2064022ecfc2136ca8f4931e77d08 100644 (file)
@@ -189,6 +189,6 @@ typedef struct { unsigned long pgprot; } pgprot_t;
 #define CAC_ADDR(addr)         ((addr) - UNCAC_BASE + PAGE_OFFSET)
 
 #include <asm-generic/memory_model.h>
-#include <asm-generic/page.h>
+#include <asm-generic/getorder.h>
 
 #endif /* _ASM_PAGE_H */
index bee5153aca483b44e03ece34a54a7a51d6795141..c783f364938ccd82ec1c99bc28bac0dfcbce54ef 100644 (file)
@@ -109,7 +109,7 @@ typedef unsigned long old_sigset_t;         /* at least 32 bits */
 #define SIG_UNBLOCK    2       /* for unblocking signals */
 #define SIG_SETMASK    3       /* for setting the signal mask */
 
-#include <asm-generic/signal.h>
+#include <asm-generic/signal-defs.h>
 
 struct sigaction {
        unsigned int    sa_flags;
diff --git a/arch/mips/include/asm/suspend.h b/arch/mips/include/asm/suspend.h
deleted file mode 100644 (file)
index 2562f8f..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_SUSPEND_H
-#define __ASM_SUSPEND_H
-
-/* Somewhen...  Maybe :-)  */
-
-#endif /* __ASM_SUSPEND_H */
index 7956e69a3bd5cfe6a8b50466f7460996599f9ce5..544a2854598f0d6558269beb298fe839e161ea08 100644 (file)
@@ -31,9 +31,6 @@ typedef unsigned short umode_t;
  * These aren't exported outside the kernel to avoid name space clashes
  */
 #ifdef __KERNEL__
-
-#define BITS_PER_LONG _MIPS_SZLONG
-
 #ifndef __ASSEMBLY__
 
 #if (defined(CONFIG_HIGHMEM) && defined(CONFIG_64BIT_PHYS_ADDR)) \
index 1f60e27523d9e65ad43289462ee269e4c696ba30..3e9100dcc12db963337da41a7b47e6b8d1faed59 100644 (file)
@@ -68,8 +68,6 @@ void *module_alloc(unsigned long size)
 void module_free(struct module *mod, void *module_region)
 {
        vfree(module_region);
-       /* FIXME: If module_region == mod->init_region, trim exception
-           table entries. */
 }
 
 int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
index bc064825f9b1b11380f299c24d98fda795d31c0a..5bf5be9566dece295d8922a8c3c54aa9f5420615 100644 (file)
@@ -151,7 +151,7 @@ static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr)
 #define smp_mb__before_atomic_inc()    barrier()
 #define smp_mb__after_atomic_inc()     barrier()
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 
 #endif /* __KERNEL__ */
 #endif /* _ASM_ATOMIC_H */
diff --git a/arch/mn10300/include/asm/bitsperlong.h b/arch/mn10300/include/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..6dc0bb0
--- /dev/null
@@ -0,0 +1 @@
+#include <asm-generic/bitsperlong.h>
index b7986b65addf81da83bebc77446966373bd155eb..d04fac1da5aa80513f350afc18dd603fc0c0b4dd 100644 (file)
@@ -12,7 +12,7 @@
 #ifndef _ASM_MMAN_H
 #define _ASM_MMAN_H
 
-#include <asm-generic/mman.h>
+#include <asm-generic/mman-common.h>
 
 #define MAP_GROWSDOWN  0x0100          /* stack-like segment */
 #define MAP_DENYWRITE  0x0800          /* ETXTBSY */
index e98817cec5f7833873ffc24f23515f227da5a594..7e891fce2370028acea4a56497aafa74c443c0be 100644 (file)
@@ -115,7 +115,7 @@ typedef unsigned long sigset_t;
 #define MINSIGSTKSZ    2048
 #define SIGSTKSZ       8192
 
-#include <asm-generic/signal.h>
+#include <asm-generic/signal-defs.h>
 
 #ifdef __KERNEL__
 struct old_sigaction {
index 6b287f2e8e843f885c9a856df8527f862e9147a1..4fa0e3648d8ee909ada33bc1a4156dcb25b7b9f1 100644 (file)
@@ -48,8 +48,6 @@ void *module_alloc(unsigned long size)
 void module_free(struct module *mod, void *module_region)
 {
        vfree(module_region);
-       /* FIXME: If module_region == mod->init_region, trim exception
-        * table entries. */
 }
 
 /*
index ada3e5364d8254993f85d9586c51abb1278cb6bc..7eeaff944360335c8548b0f25e1828432ea55f9b 100644 (file)
@@ -338,6 +338,6 @@ static __inline__ int atomic64_add_unless(atomic64_t *v, long a, long u)
 
 #endif /* CONFIG_64BIT */
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 
 #endif /* _ASM_PARISC_ATOMIC_H_ */
diff --git a/arch/parisc/include/asm/bitsperlong.h b/arch/parisc/include/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..75196b4
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __ASM_PARISC_BITSPERLONG_H
+#define __ASM_PARISC_BITSPERLONG_H
+
+/*
+ * using CONFIG_* outside of __KERNEL__ is wrong,
+ * __LP64__ was also removed from headers, so what
+ * is the right approach on parisc?
+ *     -arnd
+ */
+#if (defined(__KERNEL__) && defined(CONFIG_64BIT)) || defined (__LP64__)
+#define __BITS_PER_LONG 64
+#define SHIFT_PER_LONG 6
+#else
+#define __BITS_PER_LONG 32
+#define SHIFT_PER_LONG 5
+#endif
+
+#include <asm-generic/bitsperlong.h>
+
+#endif /* __ASM_PARISC_BITSPERLONG_H */
index 7bc5125d7d4c9f83a1f7bcd6b4bf980d4fdbaea3..a84cc1f925f63ed22a0b18f28debcb760fe4b528 100644 (file)
@@ -159,6 +159,6 @@ extern int npmem_ranges;
                                 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
 
 #include <asm-generic/memory_model.h>
-#include <asm-generic/page.h>
+#include <asm-generic/getorder.h>
 
 #endif /* _PARISC_PAGE_H */
index 7f5a39bfb4ce1eabecfed3e1cc88f27d3cee078f..20135cc8003924dc688d1abd52e064fbbf317fd5 100644 (file)
@@ -14,14 +14,6 @@ typedef unsigned short umode_t;
  */
 #ifdef __KERNEL__
 
-#ifdef CONFIG_64BIT
-#define BITS_PER_LONG 64
-#define SHIFT_PER_LONG 6
-#else
-#define BITS_PER_LONG 32
-#define SHIFT_PER_LONG 5
-#endif
-
 #ifndef __ASSEMBLY__
 
 /* Dma addresses are 32-bits wide.  */
index cd4c0b2a8e70bf228a27a47a376afa1aa2ff8d9b..7cf799d70b4c1b5da344895ddc6c055fe8d26686 100644 (file)
@@ -7,7 +7,7 @@
 #include <asm/page.h>
 #include <asm/system.h>
 #include <asm/cache.h>
-#include <asm-generic/uaccess.h>
+#include <asm-generic/uaccess-unaligned.h>
 
 #define VERIFY_READ 0
 #define VERIFY_WRITE 1
index ecd1c50244470db01620f67615e5562b87d2bcec..ef5caf2e6ed0820944c9ea891dd33c49c2a5ceda 100644 (file)
@@ -267,8 +267,6 @@ void module_free(struct module *mod, void *module_region)
        mod->arch.section = NULL;
 
        vfree(module_region);
-       /* FIXME: If module_region == mod->init_region, trim exception
-           table entries. */
 }
 
 /* Additional bytes needed in front of individual sections */
index b401950f5259e6a16a7d38a1fa3c0c509d6183ee..b7d2d07b6f965f5a613c92b12ebaf23fd938c141 100644 (file)
@@ -472,6 +472,6 @@ static __inline__ int atomic64_add_unless(atomic64_t *v, long a, long u)
 
 #endif /* __powerpc64__ */
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 #endif /* __KERNEL__ */
 #endif /* _ASM_POWERPC_ATOMIC_H_ */
diff --git a/arch/powerpc/include/asm/bitsperlong.h b/arch/powerpc/include/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..5f16590
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __ASM_POWERPC_BITSPERLONG_H
+#define __ASM_POWERPC_BITSPERLONG_H
+
+#if defined(__powerpc64__)
+# define __BITS_PER_LONG 64
+#else
+# define __BITS_PER_LONG 32
+#endif
+
+#include <asm-generic/bitsperlong.h>
+
+#endif /* __ASM_POWERPC_BITSPERLONG_H */
index e7b99bac9f4875312655e1f3b44799b6734f6a16..7b1c49811a2439cb195aa3d311da2e2493b612f9 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef _ASM_POWERPC_MMAN_H
 #define _ASM_POWERPC_MMAN_H
 
-#include <asm-generic/mman.h>
+#include <asm-generic/mman-common.h>
 
 /*
  * This program is free software; you can redistribute it and/or
index a218da6bec7c5ea69091196d363b9cb7c51158b0..fb841205745055151c43100d2e15ba33b80f9eed 100644 (file)
 #define MPC52xx_PSC_MAXNUM     6
 
 /* Programmable Serial Controller (PSC) status register bits */
+#define MPC52xx_PSC_SR_UNEX_RX 0x0001
+#define MPC52xx_PSC_SR_DATA_VAL        0x0002
+#define MPC52xx_PSC_SR_DATA_OVR        0x0004
+#define MPC52xx_PSC_SR_CMDSEND 0x0008
 #define MPC52xx_PSC_SR_CDE     0x0080
 #define MPC52xx_PSC_SR_RXRDY   0x0100
 #define MPC52xx_PSC_SR_RXFULL  0x0200
 #define MPC52xx_PSC_RXTX_FIFO_EMPTY    0x0001
 
 /* PSC interrupt status/mask bits */
+#define MPC52xx_PSC_IMR_UNEX_RX_SLOT 0x0001
+#define MPC52xx_PSC_IMR_DATA_VALID     0x0002
+#define MPC52xx_PSC_IMR_DATA_OVR       0x0004
+#define MPC52xx_PSC_IMR_CMD_SEND       0x0008
+#define MPC52xx_PSC_IMR_ERROR          0x0040
+#define MPC52xx_PSC_IMR_DEOF           0x0080
 #define MPC52xx_PSC_IMR_TXRDY          0x0100
 #define MPC52xx_PSC_IMR_RXRDY          0x0200
 #define MPC52xx_PSC_IMR_DB             0x0400
 #define MPC52xx_PSC_SICR_SIM_FIR               (0x6 << 24)
 #define MPC52xx_PSC_SICR_SIM_CODEC_24          (0x7 << 24)
 #define MPC52xx_PSC_SICR_SIM_CODEC_32          (0xf << 24)
+#define MPC52xx_PSC_SICR_AWR                   (1 << 30)
 #define MPC52xx_PSC_SICR_GENCLK                        (1 << 23)
 #define MPC52xx_PSC_SICR_I2S                   (1 << 22)
 #define MPC52xx_PSC_SICR_CLKPOL                        (1 << 21)
index a0e3f6e6b4eeadc2140080843dc35ebeda6c241f..bd0849dbcaaad4c6f87347cf2092a1079581b61e 100644 (file)
@@ -41,7 +41,7 @@ extern void clear_pages(void *page, int order);
 static inline void clear_page(void *page) { clear_pages(page, 0); }
 extern void copy_page(void *to, void *from);
 
-#include <asm-generic/page.h>
+#include <asm-generic/getorder.h>
 
 #define PGD_T_LOG2     (__builtin_ffs(sizeof(pgd_t)) - 1)
 #define PTE_T_LOG2     (__builtin_ffs(sizeof(pte_t)) - 1)
index 043bfdfe4f7379ed90edc7bf5119e25950e159a8..5817a3b747e53c12b516fd8229381b40486cd0e2 100644 (file)
@@ -180,6 +180,6 @@ do {                                                \
        (test_thread_flag(TIF_32BIT) ? \
         VM_STACK_DEFAULT_FLAGS32 : VM_STACK_DEFAULT_FLAGS64)
 
-#include <asm-generic/page.h>
+#include <asm-generic/getorder.h>
 
 #endif /* _ASM_POWERPC_PAGE_64_H */
index 69f709d8e8e750f781c3b7f2a507dd4043ef49d5..3eb13be11d8f6eff81fa251b8ab1c589ed35edce 100644 (file)
@@ -94,7 +94,7 @@ typedef struct {
 #define MINSIGSTKSZ    2048
 #define SIGSTKSZ       8192
 
-#include <asm-generic/signal.h>
+#include <asm-generic/signal-defs.h>
 
 struct old_sigaction {
        __sighandler_t sa_handler;
index 2c14fea07c8aa92ae9bc320553103dac8d255e67..a24f48704a3405ca468473aafacd961765d5d48d 100644 (file)
@@ -78,7 +78,7 @@ struct termio {
 
 #ifdef __KERNEL__
 
-#include <asm-generic/termios.h>
+#include <asm-generic/termios-base.h>
 
 #endif /* __KERNEL__ */
 
index 7ce27a52bb3450d56ba038000febbb9e555e4572..a5aea0ca34e9a3dc98fdf4b504cc56e472baad87 100644 (file)
@@ -40,15 +40,6 @@ typedef struct {
 #endif /* __ASSEMBLY__ */
 
 #ifdef __KERNEL__
-/*
- * These aren't exported outside the kernel to avoid name space clashes
- */
-#ifdef __powerpc64__
-#define BITS_PER_LONG 64
-#else
-#define BITS_PER_LONG 32
-#endif
-
 #ifndef __ASSEMBLY__
 
 typedef __vector128 vector128;
index 43e7e3a7f130220bdd56122c1560d42484a6db80..477c663e014043a5c08fbaf51e82853005391344 100644 (file)
@@ -43,8 +43,6 @@ void *module_alloc(unsigned long size)
 void module_free(struct module *mod, void *module_region)
 {
        vfree(module_region);
-       /* FIXME: If module_region == mod->init_region, trim exception
-           table entries. */
 }
 
 static const Elf_Shdr *find_section(const Elf_Ehdr *hdr,
index b3f7d1216bae8b8299735a8e3b03f267ddafcd5d..b72e7a19d05464982a9864d9f8d011bc335ad6dc 100644 (file)
@@ -294,12 +294,12 @@ static void power7_disable_pmc(unsigned int pmc, u64 mmcr[])
 }
 
 static int power7_generic_events[] = {
-       [PERF_COUNT_CPU_CYCLES] = 0x1e,
-       [PERF_COUNT_INSTRUCTIONS] = 2,
-       [PERF_COUNT_CACHE_REFERENCES] = 0xc880,         /* LD_REF_L1_LSU */
-       [PERF_COUNT_CACHE_MISSES] = 0x400f0,            /* LD_MISS_L1 */
-       [PERF_COUNT_BRANCH_INSTRUCTIONS] = 0x10068,     /* BRU_FIN */
-       [PERF_COUNT_BRANCH_MISSES] = 0x400f6,           /* BR_MPRED */
+       [PERF_COUNT_HW_CPU_CYCLES] = 0x1e,
+       [PERF_COUNT_HW_INSTRUCTIONS] = 2,
+       [PERF_COUNT_HW_CACHE_REFERENCES] = 0xc880,      /* LD_REF_L1_LSU*/
+       [PERF_COUNT_HW_CACHE_MISSES] = 0x400f0,         /* LD_MISS_L1   */
+       [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x10068,  /* BRU_FIN      */
+       [PERF_COUNT_HW_BRANCH_MISSES] = 0x400f6,        /* BR_MPRED     */
 };
 
 #define C(x)   PERF_COUNT_HW_CACHE_##x
index de432f2de2d22ed3f9f2362023a52caacea1bcb2..fca9dffcc669b186f779785ae8d1ac3a9858c356 100644 (file)
@@ -275,6 +275,6 @@ static __inline__ int atomic64_add_unless(atomic64_t *v,
 #define smp_mb__before_atomic_inc()    smp_mb()
 #define smp_mb__after_atomic_inc()     smp_mb()
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 #endif /* __KERNEL__ */
 #endif /* __ARCH_S390_ATOMIC__  */
diff --git a/arch/s390/include/asm/bitsperlong.h b/arch/s390/include/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..6b235ae
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __ASM_S390_BITSPERLONG_H
+#define __ASM_S390_BITSPERLONG_H
+
+#ifndef __s390x__
+#define __BITS_PER_LONG 32
+#else
+#define __BITS_PER_LONG 64
+#endif
+
+#include <asm-generic/bitsperlong.h>
+
+#endif /* __ASM_S390_BITSPERLONG_H */
+
index da01432e8f447b057e0989980dbdfd4efb15cd04..f63fe7b431ed4ee8a967305cb129b6b0ad8b0314 100644 (file)
@@ -9,7 +9,7 @@
 #ifndef __S390_MMAN_H__
 #define __S390_MMAN_H__
 
-#include <asm-generic/mman.h>
+#include <asm-generic/mman-common.h>
 
 #define MAP_GROWSDOWN  0x0100          /* stack-like segment */
 #define MAP_DENYWRITE  0x0800          /* ETXTBSY */
index 32e8f6aa4384245892a6523943d7819f0c808cb6..3e3594d01f8315f6c5545fd40dbe0d1d521f3de2 100644 (file)
@@ -150,7 +150,7 @@ void arch_alloc_page(struct page *page, int order);
                                 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
 
 #include <asm-generic/memory_model.h>
-#include <asm-generic/page.h>
+#include <asm-generic/getorder.h>
 
 #define __HAVE_ARCH_GATE_AREA 1
 
index f6cfddb278cb95bafc4189a50216915530a74360..cdf5cb2fe03f5aa0c2c45765a54536b78d88dde5 100644 (file)
@@ -115,7 +115,7 @@ typedef unsigned long sigset_t;
 #define MINSIGSTKSZ     2048
 #define SIGSTKSZ        8192
 
-#include <asm-generic/signal.h>
+#include <asm-generic/signal-defs.h>
 
 #ifdef __KERNEL__
 struct old_sigaction {
diff --git a/arch/s390/include/asm/suspend.h b/arch/s390/include/asm/suspend.h
deleted file mode 100644 (file)
index 1f34580..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#ifndef __ASM_S390_SUSPEND_H
-#define __ASM_S390_SUSPEND_H
-
-#endif
-
index 67f66278f533f8706d5467560530a164c63121cf..bc3a35cefc9629e4ebae31ee601d6a2432b14c5c 100644 (file)
@@ -60,7 +60,7 @@ struct termio {
 #define user_termios_to_kernel_termios(k, u) copy_from_user(k, u, sizeof(struct termios2))
 #define kernel_termios_to_user_termios(u, k) copy_to_user(u, k, sizeof(struct termios2))
 
-#include <asm-generic/termios.h>
+#include <asm-generic/termios-base.h>
 
 #endif /* __KERNEL__ */
 
index 3dc3fc228812a3ce22070d8b52705c211b23ea47..04d6b95a89c6b4ce1c5454341d378ea45700e286 100644 (file)
@@ -28,12 +28,6 @@ typedef __signed__ long saddr_t;
  */
 #ifdef __KERNEL__
 
-#ifndef __s390x__
-#define BITS_PER_LONG 32
-#else
-#define BITS_PER_LONG 64
-#endif
-
 #ifndef __ASSEMBLY__
 
 typedef u64 dma64_addr_t;
index eed4a00cb676238f7fdbc3b1ccc9dbde7ecb0b98..ab2e3ed28abc89256f7b305ee7ac63dfacfd34e4 100644 (file)
@@ -56,8 +56,6 @@ void *module_alloc(unsigned long size)
 void module_free(struct module *mod, void *module_region)
 {
        vfree(module_region);
-       /* FIXME: If module_region == mod->init_region, trim exception
-           table entries. */
 }
 
 static void
index 978b58efb1e92e18464010bfeeeae5a5296de3c0..157c320272cbb256b8e13915534ba80bd66227ac 100644 (file)
@@ -84,5 +84,5 @@ static inline int atomic_add_unless(atomic_t *v, int a, int u)
 #define smp_mb__before_atomic_inc()    barrier()
 #define smp_mb__after_atomic_inc()     barrier()
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 #endif /* __ASM_SH_ATOMIC_H */
diff --git a/arch/sh/include/asm/bitsperlong.h b/arch/sh/include/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..6dc0bb0
--- /dev/null
@@ -0,0 +1 @@
+#include <asm-generic/bitsperlong.h>
index 156eb0225cf64d0a49cd4e7ec14e1602467cabb7..7d8b72c91a5f45d25db9dca3d5d88c6cd5454836 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef __ASM_SH_MMAN_H
 #define __ASM_SH_MMAN_H
 
-#include <asm-generic/mman.h>
+#include <asm-generic/mman-common.h>
 
 #define MAP_GROWSDOWN  0x0100          /* stack-like segment */
 #define MAP_DENYWRITE  0x0800          /* ETXTBSY */
index 9c6d21ec0240981f3bc4da574162caebad1feac6..49592c780a6e8d604df09d9ed45edb9728bcbdfc 100644 (file)
@@ -163,7 +163,7 @@ typedef struct page *pgtable_t;
                                 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
 
 #include <asm-generic/memory_model.h>
-#include <asm-generic/page.h>
+#include <asm-generic/getorder.h>
 
 /* vDSO support */
 #ifdef CONFIG_VSYSCALL
index 5c5c1e852089f2b4e81f8f07f96a660dbb81afe6..9cc5f014468905a3bef54d80a399391aa2e98c8f 100644 (file)
@@ -106,7 +106,7 @@ typedef unsigned long sigset_t;
 #define MINSIGSTKSZ    2048
 #define SIGSTKSZ       8192
 
-#include <asm-generic/signal.h>
+#include <asm-generic/signal-defs.h>
 
 #ifdef __KERNEL__
 struct old_sigaction {
index c19b0f7d2cc13a100aaeecd24b420eeaff30c32f..c2efdcde266f7e91f196aaa3a1aa942e86c558f6 100644 (file)
@@ -46,8 +46,6 @@ void *module_alloc(unsigned long size)
 void module_free(struct module *mod, void *module_region)
 {
        vfree(module_region);
-       /* FIXME: If module_region == mod->init_region, trim exception
-           table entries. */
 }
 
 /* We don't need anything special. */
index bb91b1248cd1dca941846f80d132206dd4ad6f39..f0d343c3b956580265210d4fb6a059f2bde2ddd4 100644 (file)
@@ -161,5 +161,5 @@ static inline int __atomic24_sub(int i, atomic24_t *v)
 
 #endif /* !(__KERNEL__) */
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 #endif /* !(__ARCH_SPARC_ATOMIC__) */
index a0a70649269693e199973b190939e48937fc8157..f2e48009989e1e57f552066d8ae9890264590d5c 100644 (file)
@@ -114,5 +114,5 @@ static inline int atomic64_add_unless(atomic64_t *v, long a, long u)
 #define smp_mb__before_atomic_inc()    barrier()
 #define smp_mb__after_atomic_inc()     barrier()
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 #endif /* !(__ARCH_SPARC64_ATOMIC__) */
diff --git a/arch/sparc/include/asm/bitsperlong.h b/arch/sparc/include/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..40dcaa3
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __ASM_ALPHA_BITSPERLONG_H
+#define __ASM_ALPHA_BITSPERLONG_H
+
+#if defined(__sparc__) && defined(__arch64__)
+#define __BITS_PER_LONG 64
+#else
+#define __BITS_PER_LONG 32
+#endif
+
+#include <asm-generic/bitsperlong.h>
+
+#endif /* __ASM_ALPHA_BITSPERLONG_H */
+
index fdfbbf0a4736ef454287ce2d6c6a9b2da6801fc5..988192e8e95657d366e558a97954dda6cfba027f 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef __SPARC_MMAN_H__
 #define __SPARC_MMAN_H__
 
-#include <asm-generic/mman.h>
+#include <asm-generic/mman-common.h>
 
 /* SunOS'ified... */
 
index d1806edc0958b7f81c2b9d38d06a2b9ab71b2c3a..f72080bdda947ec81edce5206842d836755f4521 100644 (file)
@@ -152,6 +152,6 @@ extern unsigned long pfn_base;
                                 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
 
 #include <asm-generic/memory_model.h>
-#include <asm-generic/page.h>
+#include <asm-generic/getorder.h>
 
 #endif /* _SPARC_PAGE_H */
index 4274ed13ddb2d6209fdc914db7d51f939ad4b06c..f0d09b40103626f7f02d571170935c651f64c470 100644 (file)
@@ -132,6 +132,6 @@ typedef struct page *pgtable_t;
 #define VM_DATA_DEFAULT_FLAGS  (VM_READ | VM_WRITE | VM_EXEC | \
                                 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
 
-#include <asm-generic/page.h>
+#include <asm-generic/getorder.h>
 
 #endif /* _SPARC64_PAGE_H */
index cba45206b7f2e6eb975da848a4bbcbe4f190eb4e..e49b828a2471d57cca8e5aa723acc3a011a9d390 100644 (file)
@@ -176,7 +176,7 @@ struct sigstack {
 #define SA_STATIC_ALLOC         0x8000
 #endif
 
-#include <asm-generic/signal.h>
+#include <asm-generic/signal-defs.h>
 
 struct __new_sigaction {
        __sighandler_t          sa_handler;
index 2237118825d029d260c18b8f46a735c5294899f8..de671d73baed5703b08329378990ca3f2fb9a10b 100644 (file)
@@ -21,8 +21,6 @@ typedef unsigned short umode_t;
 
 #ifdef __KERNEL__
 
-#define BITS_PER_LONG 64
-
 #ifndef __ASSEMBLY__
 
 /* Dma addresses come in generic and 64-bit flavours.  */
@@ -46,8 +44,6 @@ typedef unsigned short umode_t;
 
 #ifdef __KERNEL__
 
-#define BITS_PER_LONG 32
-
 #ifndef __ASSEMBLY__
 
 typedef u32 dma_addr_t;
index 47d5619d43fafbdd15a0798b4357aac3f30cbce3..8303ac48103426542f6681d826c995f8b8375385 100644 (file)
@@ -17,6 +17,9 @@
 
 #ifndef __ASSEMBLY__
 
+#define ARCH_HAS_SORT_EXTABLE
+#define ARCH_HAS_SEARCH_EXTABLE
+
 /* Sparc is not segmented, however we need to be able to fool access_ok()
  * when doing system calls from kernel mode legitimately.
  *
index c64e767a3e4b3821fd906a3132db4cdc579d8f30..a38c03238918ce3659c66e34804398a6a8345786 100644 (file)
@@ -12,7 +12,7 @@
 #include <asm/asi.h>
 #include <asm/system.h>
 #include <asm/spitfire.h>
-#include <asm-generic/uaccess.h>
+#include <asm-generic/uaccess-unaligned.h>
 #endif
 
 #ifndef __ASSEMBLY__
index 90273765e81f95b7d07250fe35dc29cc5d122ecc..0ee642f63234a19ce1d32b5d9feab15e9309cc5a 100644 (file)
@@ -75,8 +75,6 @@ void *module_alloc(unsigned long size)
 void module_free(struct module *mod, void *module_region)
 {
        vfree(module_region);
-       /* FIXME: If module_region == mod->init_region, trim exception
-           table entries. */
 }
 
 /* Make generic code ignore STT_REGISTER dummy undefined symbols.  */
index 16cc28935e398ede97a7e86fff652656bd042d21..a61c349448e1e12b28621b3590553593f2d3b799 100644 (file)
@@ -28,6 +28,10 @@ search_extable(const struct exception_table_entry *start,
         *      word 3: last insn address + 4 bytes
         *      word 4: fixup code address
         *
+        * Deleted entries are encoded as:
+        *      word 1: unused
+        *      word 2: -1
+        *
         * See asm/uaccess.h for more details.
         */
 
@@ -39,6 +43,10 @@ search_extable(const struct exception_table_entry *start,
                        continue;
                }
 
+               /* A deleted entry; see trim_init_extable */
+               if (walk->fixup == -1)
+                       continue;
+
                if (walk->insn == value)
                        return walk;
        }
@@ -57,6 +65,27 @@ search_extable(const struct exception_table_entry *start,
         return NULL;
 }
 
+#ifdef CONFIG_MODULES
+/* We could memmove them around; easier to mark the trimmed ones. */
+void trim_init_extable(struct module *m)
+{
+       unsigned int i;
+       bool range;
+
+       for (i = 0; i < m->num_exentries; i += range ? 2 : 1) {
+               range = m->extable[i].fixup == 0;
+
+               if (within_module_init(m->extable[i].insn, m)) {
+                       m->extable[i].fixup = -1;
+                       if (range)
+                               m->extable[i+1].fixup = -1;
+               }
+               if (range)
+                       i++;
+       }
+}
+#endif /* CONFIG_MODULES */
+
 /* Special extable search, which handles ranges.  Returns fixup */
 unsigned long search_extables_range(unsigned long addr, unsigned long *g2)
 {
index 55f28a0bae6d3dd99ee7e23eb01ccb546ba446ef..4cc9b6cf480a299dc201791a189babd4362177d4 100644 (file)
@@ -116,7 +116,7 @@ extern unsigned long uml_physmem;
 #define virt_addr_valid(v) pfn_valid(phys_to_pfn(__pa(v)))
 
 #include <asm-generic/memory_model.h>
-#include <asm-generic/page.h>
+#include <asm-generic/getorder.h>
 
 #endif /* __ASSEMBLY__ */
 #endif /* __UM_PAGE_H */
index 58da2480a7f4229fb164ae81947bfc901de8b27d..9ce3f165111a63607fa91f98a99e3b232fb5080a 100644 (file)
@@ -53,16 +53,21 @@ extern unsigned long end_iomem;
 #else
 # define VMALLOC_END   (FIXADDR_START-2*PAGE_SIZE)
 #endif
+#define MODULES_VADDR  VMALLOC_START
+#define MODULES_END    VMALLOC_END
+#define MODULES_LEN    (MODULES_VADDR - MODULES_END)
 
 #define _PAGE_TABLE    (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY)
 #define _KERNPG_TABLE  (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY)
 #define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
-
+#define __PAGE_KERNEL_EXEC                                              \
+        (_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED)
 #define PAGE_NONE      __pgprot(_PAGE_PROTNONE | _PAGE_ACCESSED)
 #define PAGE_SHARED    __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED)
 #define PAGE_COPY      __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
 #define PAGE_READONLY  __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
 #define PAGE_KERNEL    __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED)
+#define PAGE_KERNEL_EXEC       __pgprot(__PAGE_KERNEL_EXEC)
 
 /*
  * The i386 can't do page protection for execute, and considers that the same
diff --git a/arch/um/include/asm/suspend.h b/arch/um/include/asm/suspend.h
deleted file mode 100644 (file)
index f4e8e00..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#ifndef __UM_SUSPEND_H
-#define __UM_SUSPEND_H
-
-#endif
index 598b5c1903af11f10c5c171402b6d283ba9e7565..1b549bca46454c2098974396af7db246dc27a677 100644 (file)
@@ -8,7 +8,7 @@ obj-y = bug.o bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \
 
 subarch-obj-y = lib/semaphore_32.o lib/string_32.o
 subarch-obj-$(CONFIG_HIGHMEM) += mm/highmem_32.o
-subarch-obj-$(CONFIG_MODULES) += kernel/module_32.o
+subarch-obj-$(CONFIG_MODULES) += kernel/module.o
 
 USER_OBJS := bugs.o ptrace_user.o fault.o
 
index c8b4cce9cfe1ce333dd877c8644f5b7d4fab3558..2201e9c20e4a85ec4673939f27e15a3ef3431f94 100644 (file)
@@ -8,10 +8,8 @@ obj-y = bug.o bugs.o delay.o fault.o ldt.o mem.o ptrace.o ptrace_user.o \
        setjmp.o signal.o stub.o stub_segv.o syscalls.o syscall_table.o \
        sysrq.o ksyms.o tls.o
 
-obj-$(CONFIG_MODULES) += um_module.o
-
 subarch-obj-y = lib/csum-partial_64.o lib/memcpy_64.o lib/thunk_64.o
-subarch-obj-$(CONFIG_MODULES) += kernel/module_64.o
+subarch-obj-$(CONFIG_MODULES) += kernel/module.o
 
 ldt-y = ../sys-i386/ldt.o
 
diff --git a/arch/um/sys-x86_64/um_module.c b/arch/um/sys-x86_64/um_module.c
deleted file mode 100644 (file)
index 3dead39..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#include <linux/vmalloc.h>
-#include <linux/moduleloader.h>
-
-/* Copied from i386 arch/i386/kernel/module.c */
-void *module_alloc(unsigned long size)
-{
-       if (size == 0)
-               return NULL;
-       return vmalloc_exec(size);
-}
-
-/* Free memory returned from module_alloc */
-void module_free(struct module *mod, void *module_region)
-{
-       vfree(module_region);
-       /*
-        * FIXME: If module_region == mod->init_region, trim exception
-        * table entries.
-        */
-}
-
index aff9f1fcdcd7e2be9d86049fc7f9522bafd4b35d..8cb9c814e1203a0b7434018392e2ee01ef3ca199 100644 (file)
@@ -483,5 +483,5 @@ atomic64_add_negative(unsigned long long delta, atomic64_t *ptr)
        return old_val < 0;
 }
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 #endif /* _ASM_X86_ATOMIC_32_H */
index 8c21731984daacec35c6df87071b3b94b758c4ca..0d6360220007b2bc3a35952a7fa4830c837ad4af 100644 (file)
@@ -455,5 +455,5 @@ static inline void atomic_or_long(unsigned long *v1, unsigned long v2)
 #define smp_mb__before_atomic_inc()    barrier()
 #define smp_mb__after_atomic_inc()     barrier()
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 #endif /* _ASM_X86_ATOMIC_64_H */
diff --git a/arch/x86/include/asm/bitsperlong.h b/arch/x86/include/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..b0ae1c4
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __ASM_X86_BITSPERLONG_H
+#define __ASM_X86_BITSPERLONG_H
+
+#ifdef __x86_64__
+# define __BITS_PER_LONG 64
+#else
+# define __BITS_PER_LONG 32
+#endif
+
+#include <asm-generic/bitsperlong.h>
+
+#endif /* __ASM_X86_BITSPERLONG_H */
+
index 1caf57628b9c8c750dccbf2e8d60c29a250b39c9..313389cd50d2a3dfe1285152b2d43645e40f7636 100644 (file)
 /* Pages for switcher itself, then two pages per cpu */
 #define TOTAL_SWITCHER_PAGES (SHARED_SWITCHER_PAGES + 2 * nr_cpu_ids)
 
-/* We map at -4M for ease of mapping into the guest (one PTE page). */
+/* We map at -4M (-2M when PAE is activated) for ease of mapping
+ * into the guest (one PTE page). */
+#ifdef CONFIG_X86_PAE
+#define SWITCHER_ADDR 0xFFE00000
+#else
 #define SWITCHER_ADDR 0xFFC00000
+#endif
 
 /* Found in switcher.S */
 extern unsigned long default_idt_entries[];
index faae1996487b7208b6314fd922f5e84a5cf9be4a..d31c4a684078080ebe48fbc4d90ebd7f67dce568 100644 (file)
 #define LHCALL_TS              8
 #define LHCALL_SET_CLOCKEVENT  9
 #define LHCALL_HALT            10
+#define LHCALL_SET_PMD         13
 #define LHCALL_SET_PTE         14
-#define LHCALL_SET_PMD         15
+#define LHCALL_SET_PGD         15
 #define LHCALL_LOAD_TLS                16
 #define LHCALL_NOTIFY          17
 #define LHCALL_LOAD_GDT_ENTRY  18
+#define LHCALL_SEND_INTERRUPTS 19
 
 #define LGUEST_TRAP_ENTRY 0x1F
 
  * operations?  There are two ways: the direct way is to make a "hypercall",
  * to make requests of the Host Itself.
  *
- * We use the KVM hypercall mechanism. Eighteen hypercalls are
+ * We use the KVM hypercall mechanism. Seventeen hypercalls are
  * available: the hypercall number is put in the %eax register, and the
- * arguments (when required) are placed in %ebx, %ecx and %edx.  If a return
- * value makes sense, it's returned in %eax.
+ * arguments (when required) are placed in %ebx, %ecx, %edx and %esi.
+ * If a return value makes sense, it's returned in %eax.
  *
  * Grossly invalid calls result in Sudden Death at the hands of the vengeful
  * Host, rather than returning failure.  This reflects Winston Churchill's
@@ -47,8 +49,9 @@
 
 #define LHCALL_RING_SIZE 64
 struct hcall_args {
-       /* These map directly onto eax, ebx, ecx, edx in struct lguest_regs */
-       unsigned long arg0, arg1, arg2, arg3;
+       /* These map directly onto eax, ebx, ecx, edx and esi
+        * in struct lguest_regs */
+       unsigned long arg0, arg1, arg2, arg3, arg4;
 };
 
 #endif /* !__ASSEMBLY__ */
index 90bc4108a4fdf917fc243502bd7fcf833f482a55..751af2550ed95befecc593c80343f20035f14bb4 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef _ASM_X86_MMAN_H
 #define _ASM_X86_MMAN_H
 
-#include <asm-generic/mman.h>
+#include <asm-generic/mman-common.h>
 
 #define MAP_32BIT      0x40            /* only give out 32bit addresses */
 
index 89ed9d70b0aa6b65158b22b536677429281ea46b..625c3f0e741aab7bd75d839cadf7084c303baa61 100644 (file)
@@ -56,7 +56,7 @@ extern bool __virt_addr_valid(unsigned long kaddr);
 #endif /* __ASSEMBLY__ */
 
 #include <asm-generic/memory_model.h>
-#include <asm-generic/page.h>
+#include <asm-generic/getorder.h>
 
 #define __HAVE_ARCH_GATE_AREA 1
 
index 2733fad45f989bfd5a775c99e5decb35cfe8014a..5e67c15323145753d29894eb43c4c29f0343f38e 100644 (file)
@@ -46,6 +46,10 @@ extern bool __vmalloc_start_set; /* set once high_memory is set */
 # define VMALLOC_END   (FIXADDR_START - 2 * PAGE_SIZE)
 #endif
 
+#define MODULES_VADDR  VMALLOC_START
+#define MODULES_END    VMALLOC_END
+#define MODULES_LEN    (MODULES_VADDR - MODULES_END)
+
 #define MAXMEM (VMALLOC_END - PAGE_OFFSET - __VMALLOC_RESERVE)
 
 #endif /* _ASM_X86_PGTABLE_32_DEFS_H */
index 7761a5d554bb2eb567024375b5bb3c083991946f..598457cbd0f80c47e4cf7759e8d3b6c5de048f06 100644 (file)
@@ -117,7 +117,7 @@ typedef unsigned long sigset_t;
 #define MINSIGSTKSZ    2048
 #define SIGSTKSZ       8192
 
-#include <asm-generic/signal.h>
+#include <asm-generic/signal-defs.h>
 
 #ifndef __ASSEMBLY__
 
index a5ecc9c33e920eda5e50bcfe0313b333b1b2cd31..7f3eba08e7de988dfe85affb84495b59bf0e6a0d 100644 (file)
@@ -172,6 +172,6 @@ static inline void flush_tlb_kernel_range(unsigned long start,
        flush_tlb_all();
 }
 
-extern void zap_low_mappings(void);
+extern void zap_low_mappings(bool early);
 
 #endif /* _ASM_X86_TLBFLUSH_H */
index e6f7363200773e13f94b02ef53a8dab57d974bef..09b97745772f134532d869fd2f9565ec8633b29b 100644 (file)
@@ -14,12 +14,6 @@ typedef unsigned short umode_t;
  */
 #ifdef __KERNEL__
 
-#ifdef CONFIG_X86_32
-# define BITS_PER_LONG 32
-#else
-# define BITS_PER_LONG 64
-#endif
-
 #ifndef __ASSEMBLY__
 
 typedef u64 dma64_addr_t;
index 4f78bd682125067c07fbf9c11a1b1bc1c40a2dd6..f3477bb845660c51e30a5000c5989831529bad17 100644 (file)
@@ -73,7 +73,7 @@ obj-$(CONFIG_KEXEC)           += machine_kexec_$(BITS).o
 obj-$(CONFIG_KEXEC)            += relocate_kernel_$(BITS).o crash.o
 obj-$(CONFIG_CRASH_DUMP)       += crash_dump_$(BITS).o
 obj-$(CONFIG_KPROBES)          += kprobes.o
-obj-$(CONFIG_MODULES)          += module_$(BITS).o
+obj-$(CONFIG_MODULES)          += module.o
 obj-$(CONFIG_EFI)              += efi.o efi_$(BITS).o efi_stub_$(BITS).o
 obj-$(CONFIG_DOUBLEFAULT)      += doublefault_32.o
 obj-$(CONFIG_KGDB)             += kgdb.o
index 7c243a2c5115dfa6653d7a4fec05d6521eb5ef4f..ca93638ba4309b3e90085ee3d49e5ce81568501e 100644 (file)
@@ -104,7 +104,7 @@ int acpi_save_state_mem(void)
        initial_gs = per_cpu_offset(smp_processor_id());
 #endif
        initial_code = (unsigned long)wakeup_long64;
-       saved_magic = 0x123456789abcdef0;
+       saved_magic = 0x123456789abcdef0L;
 #endif /* CONFIG_64BIT */
 
        return 0;
index 49e0939bac4211ba35f32240af5e92ed905ec578..79302e9a33a43322eea70cc2f69f019426b69847 100644 (file)
@@ -1233,9 +1233,9 @@ static int suspend(int vetoable)
        int err;
        struct apm_user *as;
 
-       device_suspend(PMSG_SUSPEND);
+       dpm_suspend_start(PMSG_SUSPEND);
 
-       device_power_down(PMSG_SUSPEND);
+       dpm_suspend_noirq(PMSG_SUSPEND);
 
        local_irq_disable();
        sysdev_suspend(PMSG_SUSPEND);
@@ -1259,9 +1259,9 @@ static int suspend(int vetoable)
        sysdev_resume();
        local_irq_enable();
 
-       device_power_up(PMSG_RESUME);
+       dpm_resume_noirq(PMSG_RESUME);
 
-       device_resume(PMSG_RESUME);
+       dpm_resume_end(PMSG_RESUME);
        queue_event(APM_NORMAL_RESUME, NULL);
        spin_lock(&user_list_lock);
        for (as = user_list; as != NULL; as = as->next) {
@@ -1277,7 +1277,7 @@ static void standby(void)
 {
        int err;
 
-       device_power_down(PMSG_SUSPEND);
+       dpm_suspend_noirq(PMSG_SUSPEND);
 
        local_irq_disable();
        sysdev_suspend(PMSG_SUSPEND);
@@ -1291,7 +1291,7 @@ static void standby(void)
        sysdev_resume();
        local_irq_enable();
 
-       device_power_up(PMSG_RESUME);
+       dpm_resume_noirq(PMSG_RESUME);
 }
 
 static apm_event_t get_event(void)
@@ -1376,7 +1376,7 @@ static void check_events(void)
                        ignore_bounce = 1;
                        if ((event != APM_NORMAL_RESUME)
                            || (ignore_normal_resume == 0)) {
-                               device_resume(PMSG_RESUME);
+                               dpm_resume_end(PMSG_RESUME);
                                queue_event(event, NULL);
                        }
                        ignore_normal_resume = 0;
index 1a830cbd70153b8662eddf16a87a5336c6cb0742..dfdbf640389536489f5ac05b3258361d133d76d8 100644 (file)
@@ -126,6 +126,7 @@ void foo(void)
 #if defined(CONFIG_LGUEST) || defined(CONFIG_LGUEST_GUEST) || defined(CONFIG_LGUEST_MODULE)
        BLANK();
        OFFSET(LGUEST_DATA_irq_enabled, lguest_data, irq_enabled);
+       OFFSET(LGUEST_DATA_irq_pending, lguest_data, irq_pending);
        OFFSET(LGUEST_DATA_pgdir, lguest_data, pgdir);
 
        BLANK();
index 895c82e78455036810b21302cf2bea49a40466a1..275bc142cd5ddab2d530be5819744fbaafd831d9 100644 (file)
@@ -968,6 +968,13 @@ fixed_mode_idx(struct perf_counter *counter, struct hw_perf_counter *hwc)
        if (!x86_pmu.num_counters_fixed)
                return -1;
 
+       /*
+        * Quirk, IA32_FIXED_CTRs do not work on current Atom processors:
+        */
+       if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL &&
+                                       boot_cpu_data.x86_model == 28)
+               return -1;
+
        event = hwc->config & ARCH_PERFMON_EVENT_MASK;
 
        if (unlikely(event == x86_pmu.event_map(PERF_COUNT_HW_INSTRUCTIONS)))
similarity index 74%
rename from arch/x86/kernel/module_64.c
rename to arch/x86/kernel/module.c
index c23880b90b5c7d9f89ede70564ba741afc729ce5..89f386f044e43fa4ed4d29503bfc838900e3841c 100644 (file)
@@ -1,6 +1,5 @@
-/*  Kernel module help for x86-64
+/*  Kernel module help for x86.
     Copyright (C) 2001 Rusty Russell.
-    Copyright (C) 2002,2003 Andi Kleen, 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
 #include <linux/fs.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
 #include <linux/bug.h>
+#include <linux/mm.h>
 
 #include <asm/system.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
 
+#if 0
+#define DEBUGP printk
+#else
 #define DEBUGP(fmt...)
-
-#ifndef CONFIG_UML
-void module_free(struct module *mod, void *module_region)
-{
-       vfree(module_region);
-       /* FIXME: If module_region == mod->init_region, trim exception
-          table entries. */
-}
+#endif
 
 void *module_alloc(unsigned long size)
 {
@@ -54,9 +48,15 @@ void *module_alloc(unsigned long size)
        if (!area)
                return NULL;
 
-       return __vmalloc_area(area, GFP_KERNEL, PAGE_KERNEL_EXEC);
+       return __vmalloc_area(area, GFP_KERNEL | __GFP_HIGHMEM,
+                                       PAGE_KERNEL_EXEC);
+}
+
+/* Free memory returned from module_alloc */
+void module_free(struct module *mod, void *module_region)
+{
+       vfree(module_region);
 }
-#endif
 
 /* We don't need anything special. */
 int module_frob_arch_sections(Elf_Ehdr *hdr,
@@ -67,6 +67,58 @@ int module_frob_arch_sections(Elf_Ehdr *hdr,
        return 0;
 }
 
+#ifdef CONFIG_X86_32
+int apply_relocate(Elf32_Shdr *sechdrs,
+                  const char *strtab,
+                  unsigned int symindex,
+                  unsigned int relsec,
+                  struct module *me)
+{
+       unsigned int i;
+       Elf32_Rel *rel = (void *)sechdrs[relsec].sh_addr;
+       Elf32_Sym *sym;
+       uint32_t *location;
+
+       DEBUGP("Applying relocate section %u to %u\n", relsec,
+              sechdrs[relsec].sh_info);
+       for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
+               /* This is where to make the change */
+               location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
+                       + rel[i].r_offset;
+               /* This is the symbol it is referring to.  Note that all
+                  undefined symbols have been resolved.  */
+               sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
+                       + ELF32_R_SYM(rel[i].r_info);
+
+               switch (ELF32_R_TYPE(rel[i].r_info)) {
+               case R_386_32:
+                       /* We add the value into the location given */
+                       *location += sym->st_value;
+                       break;
+               case R_386_PC32:
+                       /* Add the value, subtract its postition */
+                       *location += sym->st_value - (uint32_t)location;
+                       break;
+               default:
+                       printk(KERN_ERR "module %s: Unknown relocation: %u\n",
+                              me->name, ELF32_R_TYPE(rel[i].r_info));
+                       return -ENOEXEC;
+               }
+       }
+       return 0;
+}
+
+int apply_relocate_add(Elf32_Shdr *sechdrs,
+                      const char *strtab,
+                      unsigned int symindex,
+                      unsigned int relsec,
+                      struct module *me)
+{
+       printk(KERN_ERR "module %s: ADD RELOCATION unsupported\n",
+              me->name);
+       return -ENOEXEC;
+}
+#else /*X86_64*/
 int apply_relocate_add(Elf64_Shdr *sechdrs,
                   const char *strtab,
                   unsigned int symindex,
@@ -147,6 +199,8 @@ int apply_relocate(Elf_Shdr *sechdrs,
        return -ENOSYS;
 }
 
+#endif
+
 int module_finalize(const Elf_Ehdr *hdr,
                    const Elf_Shdr *sechdrs,
                    struct module *me)
diff --git a/arch/x86/kernel/module_32.c b/arch/x86/kernel/module_32.c
deleted file mode 100644 (file)
index 0edd819..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-/*  Kernel module help for i386.
-    Copyright (C) 2001 Rusty Russell.
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the 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/moduleloader.h>
-#include <linux/elf.h>
-#include <linux/vmalloc.h>
-#include <linux/fs.h>
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/bug.h>
-
-#if 0
-#define DEBUGP printk
-#else
-#define DEBUGP(fmt...)
-#endif
-
-void *module_alloc(unsigned long size)
-{
-       if (size == 0)
-               return NULL;
-       return vmalloc_exec(size);
-}
-
-
-/* Free memory returned from module_alloc */
-void module_free(struct module *mod, void *module_region)
-{
-       vfree(module_region);
-       /* FIXME: If module_region == mod->init_region, trim exception
-          table entries. */
-}
-
-/* We don't need anything special. */
-int module_frob_arch_sections(Elf_Ehdr *hdr,
-                             Elf_Shdr *sechdrs,
-                             char *secstrings,
-                             struct module *mod)
-{
-       return 0;
-}
-
-int apply_relocate(Elf32_Shdr *sechdrs,
-                  const char *strtab,
-                  unsigned int symindex,
-                  unsigned int relsec,
-                  struct module *me)
-{
-       unsigned int i;
-       Elf32_Rel *rel = (void *)sechdrs[relsec].sh_addr;
-       Elf32_Sym *sym;
-       uint32_t *location;
-
-       DEBUGP("Applying relocate section %u to %u\n", relsec,
-              sechdrs[relsec].sh_info);
-       for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
-               /* This is where to make the change */
-               location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
-                       + rel[i].r_offset;
-               /* This is the symbol it is referring to.  Note that all
-                  undefined symbols have been resolved.  */
-               sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
-                       + ELF32_R_SYM(rel[i].r_info);
-
-               switch (ELF32_R_TYPE(rel[i].r_info)) {
-               case R_386_32:
-                       /* We add the value into the location given */
-                       *location += sym->st_value;
-                       break;
-               case R_386_PC32:
-                       /* Add the value, subtract its postition */
-                       *location += sym->st_value - (uint32_t)location;
-                       break;
-               default:
-                       printk(KERN_ERR "module %s: Unknown relocation: %u\n",
-                              me->name, ELF32_R_TYPE(rel[i].r_info));
-                       return -ENOEXEC;
-               }
-       }
-       return 0;
-}
-
-int apply_relocate_add(Elf32_Shdr *sechdrs,
-                      const char *strtab,
-                      unsigned int symindex,
-                      unsigned int relsec,
-                      struct module *me)
-{
-       printk(KERN_ERR "module %s: ADD RELOCATION unsupported\n",
-              me->name);
-       return -ENOEXEC;
-}
-
-int module_finalize(const Elf_Ehdr *hdr,
-                   const Elf_Shdr *sechdrs,
-                   struct module *me)
-{
-       const Elf_Shdr *s, *text = NULL, *alt = NULL, *locks = NULL,
-               *para = NULL;
-       char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
-
-       for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) {
-               if (!strcmp(".text", secstrings + s->sh_name))
-                       text = s;
-               if (!strcmp(".altinstructions", secstrings + s->sh_name))
-                       alt = s;
-               if (!strcmp(".smp_locks", secstrings + s->sh_name))
-                       locks = s;
-               if (!strcmp(".parainstructions", secstrings + s->sh_name))
-                       para = s;
-       }
-
-       if (alt) {
-               /* patch .altinstructions */
-               void *aseg = (void *)alt->sh_addr;
-               apply_alternatives(aseg, aseg + alt->sh_size);
-       }
-       if (locks && text) {
-               void *lseg = (void *)locks->sh_addr;
-               void *tseg = (void *)text->sh_addr;
-               alternatives_smp_module_add(me, me->name,
-                                           lseg, lseg + locks->sh_size,
-                                           tseg, tseg + text->sh_size);
-       }
-
-       if (para) {
-               void *pseg = (void *)para->sh_addr;
-               apply_paravirt(pseg, pseg + para->sh_size);
-       }
-
-       return module_bug_finalize(hdr, sechdrs, me);
-}
-
-void module_arch_cleanup(struct module *mod)
-{
-       alternatives_smp_module_del(mod);
-       module_bug_cleanup(mod);
-}
index d1c636bf31a71018d73f7e36616d2c7d533707ea..be5ae80f897fb58f68f353fcd65c9346b3834d39 100644 (file)
@@ -301,15 +301,13 @@ static void __init reserve_brk(void)
 
 #ifdef CONFIG_BLK_DEV_INITRD
 
-#ifdef CONFIG_X86_32
-
 #define MAX_MAP_CHUNK  (NR_FIX_BTMAPS << PAGE_SHIFT)
 static void __init relocate_initrd(void)
 {
 
        u64 ramdisk_image = boot_params.hdr.ramdisk_image;
        u64 ramdisk_size  = boot_params.hdr.ramdisk_size;
-       u64 end_of_lowmem = max_low_pfn << PAGE_SHIFT;
+       u64 end_of_lowmem = max_low_pfn_mapped << PAGE_SHIFT;
        u64 ramdisk_here;
        unsigned long slop, clen, mapaddr;
        char *p, *q;
@@ -365,14 +363,13 @@ static void __init relocate_initrd(void)
                ramdisk_image, ramdisk_image + ramdisk_size - 1,
                ramdisk_here, ramdisk_here + ramdisk_size - 1);
 }
-#endif
 
 static void __init reserve_initrd(void)
 {
        u64 ramdisk_image = boot_params.hdr.ramdisk_image;
        u64 ramdisk_size  = boot_params.hdr.ramdisk_size;
        u64 ramdisk_end   = ramdisk_image + ramdisk_size;
-       u64 end_of_lowmem = max_low_pfn << PAGE_SHIFT;
+       u64 end_of_lowmem = max_low_pfn_mapped << PAGE_SHIFT;
 
        if (!boot_params.hdr.type_of_loader ||
            !ramdisk_image || !ramdisk_size)
@@ -402,14 +399,8 @@ static void __init reserve_initrd(void)
                return;
        }
 
-#ifdef CONFIG_X86_32
        relocate_initrd();
-#else
-       printk(KERN_ERR "initrd extends beyond end of memory "
-              "(0x%08llx > 0x%08llx)\ndisabling initrd\n",
-              ramdisk_end, end_of_lowmem);
-       initrd_start = 0;
-#endif
+
        free_early(ramdisk_image, ramdisk_end);
 }
 #else
index 7c80007ea5f7fb28ce9e61c4569f3c3432981b1d..2fecda69ee646a78402cc6a1d9e3d86a29ca06d6 100644 (file)
@@ -873,7 +873,7 @@ int __cpuinit native_cpu_up(unsigned int cpu)
 
        err = do_boot_cpu(apicid, cpu);
 
-       zap_low_mappings();
+       zap_low_mappings(false);
        low_mappings = 0;
 #else
        err = do_boot_cpu(apicid, cpu);
index 4c85b2e2bb652873da1ee5367549e070b363bf8a..367e878820418789055a6a2da48aa27350ada443 100644 (file)
@@ -108,6 +108,8 @@ SECTIONS
        /* Data */
        . = ALIGN(PAGE_SIZE);
        .data : AT(ADDR(.data) - LOAD_OFFSET) {
+               /* Start of data section */
+               _sdata = .;
                DATA_DATA
                CONSTRUCTORS
 
index 8dab8f7844d3e2ac62371e413ce71a07eb280d51..38718041efc34fbf6d8f120fe90e26b6d37abbaa 100644 (file)
@@ -2,7 +2,6 @@ config LGUEST_GUEST
        bool "Lguest guest support"
        select PARAVIRT
        depends on X86_32
-       depends on !X86_PAE
        select VIRTIO
        select VIRTIO_RING
        select VIRTIO_CONSOLE
index 4e0c265593958fcb70a8588d942a27a7d0ac3da6..7bc65f0f62c4fc51607dd443641d1273cb4ebb03 100644 (file)
@@ -87,7 +87,7 @@ struct lguest_data lguest_data = {
 
 /*G:037 async_hcall() is pretty simple: I'm quite proud of it really.  We have a
  * ring buffer of stored hypercalls which the Host will run though next time we
- * do a normal hypercall.  Each entry in the ring has 4 slots for the hypercall
+ * do a normal hypercall.  Each entry in the ring has 5 slots for the hypercall
  * arguments, and a "hcall_status" word which is 0 if the call is ready to go,
  * and 255 once the Host has finished with it.
  *
@@ -96,7 +96,8 @@ struct lguest_data lguest_data = {
  * effect of causing the Host to run all the stored calls in the ring buffer
  * which empties it for next time! */
 static void async_hcall(unsigned long call, unsigned long arg1,
-                       unsigned long arg2, unsigned long arg3)
+                       unsigned long arg2, unsigned long arg3,
+                       unsigned long arg4)
 {
        /* Note: This code assumes we're uniprocessor. */
        static unsigned int next_call;
@@ -108,12 +109,13 @@ static void async_hcall(unsigned long call, unsigned long arg1,
        local_irq_save(flags);
        if (lguest_data.hcall_status[next_call] != 0xFF) {
                /* Table full, so do normal hcall which will flush table. */
-               kvm_hypercall3(call, arg1, arg2, arg3);
+               kvm_hypercall4(call, arg1, arg2, arg3, arg4);
        } else {
                lguest_data.hcalls[next_call].arg0 = call;
                lguest_data.hcalls[next_call].arg1 = arg1;
                lguest_data.hcalls[next_call].arg2 = arg2;
                lguest_data.hcalls[next_call].arg3 = arg3;
+               lguest_data.hcalls[next_call].arg4 = arg4;
                /* Arguments must all be written before we mark it to go */
                wmb();
                lguest_data.hcall_status[next_call] = 0;
@@ -141,7 +143,7 @@ static void lazy_hcall1(unsigned long call,
        if (paravirt_get_lazy_mode() == PARAVIRT_LAZY_NONE)
                kvm_hypercall1(call, arg1);
        else
-               async_hcall(call, arg1, 0, 0);
+               async_hcall(call, arg1, 0, 0, 0);
 }
 
 static void lazy_hcall2(unsigned long call,
@@ -151,7 +153,7 @@ static void lazy_hcall2(unsigned long call,
        if (paravirt_get_lazy_mode() == PARAVIRT_LAZY_NONE)
                kvm_hypercall2(call, arg1, arg2);
        else
-               async_hcall(call, arg1, arg2, 0);
+               async_hcall(call, arg1, arg2, 0, 0);
 }
 
 static void lazy_hcall3(unsigned long call,
@@ -162,9 +164,23 @@ static void lazy_hcall3(unsigned long call,
        if (paravirt_get_lazy_mode() == PARAVIRT_LAZY_NONE)
                kvm_hypercall3(call, arg1, arg2, arg3);
        else
-               async_hcall(call, arg1, arg2, arg3);
+               async_hcall(call, arg1, arg2, arg3, 0);
 }
 
+#ifdef CONFIG_X86_PAE
+static void lazy_hcall4(unsigned long call,
+                      unsigned long arg1,
+                      unsigned long arg2,
+                      unsigned long arg3,
+                      unsigned long arg4)
+{
+       if (paravirt_get_lazy_mode() == PARAVIRT_LAZY_NONE)
+               kvm_hypercall4(call, arg1, arg2, arg3, arg4);
+       else
+               async_hcall(call, arg1, arg2, arg3, arg4);
+}
+#endif
+
 /* When lazy mode is turned off reset the per-cpu lazy mode variable and then
  * issue the do-nothing hypercall to flush any stored calls. */
 static void lguest_leave_lazy_mmu_mode(void)
@@ -179,7 +195,7 @@ static void lguest_end_context_switch(struct task_struct *next)
        paravirt_end_context_switch(next);
 }
 
-/*G:033
+/*G:032
  * After that diversion we return to our first native-instruction
  * replacements: four functions for interrupt control.
  *
@@ -199,30 +215,28 @@ static unsigned long save_fl(void)
 {
        return lguest_data.irq_enabled;
 }
-PV_CALLEE_SAVE_REGS_THUNK(save_fl);
-
-/* restore_flags() just sets the flags back to the value given. */
-static void restore_fl(unsigned long flags)
-{
-       lguest_data.irq_enabled = flags;
-}
-PV_CALLEE_SAVE_REGS_THUNK(restore_fl);
 
 /* Interrupts go off... */
 static void irq_disable(void)
 {
        lguest_data.irq_enabled = 0;
 }
+
+/* Let's pause a moment.  Remember how I said these are called so often?
+ * Jeremy Fitzhardinge optimized them so hard early in 2009 that he had to
+ * break some rules.  In particular, these functions are assumed to save their
+ * own registers if they need to: normal C functions assume they can trash the
+ * eax register.  To use normal C functions, we use
+ * PV_CALLEE_SAVE_REGS_THUNK(), which pushes %eax onto the stack, calls the
+ * C function, then restores it. */
+PV_CALLEE_SAVE_REGS_THUNK(save_fl);
 PV_CALLEE_SAVE_REGS_THUNK(irq_disable);
+/*:*/
 
-/* Interrupts go on... */
-static void irq_enable(void)
-{
-       lguest_data.irq_enabled = X86_EFLAGS_IF;
-}
-PV_CALLEE_SAVE_REGS_THUNK(irq_enable);
+/* These are in i386_head.S */
+extern void lg_irq_enable(void);
+extern void lg_restore_fl(unsigned long flags);
 
-/*:*/
 /*M:003 Note that we don't check for outstanding interrupts when we re-enable
  * them (or when we unmask an interrupt).  This seems to work for the moment,
  * since interrupts are rare and we'll just get the interrupt on the next timer
@@ -368,8 +382,8 @@ static void lguest_cpuid(unsigned int *ax, unsigned int *bx,
        case 1: /* Basic feature request. */
                /* We only allow kernel to see SSE3, CMPXCHG16B and SSSE3 */
                *cx &= 0x00002201;
-               /* SSE, SSE2, FXSR, MMX, CMOV, CMPXCHG8B, TSC, FPU. */
-               *dx &= 0x07808111;
+               /* SSE, SSE2, FXSR, MMX, CMOV, CMPXCHG8B, TSC, FPU, PAE. */
+               *dx &= 0x07808151;
                /* The Host can do a nice optimization if it knows that the
                 * kernel mappings (addresses above 0xC0000000 or whatever
                 * PAGE_OFFSET is set to) haven't changed.  But Linux calls
@@ -388,6 +402,11 @@ static void lguest_cpuid(unsigned int *ax, unsigned int *bx,
                if (*ax > 0x80000008)
                        *ax = 0x80000008;
                break;
+       case 0x80000001:
+               /* Here we should fix nx cap depending on host. */
+               /* For this version of PAE, we just clear NX bit. */
+               *dx &= ~(1 << 20);
+               break;
        }
 }
 
@@ -521,25 +540,52 @@ static void lguest_write_cr4(unsigned long val)
 static void lguest_pte_update(struct mm_struct *mm, unsigned long addr,
                               pte_t *ptep)
 {
+#ifdef CONFIG_X86_PAE
+       lazy_hcall4(LHCALL_SET_PTE, __pa(mm->pgd), addr,
+                   ptep->pte_low, ptep->pte_high);
+#else
        lazy_hcall3(LHCALL_SET_PTE, __pa(mm->pgd), addr, ptep->pte_low);
+#endif
 }
 
 static void lguest_set_pte_at(struct mm_struct *mm, unsigned long addr,
                              pte_t *ptep, pte_t pteval)
 {
-       *ptep = pteval;
+       native_set_pte(ptep, pteval);
        lguest_pte_update(mm, addr, ptep);
 }
 
-/* The Guest calls this to set a top-level entry.  Again, we set the entry then
- * tell the Host which top-level page we changed, and the index of the entry we
- * changed. */
+/* The Guest calls lguest_set_pud to set a top-level entry and lguest_set_pmd
+ * to set a middle-level entry when PAE is activated.
+ * Again, we set the entry then tell the Host which page we changed,
+ * and the index of the entry we changed. */
+#ifdef CONFIG_X86_PAE
+static void lguest_set_pud(pud_t *pudp, pud_t pudval)
+{
+       native_set_pud(pudp, pudval);
+
+       /* 32 bytes aligned pdpt address and the index. */
+       lazy_hcall2(LHCALL_SET_PGD, __pa(pudp) & 0xFFFFFFE0,
+                  (__pa(pudp) & 0x1F) / sizeof(pud_t));
+}
+
 static void lguest_set_pmd(pmd_t *pmdp, pmd_t pmdval)
 {
-       *pmdp = pmdval;
+       native_set_pmd(pmdp, pmdval);
        lazy_hcall2(LHCALL_SET_PMD, __pa(pmdp) & PAGE_MASK,
-                  (__pa(pmdp) & (PAGE_SIZE - 1)) / 4);
+                  (__pa(pmdp) & (PAGE_SIZE - 1)) / sizeof(pmd_t));
 }
+#else
+
+/* The Guest calls lguest_set_pmd to set a top-level entry when PAE is not
+ * activated. */
+static void lguest_set_pmd(pmd_t *pmdp, pmd_t pmdval)
+{
+       native_set_pmd(pmdp, pmdval);
+       lazy_hcall2(LHCALL_SET_PGD, __pa(pmdp) & PAGE_MASK,
+                  (__pa(pmdp) & (PAGE_SIZE - 1)) / sizeof(pmd_t));
+}
+#endif
 
 /* There are a couple of legacy places where the kernel sets a PTE, but we
  * don't know the top level any more.  This is useless for us, since we don't
@@ -552,11 +598,31 @@ static void lguest_set_pmd(pmd_t *pmdp, pmd_t pmdval)
  * which brings boot back to 0.25 seconds. */
 static void lguest_set_pte(pte_t *ptep, pte_t pteval)
 {
-       *ptep = pteval;
+       native_set_pte(ptep, pteval);
+       if (cr3_changed)
+               lazy_hcall1(LHCALL_FLUSH_TLB, 1);
+}
+
+#ifdef CONFIG_X86_PAE
+static void lguest_set_pte_atomic(pte_t *ptep, pte_t pte)
+{
+       native_set_pte_atomic(ptep, pte);
        if (cr3_changed)
                lazy_hcall1(LHCALL_FLUSH_TLB, 1);
 }
 
+void lguest_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
+{
+       native_pte_clear(mm, addr, ptep);
+       lguest_pte_update(mm, addr, ptep);
+}
+
+void lguest_pmd_clear(pmd_t *pmdp)
+{
+       lguest_set_pmd(pmdp, __pmd(0));
+}
+#endif
+
 /* Unfortunately for Lguest, the pv_mmu_ops for page tables were based on
  * native page table operations.  On native hardware you can set a new page
  * table entry whenever you want, but if you want to remove one you have to do
@@ -628,13 +694,12 @@ static void __init lguest_init_IRQ(void)
 {
        unsigned int i;
 
-       for (i = 0; i < LGUEST_IRQS; i++) {
-               int vector = FIRST_EXTERNAL_VECTOR + i;
+       for (i = FIRST_EXTERNAL_VECTOR; i < NR_VECTORS; i++) {
                /* Some systems map "vectors" to interrupts weirdly.  Lguest has
                 * a straightforward 1 to 1 mapping, so force that here. */
-               __get_cpu_var(vector_irq)[vector] = i;
-               if (vector != SYSCALL_VECTOR)
-                       set_intr_gate(vector, interrupt[i]);
+               __get_cpu_var(vector_irq)[i] = i - FIRST_EXTERNAL_VECTOR;
+               if (i != SYSCALL_VECTOR)
+                       set_intr_gate(i, interrupt[i - FIRST_EXTERNAL_VECTOR]);
        }
        /* This call is required to set up for 4k stacks, where we have
         * separate stacks for hard and soft interrupts. */
@@ -973,10 +1038,10 @@ static void lguest_restart(char *reason)
  *
  * Our current solution is to allow the paravirt back end to optionally patch
  * over the indirect calls to replace them with something more efficient.  We
- * patch the four most commonly called functions: disable interrupts, enable
- * interrupts, restore interrupts and save interrupts.  We usually have 6 or 10
- * bytes to patch into: the Guest versions of these operations are small enough
- * that we can fit comfortably.
+ * patch two of the simplest of the most commonly called functions: disable
+ * interrupts and save interrupts.  We usually have 6 or 10 bytes to patch
+ * into: the Guest versions of these operations are small enough that we can
+ * fit comfortably.
  *
  * First we need assembly templates of each of the patchable Guest operations,
  * and these are in i386_head.S. */
@@ -987,8 +1052,6 @@ static const struct lguest_insns
        const char *start, *end;
 } lguest_insns[] = {
        [PARAVIRT_PATCH(pv_irq_ops.irq_disable)] = { lgstart_cli, lgend_cli },
-       [PARAVIRT_PATCH(pv_irq_ops.irq_enable)] = { lgstart_sti, lgend_sti },
-       [PARAVIRT_PATCH(pv_irq_ops.restore_fl)] = { lgstart_popf, lgend_popf },
        [PARAVIRT_PATCH(pv_irq_ops.save_fl)] = { lgstart_pushf, lgend_pushf },
 };
 
@@ -1026,6 +1089,7 @@ __init void lguest_init(void)
        pv_info.name = "lguest";
        pv_info.paravirt_enabled = 1;
        pv_info.kernel_rpl = 1;
+       pv_info.shared_kernel_pmd = 1;
 
        /* We set up all the lguest overrides for sensitive operations.  These
         * are detailed with the operations themselves. */
@@ -1033,9 +1097,9 @@ __init void lguest_init(void)
        /* interrupt-related operations */
        pv_irq_ops.init_IRQ = lguest_init_IRQ;
        pv_irq_ops.save_fl = PV_CALLEE_SAVE(save_fl);
-       pv_irq_ops.restore_fl = PV_CALLEE_SAVE(restore_fl);
+       pv_irq_ops.restore_fl = __PV_IS_CALLEE_SAVE(lg_restore_fl);
        pv_irq_ops.irq_disable = PV_CALLEE_SAVE(irq_disable);
-       pv_irq_ops.irq_enable = PV_CALLEE_SAVE(irq_enable);
+       pv_irq_ops.irq_enable = __PV_IS_CALLEE_SAVE(lg_irq_enable);
        pv_irq_ops.safe_halt = lguest_safe_halt;
 
        /* init-time operations */
@@ -1071,6 +1135,12 @@ __init void lguest_init(void)
        pv_mmu_ops.set_pte = lguest_set_pte;
        pv_mmu_ops.set_pte_at = lguest_set_pte_at;
        pv_mmu_ops.set_pmd = lguest_set_pmd;
+#ifdef CONFIG_X86_PAE
+       pv_mmu_ops.set_pte_atomic = lguest_set_pte_atomic;
+       pv_mmu_ops.pte_clear = lguest_pte_clear;
+       pv_mmu_ops.pmd_clear = lguest_pmd_clear;
+       pv_mmu_ops.set_pud = lguest_set_pud;
+#endif
        pv_mmu_ops.read_cr2 = lguest_read_cr2;
        pv_mmu_ops.read_cr3 = lguest_read_cr3;
        pv_mmu_ops.lazy_mode.enter = paravirt_enter_lazy_mmu;
index f795419894718796d48050f80a6b279a6a875dff..a9c8cfe61cd4d48497a648538ce5b1c7d04e7cde 100644 (file)
@@ -46,10 +46,64 @@ ENTRY(lguest_entry)
        .globl lgstart_##name; .globl lgend_##name
 
 LGUEST_PATCH(cli, movl $0, lguest_data+LGUEST_DATA_irq_enabled)
-LGUEST_PATCH(sti, movl $X86_EFLAGS_IF, lguest_data+LGUEST_DATA_irq_enabled)
-LGUEST_PATCH(popf, movl %eax, lguest_data+LGUEST_DATA_irq_enabled)
 LGUEST_PATCH(pushf, movl lguest_data+LGUEST_DATA_irq_enabled, %eax)
-/*:*/
+
+/*G:033 But using those wrappers is inefficient (we'll see why that doesn't
+ * matter for save_fl and irq_disable later).  If we write our routines
+ * carefully in assembler, we can avoid clobbering any registers and avoid
+ * jumping through the wrapper functions.
+ *
+ * I skipped over our first piece of assembler, but this one is worth studying
+ * in a bit more detail so I'll describe in easy stages.  First, the routine
+ * to enable interrupts: */
+ENTRY(lg_irq_enable)
+       /* The reverse of irq_disable, this sets lguest_data.irq_enabled to
+        * X86_EFLAGS_IF (ie. "Interrupts enabled"). */
+       movl $X86_EFLAGS_IF, lguest_data+LGUEST_DATA_irq_enabled
+       /* But now we need to check if the Host wants to know: there might have
+        * been interrupts waiting to be delivered, in which case it will have
+        * set lguest_data.irq_pending to X86_EFLAGS_IF.  If it's not zero, we
+        * jump to send_interrupts, otherwise we're done. */
+       testl $0, lguest_data+LGUEST_DATA_irq_pending
+       jnz send_interrupts
+       /* One cool thing about x86 is that you can do many things without using
+        * a register.  In this case, the normal path hasn't needed to save or
+        * restore any registers at all! */
+       ret
+send_interrupts:
+       /* OK, now we need a register: eax is used for the hypercall number,
+        * which is LHCALL_SEND_INTERRUPTS.
+        *
+        * We used not to bother with this pending detection at all, which was
+        * much simpler.  Sooner or later the Host would realize it had to
+        * send us an interrupt.  But that turns out to make performance 7
+        * times worse on a simple tcp benchmark.  So now we do this the hard
+        * way. */
+       pushl %eax
+       movl $LHCALL_SEND_INTERRUPTS, %eax
+       /* This is a vmcall instruction (same thing that KVM uses).  Older
+        * assembler versions might not know the "vmcall" instruction, so we
+        * create one manually here. */
+       .byte 0x0f,0x01,0xc1 /* KVM_HYPERCALL */
+       popl %eax
+       ret
+
+/* Finally, the "popf" or "restore flags" routine.  The %eax register holds the
+ * flags (in practice, either X86_EFLAGS_IF or 0): if it's X86_EFLAGS_IF we're
+ * enabling interrupts again, if it's 0 we're leaving them off. */
+ENTRY(lg_restore_fl)
+       /* This is just "lguest_data.irq_enabled = flags;" */
+       movl %eax, lguest_data+LGUEST_DATA_irq_enabled
+       /* Now, if the %eax value has enabled interrupts and
+        * lguest_data.irq_pending is set, we want to tell the Host so it can
+        * deliver any outstanding interrupts.  Fortunately, both values will
+        * be X86_EFLAGS_IF (ie. 512) in that case, and the "testl"
+        * instruction will AND them together for us.  If both are set, we
+        * jump to send_interrupts. */
+       testl lguest_data+LGUEST_DATA_irq_pending, %eax
+       jnz send_interrupts
+       /* Again, the normal path has used no extra registers.  Clever, huh? */
+       ret
 
 /* These demark the EIP range where host should never deliver interrupts. */
 .global lguest_noirq_start
index 949708d7a481ec10e9591faba7466b3e1a75f57d..9ff3c0816d158e33c168e4c11a64e511c236705f 100644 (file)
@@ -564,7 +564,7 @@ static inline void save_pg_dir(void)
 }
 #endif /* !CONFIG_ACPI_SLEEP */
 
-void zap_low_mappings(void)
+void zap_low_mappings(bool early)
 {
        int i;
 
@@ -581,7 +581,11 @@ void zap_low_mappings(void)
                set_pgd(swapper_pg_dir+i, __pgd(0));
 #endif
        }
-       flush_tlb_all();
+
+       if (early)
+               __flush_tlb();
+       else
+               flush_tlb_all();
 }
 
 pteval_t __supported_pte_mask __read_mostly = ~(_PAGE_NX | _PAGE_GLOBAL | _PAGE_IOMAP);
@@ -956,7 +960,7 @@ void __init mem_init(void)
                test_wp_bit();
 
        save_pg_dir();
-       zap_low_mappings();
+       zap_low_mappings(true);
 }
 
 #ifdef CONFIG_MEMORY_HOTPLUG
index 58b32db33125a9eb13cc3859597cfb5b32b2cb4c..de2abbd0754481d0528ee527077d668d7b3e422b 100644 (file)
@@ -3,5 +3,5 @@
 nostackp := $(call cc-option, -fno-stack-protector)
 CFLAGS_cpu_$(BITS).o   := $(nostackp)
 
-obj-$(CONFIG_PM_SLEEP)         += cpu_$(BITS).o
+obj-$(CONFIG_PM_SLEEP)         += cpu.o
 obj-$(CONFIG_HIBERNATION)      += hibernate_$(BITS).o hibernate_asm_$(BITS).o
similarity index 62%
rename from arch/x86/power/cpu_64.c
rename to arch/x86/power/cpu.c
index 5343540f26074bac105a66790733e66723e1e615..d277ef1eea511c1047b2dfb06ea784496c42a0f1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Suspend and hibernation support for x86-64
+ * Suspend support specific for i386/x86-64.
  *
  * Distribute under GPLv2
  *
@@ -8,18 +8,28 @@
  * Copyright (c) 2001 Patrick Mochel <mochel@osdl.org>
  */
 
-#include <linux/smp.h>
 #include <linux/suspend.h>
-#include <asm/proto.h>
-#include <asm/page.h>
+#include <linux/smp.h>
+
 #include <asm/pgtable.h>
+#include <asm/proto.h>
 #include <asm/mtrr.h>
+#include <asm/page.h>
+#include <asm/mce.h>
 #include <asm/xcr.h>
 #include <asm/suspend.h>
 
-static void fix_processor_context(void);
+#ifdef CONFIG_X86_32
+static struct saved_context saved_context;
 
+unsigned long saved_context_ebx;
+unsigned long saved_context_esp, saved_context_ebp;
+unsigned long saved_context_esi, saved_context_edi;
+unsigned long saved_context_eflags;
+#else
+/* CONFIG_X86_64 */
 struct saved_context saved_context;
+#endif
 
 /**
  *     __save_processor_state - save CPU registers before creating a
@@ -38,19 +48,35 @@ struct saved_context saved_context;
  */
 static void __save_processor_state(struct saved_context *ctxt)
 {
+#ifdef CONFIG_X86_32
+       mtrr_save_fixed_ranges(NULL);
+#endif
        kernel_fpu_begin();
 
        /*
         * descriptor tables
         */
+#ifdef CONFIG_X86_32
+       store_gdt(&ctxt->gdt);
+       store_idt(&ctxt->idt);
+#else
+/* CONFIG_X86_64 */
        store_gdt((struct desc_ptr *)&ctxt->gdt_limit);
        store_idt((struct desc_ptr *)&ctxt->idt_limit);
+#endif
        store_tr(ctxt->tr);
 
        /* XMM0..XMM15 should be handled by kernel_fpu_begin(). */
        /*
         * segment registers
         */
+#ifdef CONFIG_X86_32
+       savesegment(es, ctxt->es);
+       savesegment(fs, ctxt->fs);
+       savesegment(gs, ctxt->gs);
+       savesegment(ss, ctxt->ss);
+#else
+/* CONFIG_X86_64 */
        asm volatile ("movw %%ds, %0" : "=m" (ctxt->ds));
        asm volatile ("movw %%es, %0" : "=m" (ctxt->es));
        asm volatile ("movw %%fs, %0" : "=m" (ctxt->fs));
@@ -62,30 +88,87 @@ static void __save_processor_state(struct saved_context *ctxt)
        rdmsrl(MSR_KERNEL_GS_BASE, ctxt->gs_kernel_base);
        mtrr_save_fixed_ranges(NULL);
 
+       rdmsrl(MSR_EFER, ctxt->efer);
+#endif
+
        /*
         * control registers
         */
-       rdmsrl(MSR_EFER, ctxt->efer);
        ctxt->cr0 = read_cr0();
        ctxt->cr2 = read_cr2();
        ctxt->cr3 = read_cr3();
+#ifdef CONFIG_X86_32
+       ctxt->cr4 = read_cr4_safe();
+#else
+/* CONFIG_X86_64 */
        ctxt->cr4 = read_cr4();
        ctxt->cr8 = read_cr8();
+#endif
 }
 
+/* Needed by apm.c */
 void save_processor_state(void)
 {
        __save_processor_state(&saved_context);
 }
+#ifdef CONFIG_X86_32
+EXPORT_SYMBOL(save_processor_state);
+#endif
 
 static void do_fpu_end(void)
 {
        /*
-        * Restore FPU regs if necessary
+        * Restore FPU regs if necessary.
         */
        kernel_fpu_end();
 }
 
+static void fix_processor_context(void)
+{
+       int cpu = smp_processor_id();
+       struct tss_struct *t = &per_cpu(init_tss, cpu);
+
+       set_tss_desc(cpu, t);   /*
+                                * This just modifies memory; should not be
+                                * necessary. But... This is necessary, because
+                                * 386 hardware has concept of busy TSS or some
+                                * similar stupidity.
+                                */
+
+#ifdef CONFIG_X86_64
+       get_cpu_gdt_table(cpu)[GDT_ENTRY_TSS].type = 9;
+
+       syscall_init();                         /* This sets MSR_*STAR and related */
+#endif
+       load_TR_desc();                         /* This does ltr */
+       load_LDT(&current->active_mm->context); /* This does lldt */
+
+       /*
+        * Now maybe reload the debug registers
+        */
+       if (current->thread.debugreg7) {
+#ifdef CONFIG_X86_32
+               set_debugreg(current->thread.debugreg0, 0);
+               set_debugreg(current->thread.debugreg1, 1);
+               set_debugreg(current->thread.debugreg2, 2);
+               set_debugreg(current->thread.debugreg3, 3);
+               /* no 4 and 5 */
+               set_debugreg(current->thread.debugreg6, 6);
+               set_debugreg(current->thread.debugreg7, 7);
+#else
+               /* CONFIG_X86_64 */
+               loaddebug(&current->thread, 0);
+               loaddebug(&current->thread, 1);
+               loaddebug(&current->thread, 2);
+               loaddebug(&current->thread, 3);
+               /* no 4 and 5 */
+               loaddebug(&current->thread, 6);
+               loaddebug(&current->thread, 7);
+#endif
+       }
+
+}
+
 /**
  *     __restore_processor_state - restore the contents of CPU registers saved
  *             by __save_processor_state()
@@ -96,9 +179,16 @@ static void __restore_processor_state(struct saved_context *ctxt)
        /*
         * control registers
         */
+       /* cr4 was introduced in the Pentium CPU */
+#ifdef CONFIG_X86_32
+       if (ctxt->cr4)
+               write_cr4(ctxt->cr4);
+#else
+/* CONFIG X86_64 */
        wrmsrl(MSR_EFER, ctxt->efer);
        write_cr8(ctxt->cr8);
        write_cr4(ctxt->cr4);
+#endif
        write_cr3(ctxt->cr3);
        write_cr2(ctxt->cr2);
        write_cr0(ctxt->cr0);
@@ -107,13 +197,31 @@ static void __restore_processor_state(struct saved_context *ctxt)
         * now restore the descriptor tables to their proper values
         * ltr is done i fix_processor_context().
         */
+#ifdef CONFIG_X86_32
+       load_gdt(&ctxt->gdt);
+       load_idt(&ctxt->idt);
+#else
+/* CONFIG_X86_64 */
        load_gdt((const struct desc_ptr *)&ctxt->gdt_limit);
        load_idt((const struct desc_ptr *)&ctxt->idt_limit);
-
+#endif
 
        /*
         * segment registers
         */
+#ifdef CONFIG_X86_32
+       loadsegment(es, ctxt->es);
+       loadsegment(fs, ctxt->fs);
+       loadsegment(gs, ctxt->gs);
+       loadsegment(ss, ctxt->ss);
+
+       /*
+        * sysenter MSRs
+        */
+       if (boot_cpu_has(X86_FEATURE_SEP))
+               enable_sep_cpu();
+#else
+/* CONFIG_X86_64 */
        asm volatile ("movw %0, %%ds" :: "r" (ctxt->ds));
        asm volatile ("movw %0, %%es" :: "r" (ctxt->es));
        asm volatile ("movw %0, %%fs" :: "r" (ctxt->fs));
@@ -123,6 +231,7 @@ static void __restore_processor_state(struct saved_context *ctxt)
        wrmsrl(MSR_FS_BASE, ctxt->fs_base);
        wrmsrl(MSR_GS_BASE, ctxt->gs_base);
        wrmsrl(MSR_KERNEL_GS_BASE, ctxt->gs_kernel_base);
+#endif
 
        /*
         * restore XCR0 for xsave capable cpu's.
@@ -134,41 +243,17 @@ static void __restore_processor_state(struct saved_context *ctxt)
 
        do_fpu_end();
        mtrr_ap_init();
+
+#ifdef CONFIG_X86_32
+       mcheck_init(&boot_cpu_data);
+#endif
 }
 
+/* Needed by apm.c */
 void restore_processor_state(void)
 {
        __restore_processor_state(&saved_context);
 }
-
-static void fix_processor_context(void)
-{
-       int cpu = smp_processor_id();
-       struct tss_struct *t = &per_cpu(init_tss, cpu);
-
-       /*
-        * This just modifies memory; should not be necessary. But... This
-        * is necessary, because 386 hardware has concept of busy TSS or some
-        * similar stupidity.
-        */
-       set_tss_desc(cpu, t);
-
-       get_cpu_gdt_table(cpu)[GDT_ENTRY_TSS].type = 9;
-
-       syscall_init();                         /* This sets MSR_*STAR and related */
-       load_TR_desc();                         /* This does ltr */
-       load_LDT(&current->active_mm->context); /* This does lldt */
-
-       /*
-        * Now maybe reload the debug registers
-        */
-       if (current->thread.debugreg7){
-                loaddebug(&current->thread, 0);
-                loaddebug(&current->thread, 1);
-                loaddebug(&current->thread, 2);
-                loaddebug(&current->thread, 3);
-                /* no 4 and 5 */
-                loaddebug(&current->thread, 6);
-                loaddebug(&current->thread, 7);
-       }
-}
+#ifdef CONFIG_X86_32
+EXPORT_SYMBOL(restore_processor_state);
+#endif
diff --git a/arch/x86/power/cpu_32.c b/arch/x86/power/cpu_32.c
deleted file mode 100644 (file)
index ce702c5..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Suspend support specific for i386.
- *
- * Distribute under GPLv2
- *
- * Copyright (c) 2002 Pavel Machek <pavel@suse.cz>
- * Copyright (c) 2001 Patrick Mochel <mochel@osdl.org>
- */
-
-#include <linux/module.h>
-#include <linux/suspend.h>
-#include <asm/mtrr.h>
-#include <asm/mce.h>
-#include <asm/xcr.h>
-#include <asm/suspend.h>
-
-static struct saved_context saved_context;
-
-unsigned long saved_context_ebx;
-unsigned long saved_context_esp, saved_context_ebp;
-unsigned long saved_context_esi, saved_context_edi;
-unsigned long saved_context_eflags;
-
-static void __save_processor_state(struct saved_context *ctxt)
-{
-       mtrr_save_fixed_ranges(NULL);
-       kernel_fpu_begin();
-
-       /*
-        * descriptor tables
-        */
-       store_gdt(&ctxt->gdt);
-       store_idt(&ctxt->idt);
-       store_tr(ctxt->tr);
-
-       /*
-        * segment registers
-        */
-       savesegment(es, ctxt->es);
-       savesegment(fs, ctxt->fs);
-       savesegment(gs, ctxt->gs);
-       savesegment(ss, ctxt->ss);
-
-       /*
-        * control registers
-        */
-       ctxt->cr0 = read_cr0();
-       ctxt->cr2 = read_cr2();
-       ctxt->cr3 = read_cr3();
-       ctxt->cr4 = read_cr4_safe();
-}
-
-/* Needed by apm.c */
-void save_processor_state(void)
-{
-       __save_processor_state(&saved_context);
-}
-EXPORT_SYMBOL(save_processor_state);
-
-static void do_fpu_end(void)
-{
-       /*
-        * Restore FPU regs if necessary.
-        */
-       kernel_fpu_end();
-}
-
-static void fix_processor_context(void)
-{
-       int cpu = smp_processor_id();
-       struct tss_struct *t = &per_cpu(init_tss, cpu);
-
-       set_tss_desc(cpu, t);   /*
-                                * This just modifies memory; should not be
-                                * necessary. But... This is necessary, because
-                                * 386 hardware has concept of busy TSS or some
-                                * similar stupidity.
-                                */
-
-       load_TR_desc();                         /* This does ltr */
-       load_LDT(&current->active_mm->context); /* This does lldt */
-
-       /*
-        * Now maybe reload the debug registers
-        */
-       if (current->thread.debugreg7) {
-               set_debugreg(current->thread.debugreg0, 0);
-               set_debugreg(current->thread.debugreg1, 1);
-               set_debugreg(current->thread.debugreg2, 2);
-               set_debugreg(current->thread.debugreg3, 3);
-               /* no 4 and 5 */
-               set_debugreg(current->thread.debugreg6, 6);
-               set_debugreg(current->thread.debugreg7, 7);
-       }
-
-}
-
-static void __restore_processor_state(struct saved_context *ctxt)
-{
-       /*
-        * control registers
-        */
-       /* cr4 was introduced in the Pentium CPU */
-       if (ctxt->cr4)
-               write_cr4(ctxt->cr4);
-       write_cr3(ctxt->cr3);
-       write_cr2(ctxt->cr2);
-       write_cr0(ctxt->cr0);
-
-       /*
-        * now restore the descriptor tables to their proper values
-        * ltr is done i fix_processor_context().
-        */
-       load_gdt(&ctxt->gdt);
-       load_idt(&ctxt->idt);
-
-       /*
-        * segment registers
-        */
-       loadsegment(es, ctxt->es);
-       loadsegment(fs, ctxt->fs);
-       loadsegment(gs, ctxt->gs);
-       loadsegment(ss, ctxt->ss);
-
-       /*
-        * sysenter MSRs
-        */
-       if (boot_cpu_has(X86_FEATURE_SEP))
-               enable_sep_cpu();
-
-       /*
-        * restore XCR0 for xsave capable cpu's.
-        */
-       if (cpu_has_xsave)
-               xsetbv(XCR_XFEATURE_ENABLED_MASK, pcntxt_mask);
-
-       fix_processor_context();
-       do_fpu_end();
-       mtrr_ap_init();
-       mcheck_init(&boot_cpu_data);
-}
-
-/* Needed by apm.c */
-void restore_processor_state(void)
-{
-       __restore_processor_state(&saved_context);
-}
-EXPORT_SYMBOL(restore_processor_state);
index 67ad67bed8c14abb1c3daac72a0106d80e31c9af..22d6dde426192266c3915403f8a73785fef6f916 100644 (file)
@@ -292,7 +292,7 @@ static inline void atomic_set_mask(unsigned int mask, atomic_t *v)
 #define smp_mb__before_atomic_inc()    barrier()
 #define smp_mb__after_atomic_inc()     barrier()
 
-#include <asm-generic/atomic.h>
+#include <asm-generic/atomic-long.h>
 #endif /* __KERNEL__ */
 
 #endif /* _XTENSA_ATOMIC_H */
diff --git a/arch/xtensa/include/asm/bitsperlong.h b/arch/xtensa/include/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..6dc0bb0
--- /dev/null
@@ -0,0 +1 @@
+#include <asm-generic/bitsperlong.h>
index 17e0c5383b108abb9dac7af627aa00ac1fd343ec..161bb89e98c8b20f8b76f9c194bffa2727fa6cf2 100644 (file)
@@ -129,7 +129,7 @@ static inline __attribute_const__ int get_order(unsigned long size)
 
 #else
 
-# include <asm-generic/page.h>
+# include <asm-generic/getorder.h>
 
 #endif
 
index 3981a466c779cfd57f8e4cd0033581d8dfc3edd4..c1accea8cb56c036cec14091d5b85f98b2c572e7 100644 (file)
@@ -34,8 +34,6 @@ void *module_alloc(unsigned long size)
 void module_free(struct module *mod, void *module_region)
 {
        vfree(module_region);
-       /* FIXME: If module_region == mod->init_region, trim exception
-          table entries. */
 }
 
 int module_frob_arch_sections(Elf32_Ehdr *hdr,
index d3a59c688fe4fcaf0f7ebf514f44f59a08e90693..8a267c4276291bb775e2590a6935825ae35e9305 100644 (file)
@@ -17,7 +17,7 @@
 #include <linux/bitops.h>
 #include <linux/mutex.h>
 #include <linux/kthread.h>
-
+#include <linux/highmem.h>
 #include <linux/firmware.h>
 #include "base.h"
 
@@ -45,7 +45,10 @@ struct firmware_priv {
        struct bin_attribute attr_data;
        struct firmware *fw;
        unsigned long status;
-       int alloc_size;
+       struct page **pages;
+       int nr_pages;
+       int page_array_size;
+       const char *vdata;
        struct timer_list timeout;
 };
 
@@ -122,6 +125,10 @@ static ssize_t firmware_loading_show(struct device *dev,
        return sprintf(buf, "%d\n", loading);
 }
 
+/* Some architectures don't have PAGE_KERNEL_RO */
+#ifndef PAGE_KERNEL_RO
+#define PAGE_KERNEL_RO PAGE_KERNEL
+#endif
 /**
  * firmware_loading_store - set value in the 'loading' control file
  * @dev: device pointer
@@ -141,6 +148,7 @@ static ssize_t firmware_loading_store(struct device *dev,
 {
        struct firmware_priv *fw_priv = dev_get_drvdata(dev);
        int loading = simple_strtol(buf, NULL, 10);
+       int i;
 
        switch (loading) {
        case 1:
@@ -151,13 +159,30 @@ static ssize_t firmware_loading_store(struct device *dev,
                }
                vfree(fw_priv->fw->data);
                fw_priv->fw->data = NULL;
+               for (i = 0; i < fw_priv->nr_pages; i++)
+                       __free_page(fw_priv->pages[i]);
+               kfree(fw_priv->pages);
+               fw_priv->pages = NULL;
+               fw_priv->page_array_size = 0;
+               fw_priv->nr_pages = 0;
                fw_priv->fw->size = 0;
-               fw_priv->alloc_size = 0;
                set_bit(FW_STATUS_LOADING, &fw_priv->status);
                mutex_unlock(&fw_lock);
                break;
        case 0:
                if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
+                       vfree(fw_priv->fw->data);
+                       fw_priv->fw->data = vmap(fw_priv->pages,
+                                                fw_priv->nr_pages,
+                                                0, PAGE_KERNEL_RO);
+                       if (!fw_priv->fw->data) {
+                               dev_err(dev, "%s: vmap() failed\n", __func__);
+                               goto err;
+                       }
+                       /* Pages will be freed by vfree() */
+                       fw_priv->pages = NULL;
+                       fw_priv->page_array_size = 0;
+                       fw_priv->nr_pages = 0;
                        complete(&fw_priv->completion);
                        clear_bit(FW_STATUS_LOADING, &fw_priv->status);
                        break;
@@ -167,6 +192,7 @@ static ssize_t firmware_loading_store(struct device *dev,
                dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
                /* fallthrough */
        case -1:
+       err:
                fw_load_abort(fw_priv);
                break;
        }
@@ -191,8 +217,28 @@ firmware_data_read(struct kobject *kobj, struct bin_attribute *bin_attr,
                ret_count = -ENODEV;
                goto out;
        }
-       ret_count = memory_read_from_buffer(buffer, count, &offset,
-                                               fw->data, fw->size);
+       if (offset > fw->size)
+               return 0;
+       if (count > fw->size - offset)
+               count = fw->size - offset;
+
+       ret_count = count;
+
+       while (count) {
+               void *page_data;
+               int page_nr = offset >> PAGE_SHIFT;
+               int page_ofs = offset & (PAGE_SIZE-1);
+               int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
+
+               page_data = kmap(fw_priv->pages[page_nr]);
+
+               memcpy(buffer, page_data + page_ofs, page_cnt);
+
+               kunmap(fw_priv->pages[page_nr]);
+               buffer += page_cnt;
+               offset += page_cnt;
+               count -= page_cnt;
+       }
 out:
        mutex_unlock(&fw_lock);
        return ret_count;
@@ -201,27 +247,39 @@ out:
 static int
 fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
 {
-       u8 *new_data;
-       int new_size = fw_priv->alloc_size;
+       int pages_needed = ALIGN(min_size, PAGE_SIZE) >> PAGE_SHIFT;
+
+       /* If the array of pages is too small, grow it... */
+       if (fw_priv->page_array_size < pages_needed) {
+               int new_array_size = max(pages_needed,
+                                        fw_priv->page_array_size * 2);
+               struct page **new_pages;
+
+               new_pages = kmalloc(new_array_size * sizeof(void *),
+                                   GFP_KERNEL);
+               if (!new_pages) {
+                       fw_load_abort(fw_priv);
+                       return -ENOMEM;
+               }
+               memcpy(new_pages, fw_priv->pages,
+                      fw_priv->page_array_size * sizeof(void *));
+               memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
+                      (new_array_size - fw_priv->page_array_size));
+               kfree(fw_priv->pages);
+               fw_priv->pages = new_pages;
+               fw_priv->page_array_size = new_array_size;
+       }
 
-       if (min_size <= fw_priv->alloc_size)
-               return 0;
+       while (fw_priv->nr_pages < pages_needed) {
+               fw_priv->pages[fw_priv->nr_pages] =
+                       alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
 
-       new_size = ALIGN(min_size, PAGE_SIZE);
-       new_data = vmalloc(new_size);
-       if (!new_data) {
-               printk(KERN_ERR "%s: unable to alloc buffer\n", __func__);
-               /* Make sure that we don't keep incomplete data */
-               fw_load_abort(fw_priv);
-               return -ENOMEM;
-       }
-       fw_priv->alloc_size = new_size;
-       if (fw_priv->fw->data) {
-               memcpy(new_data, fw_priv->fw->data, fw_priv->fw->size);
-               vfree(fw_priv->fw->data);
+               if (!fw_priv->pages[fw_priv->nr_pages]) {
+                       fw_load_abort(fw_priv);
+                       return -ENOMEM;
+               }
+               fw_priv->nr_pages++;
        }
-       fw_priv->fw->data = new_data;
-       BUG_ON(min_size > fw_priv->alloc_size);
        return 0;
 }
 
@@ -258,10 +316,25 @@ firmware_data_write(struct kobject *kobj, struct bin_attribute *bin_attr,
        if (retval)
                goto out;
 
-       memcpy((u8 *)fw->data + offset, buffer, count);
-
-       fw->size = max_t(size_t, offset + count, fw->size);
        retval = count;
+
+       while (count) {
+               void *page_data;
+               int page_nr = offset >> PAGE_SHIFT;
+               int page_ofs = offset & (PAGE_SIZE - 1);
+               int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
+
+               page_data = kmap(fw_priv->pages[page_nr]);
+
+               memcpy(page_data + page_ofs, buffer, page_cnt);
+
+               kunmap(fw_priv->pages[page_nr]);
+               buffer += page_cnt;
+               offset += page_cnt;
+               count -= page_cnt;
+       }
+
+       fw->size = max_t(size_t, offset, fw->size);
 out:
        mutex_unlock(&fw_lock);
        return retval;
@@ -277,7 +350,11 @@ static struct bin_attribute firmware_attr_data_tmpl = {
 static void fw_dev_release(struct device *dev)
 {
        struct firmware_priv *fw_priv = dev_get_drvdata(dev);
+       int i;
 
+       for (i = 0; i < fw_priv->nr_pages; i++)
+               __free_page(fw_priv->pages[i]);
+       kfree(fw_priv->pages);
        kfree(fw_priv);
        kfree(dev);
 
index 8b4708e06244b2ba0b61b241b2ffd1d55b18f444..ead3f64c41d0506774788860f0f2232f22c9ce0a 100644 (file)
@@ -469,22 +469,6 @@ static void platform_drv_shutdown(struct device *_dev)
        drv->shutdown(dev);
 }
 
-static int platform_drv_suspend(struct device *_dev, pm_message_t state)
-{
-       struct platform_driver *drv = to_platform_driver(_dev->driver);
-       struct platform_device *dev = to_platform_device(_dev);
-
-       return drv->suspend(dev, state);
-}
-
-static int platform_drv_resume(struct device *_dev)
-{
-       struct platform_driver *drv = to_platform_driver(_dev->driver);
-       struct platform_device *dev = to_platform_device(_dev);
-
-       return drv->resume(dev);
-}
-
 /**
  * platform_driver_register
  * @drv: platform driver structure
@@ -498,10 +482,10 @@ int platform_driver_register(struct platform_driver *drv)
                drv->driver.remove = platform_drv_remove;
        if (drv->shutdown)
                drv->driver.shutdown = platform_drv_shutdown;
-       if (drv->suspend)
-               drv->driver.suspend = platform_drv_suspend;
-       if (drv->resume)
-               drv->driver.resume = platform_drv_resume;
+       if (drv->suspend || drv->resume)
+               pr_warning("Platform driver '%s' needs updating - please use "
+                       "dev_pm_ops\n", drv->driver.name);
+
        return driver_register(&drv->driver);
 }
 EXPORT_SYMBOL_GPL(platform_driver_register);
@@ -633,10 +617,12 @@ static int platform_match(struct device *dev, struct device_driver *drv)
 
 static int platform_legacy_suspend(struct device *dev, pm_message_t mesg)
 {
+       struct platform_driver *pdrv = to_platform_driver(dev->driver);
+       struct platform_device *pdev = to_platform_device(dev);
        int ret = 0;
 
-       if (dev->driver && dev->driver->suspend)
-               ret = dev->driver->suspend(dev, mesg);
+       if (dev->driver && pdrv->suspend)
+               ret = pdrv->suspend(pdev, mesg);
 
        return ret;
 }
@@ -667,10 +653,12 @@ static int platform_legacy_resume_early(struct device *dev)
 
 static int platform_legacy_resume(struct device *dev)
 {
+       struct platform_driver *pdrv = to_platform_driver(dev->driver);
+       struct platform_device *pdev = to_platform_device(dev);
        int ret = 0;
 
-       if (dev->driver && dev->driver->resume)
-               ret = dev->driver->resume(dev);
+       if (dev->driver && pdrv->resume)
+               ret = pdrv->resume(pdev);
 
        return ret;
 }
index 3e4bc699bc0f30349be3ed75b9ecc28cdb77ddf1..fae725458981793649bf3e339a20394ac9859448 100644 (file)
@@ -315,13 +315,13 @@ static void pm_dev_err(struct device *dev, pm_message_t state, char *info,
 /*------------------------- Resume routines -------------------------*/
 
 /**
- *     resume_device_noirq - Power on one device (early resume).
+ *     device_resume_noirq - Power on one device (early resume).
  *     @dev:   Device.
  *     @state: PM transition of the system being carried out.
  *
  *     Must be called with interrupts disabled.
  */
-static int resume_device_noirq(struct device *dev, pm_message_t state)
+static int device_resume_noirq(struct device *dev, pm_message_t state)
 {
        int error = 0;
 
@@ -334,9 +334,6 @@ static int resume_device_noirq(struct device *dev, pm_message_t state)
        if (dev->bus->pm) {
                pm_dev_dbg(dev, state, "EARLY ");
                error = pm_noirq_op(dev, dev->bus->pm, state);
-       } else if (dev->bus->resume_early) {
-               pm_dev_dbg(dev, state, "legacy EARLY ");
-               error = dev->bus->resume_early(dev);
        }
  End:
        TRACE_RESUME(error);
@@ -344,16 +341,16 @@ static int resume_device_noirq(struct device *dev, pm_message_t state)
 }
 
 /**
- *     dpm_power_up - Power on all regular (non-sysdev) devices.
+ *     dpm_resume_noirq - Power on all regular (non-sysdev) devices.
  *     @state: PM transition of the system being carried out.
  *
- *     Execute the appropriate "noirq resume" callback for all devices marked
- *     as DPM_OFF_IRQ.
+ *     Call the "noirq" resume handlers for all devices marked as
+ *     DPM_OFF_IRQ and enable device drivers to receive interrupts.
  *
  *     Must be called under dpm_list_mtx.  Device drivers should not receive
  *     interrupts while it's being executed.
  */
-static void dpm_power_up(pm_message_t state)
+void dpm_resume_noirq(pm_message_t state)
 {
        struct device *dev;
 
@@ -363,33 +360,21 @@ static void dpm_power_up(pm_message_t state)
                        int error;
 
                        dev->power.status = DPM_OFF;
-                       error = resume_device_noirq(dev, state);
+                       error = device_resume_noirq(dev, state);
                        if (error)
                                pm_dev_err(dev, state, " early", error);
                }
        mutex_unlock(&dpm_list_mtx);
-}
-
-/**
- *     device_power_up - Turn on all devices that need special attention.
- *     @state: PM transition of the system being carried out.
- *
- *     Call the "early" resume handlers and enable device drivers to receive
- *     interrupts.
- */
-void device_power_up(pm_message_t state)
-{
-       dpm_power_up(state);
        resume_device_irqs();
 }
-EXPORT_SYMBOL_GPL(device_power_up);
+EXPORT_SYMBOL_GPL(dpm_resume_noirq);
 
 /**
- *     resume_device - Restore state for one device.
+ *     device_resume - Restore state for one device.
  *     @dev:   Device.
  *     @state: PM transition of the system being carried out.
  */
-static int resume_device(struct device *dev, pm_message_t state)
+static int device_resume(struct device *dev, pm_message_t state)
 {
        int error = 0;
 
@@ -414,9 +399,6 @@ static int resume_device(struct device *dev, pm_message_t state)
                if (dev->type->pm) {
                        pm_dev_dbg(dev, state, "type ");
                        error = pm_op(dev, dev->type->pm, state);
-               } else if (dev->type->resume) {
-                       pm_dev_dbg(dev, state, "legacy type ");
-                       error = dev->type->resume(dev);
                }
                if (error)
                        goto End;
@@ -462,7 +444,7 @@ static void dpm_resume(pm_message_t state)
                        dev->power.status = DPM_RESUMING;
                        mutex_unlock(&dpm_list_mtx);
 
-                       error = resume_device(dev, state);
+                       error = device_resume(dev, state);
 
                        mutex_lock(&dpm_list_mtx);
                        if (error)
@@ -480,11 +462,11 @@ static void dpm_resume(pm_message_t state)
 }
 
 /**
- *     complete_device - Complete a PM transition for given device
+ *     device_complete - Complete a PM transition for given device
  *     @dev:   Device.
  *     @state: PM transition of the system being carried out.
  */
-static void complete_device(struct device *dev, pm_message_t state)
+static void device_complete(struct device *dev, pm_message_t state)
 {
        down(&dev->sem);
 
@@ -527,7 +509,7 @@ static void dpm_complete(pm_message_t state)
                        dev->power.status = DPM_ON;
                        mutex_unlock(&dpm_list_mtx);
 
-                       complete_device(dev, state);
+                       device_complete(dev, state);
 
                        mutex_lock(&dpm_list_mtx);
                }
@@ -540,19 +522,19 @@ static void dpm_complete(pm_message_t state)
 }
 
 /**
- *     device_resume - Restore state of each device in system.
+ *     dpm_resume_end - Restore state of each device in system.
  *     @state: PM transition of the system being carried out.
  *
  *     Resume all the devices, unlock them all, and allow new
  *     devices to be registered once again.
  */
-void device_resume(pm_message_t state)
+void dpm_resume_end(pm_message_t state)
 {
        might_sleep();
        dpm_resume(state);
        dpm_complete(state);
 }
-EXPORT_SYMBOL_GPL(device_resume);
+EXPORT_SYMBOL_GPL(dpm_resume_end);
 
 
 /*------------------------- Suspend routines -------------------------*/
@@ -577,13 +559,13 @@ static pm_message_t resume_event(pm_message_t sleep_state)
 }
 
 /**
- *     suspend_device_noirq - Shut down one device (late suspend).
+ *     device_suspend_noirq - Shut down one device (late suspend).
  *     @dev:   Device.
  *     @state: PM transition of the system being carried out.
  *
  *     This is called with interrupts off and only a single CPU running.
  */
-static int suspend_device_noirq(struct device *dev, pm_message_t state)
+static int device_suspend_noirq(struct device *dev, pm_message_t state)
 {
        int error = 0;
 
@@ -593,24 +575,20 @@ static int suspend_device_noirq(struct device *dev, pm_message_t state)
        if (dev->bus->pm) {
                pm_dev_dbg(dev, state, "LATE ");
                error = pm_noirq_op(dev, dev->bus->pm, state);
-       } else if (dev->bus->suspend_late) {
-               pm_dev_dbg(dev, state, "legacy LATE ");
-               error = dev->bus->suspend_late(dev, state);
-               suspend_report_result(dev->bus->suspend_late, error);
        }
        return error;
 }
 
 /**
- *     device_power_down - Shut down special devices.
+ *     dpm_suspend_noirq - Power down all regular (non-sysdev) devices.
  *     @state: PM transition of the system being carried out.
  *
- *     Prevent device drivers from receiving interrupts and call the "late"
+ *     Prevent device drivers from receiving interrupts and call the "noirq"
  *     suspend handlers.
  *
  *     Must be called under dpm_list_mtx.
  */
-int device_power_down(pm_message_t state)
+int dpm_suspend_noirq(pm_message_t state)
 {
        struct device *dev;
        int error = 0;
@@ -618,7 +596,7 @@ int device_power_down(pm_message_t state)
        suspend_device_irqs();
        mutex_lock(&dpm_list_mtx);
        list_for_each_entry_reverse(dev, &dpm_list, power.entry) {
-               error = suspend_device_noirq(dev, state);
+               error = device_suspend_noirq(dev, state);
                if (error) {
                        pm_dev_err(dev, state, " late", error);
                        break;
@@ -627,17 +605,17 @@ int device_power_down(pm_message_t state)
        }
        mutex_unlock(&dpm_list_mtx);
        if (error)
-               device_power_up(resume_event(state));
+               dpm_resume_noirq(resume_event(state));
        return error;
 }
-EXPORT_SYMBOL_GPL(device_power_down);
+EXPORT_SYMBOL_GPL(dpm_suspend_noirq);
 
 /**
- *     suspend_device - Save state of one device.
+ *     device_suspend - Save state of one device.
  *     @dev:   Device.
  *     @state: PM transition of the system being carried out.
  */
-static int suspend_device(struct device *dev, pm_message_t state)
+static int device_suspend(struct device *dev, pm_message_t state)
 {
        int error = 0;
 
@@ -660,10 +638,6 @@ static int suspend_device(struct device *dev, pm_message_t state)
                if (dev->type->pm) {
                        pm_dev_dbg(dev, state, "type ");
                        error = pm_op(dev, dev->type->pm, state);
-               } else if (dev->type->suspend) {
-                       pm_dev_dbg(dev, state, "legacy type ");
-                       error = dev->type->suspend(dev, state);
-                       suspend_report_result(dev->type->suspend, error);
                }
                if (error)
                        goto End;
@@ -704,7 +678,7 @@ static int dpm_suspend(pm_message_t state)
                get_device(dev);
                mutex_unlock(&dpm_list_mtx);
 
-               error = suspend_device(dev, state);
+               error = device_suspend(dev, state);
 
                mutex_lock(&dpm_list_mtx);
                if (error) {
@@ -723,11 +697,11 @@ static int dpm_suspend(pm_message_t state)
 }
 
 /**
- *     prepare_device - Execute the ->prepare() callback(s) for given device.
+ *     device_prepare - Execute the ->prepare() callback(s) for given device.
  *     @dev:   Device.
  *     @state: PM transition of the system being carried out.
  */
-static int prepare_device(struct device *dev, pm_message_t state)
+static int device_prepare(struct device *dev, pm_message_t state)
 {
        int error = 0;
 
@@ -781,7 +755,7 @@ static int dpm_prepare(pm_message_t state)
                dev->power.status = DPM_PREPARING;
                mutex_unlock(&dpm_list_mtx);
 
-               error = prepare_device(dev, state);
+               error = device_prepare(dev, state);
 
                mutex_lock(&dpm_list_mtx);
                if (error) {
@@ -807,12 +781,12 @@ static int dpm_prepare(pm_message_t state)
 }
 
 /**
- *     device_suspend - Save state and stop all devices in system.
+ *     dpm_suspend_start - Save state and stop all devices in system.
  *     @state: PM transition of the system being carried out.
  *
  *     Prepare and suspend all devices.
  */
-int device_suspend(pm_message_t state)
+int dpm_suspend_start(pm_message_t state)
 {
        int error;
 
@@ -822,7 +796,7 @@ int device_suspend(pm_message_t state)
                error = dpm_suspend(state);
        return error;
 }
-EXPORT_SYMBOL_GPL(device_suspend);
+EXPORT_SYMBOL_GPL(dpm_suspend_start);
 
 void __suspend_report_result(const char *function, void *fn, int ret)
 {
index 3236b434b964c04078d6dfda2d4364e70fafb11b..9742a78c9fe42a043bfa0c57e89ead65e4f6fc2f 100644 (file)
@@ -343,11 +343,15 @@ static void __sysdev_resume(struct sys_device *dev)
        /* First, call the class-specific one */
        if (cls->resume)
                cls->resume(dev);
+       WARN_ONCE(!irqs_disabled(),
+               "Interrupts enabled after %pF\n", cls->resume);
 
        /* Call auxillary drivers next. */
        list_for_each_entry(drv, &cls->drivers, entry) {
                if (drv->resume)
                        drv->resume(dev);
+               WARN_ONCE(!irqs_disabled(),
+                       "Interrupts enabled after %pF\n", drv->resume);
        }
 }
 
@@ -377,6 +381,9 @@ int sysdev_suspend(pm_message_t state)
        if (ret)
                return ret;
 
+       WARN_ONCE(!irqs_disabled(),
+               "Interrupts enabled while suspending system devices\n");
+
        pr_debug("Suspending System Devices\n");
 
        list_for_each_entry_reverse(cls, &system_kset->list, kset.kobj.entry) {
@@ -393,6 +400,9 @@ int sysdev_suspend(pm_message_t state)
                                        if (ret)
                                                goto aux_driver;
                                }
+                               WARN_ONCE(!irqs_disabled(),
+                                       "Interrupts enabled after %pF\n",
+                                       drv->suspend);
                        }
 
                        /* Now call the generic one */
@@ -400,6 +410,9 @@ int sysdev_suspend(pm_message_t state)
                                ret = cls->suspend(sysdev, state);
                                if (ret)
                                        goto cls_driver;
+                               WARN_ONCE(!irqs_disabled(),
+                                       "Interrupts enabled after %pF\n",
+                                       cls->suspend);
                        }
                }
        }
@@ -452,6 +465,9 @@ int sysdev_resume(void)
 {
        struct sysdev_class *cls;
 
+       WARN_ONCE(!irqs_disabled(),
+               "Interrupts enabled while resuming system devices\n");
+
        pr_debug("Resuming System Devices\n");
 
        list_for_each_entry(cls, &system_kset->list, kset.kobj.entry) {
index f42fa50d35506276722ac6b21417468f847ba6c7..ac5e05a98b2f768a462541aa887a7ee1f6d18838 100644 (file)
@@ -438,7 +438,7 @@ source "drivers/s390/block/Kconfig"
 
 config XILINX_SYSACE
        tristate "Xilinx SystemACE support"
-       depends on 4xx
+       depends on 4xx || MICROBLAZE
        help
          Include support for the Xilinx SystemACE CompactFlash interface
 
index c0facaa55cf46ee96932d08ee453829735bdf939..43db3ea15b54936da4502526602c117876bd67fc 100644 (file)
@@ -254,7 +254,7 @@ static int index_to_minor(int index)
        return index << PART_BITS;
 }
 
-static int virtblk_probe(struct virtio_device *vdev)
+static int __devinit virtblk_probe(struct virtio_device *vdev)
 {
        struct virtio_blk *vblk;
        int err;
@@ -288,7 +288,7 @@ static int virtblk_probe(struct virtio_device *vdev)
        sg_init_table(vblk->sg, vblk->sg_elems);
 
        /* We expect one virtqueue, for output. */
-       vblk->vq = vdev->config->find_vq(vdev, 0, blk_done);
+       vblk->vq = virtio_find_single_vq(vdev, blk_done, "requests");
        if (IS_ERR(vblk->vq)) {
                err = PTR_ERR(vblk->vq);
                goto out_free_vblk;
@@ -388,14 +388,14 @@ out_put_disk:
 out_mempool:
        mempool_destroy(vblk->pool);
 out_free_vq:
-       vdev->config->del_vq(vblk->vq);
+       vdev->config->del_vqs(vdev);
 out_free_vblk:
        kfree(vblk);
 out:
        return err;
 }
 
-static void virtblk_remove(struct virtio_device *vdev)
+static void __devexit virtblk_remove(struct virtio_device *vdev)
 {
        struct virtio_blk *vblk = vdev->priv;
 
@@ -409,7 +409,7 @@ static void virtblk_remove(struct virtio_device *vdev)
        blk_cleanup_queue(vblk->disk->queue);
        put_disk(vblk->disk);
        mempool_destroy(vblk->pool);
-       vdev->config->del_vq(vblk->vq);
+       vdev->config->del_vqs(vdev);
        kfree(vblk);
 }
 
index 02ecfd5fa61c56f17ed658e3759e525794f53d81..b1e9652c0d9dfe4eec20147a25110cb8ca36b46d 100644 (file)
@@ -906,7 +906,7 @@ config DTLK
 
 config XILINX_HWICAP
        tristate "Xilinx HWICAP Support"
-       depends on XILINX_VIRTEX
+       depends on XILINX_VIRTEX || MICROBLAZE
        help
          This option enables support for Xilinx Internal Configuration
          Access Port (ICAP) driver.  The ICAP is used on Xilinx Virtex
index 3686912427ba3ebdba483a22b1f2c131b68e4a80..7a748fa0dfceb00ae93de59d4b5987ebfaff931d 100644 (file)
 #define PCI_DEVICE_ID_INTEL_G45_IG          0x2E22
 #define PCI_DEVICE_ID_INTEL_G41_HB          0x2E30
 #define PCI_DEVICE_ID_INTEL_G41_IG          0x2E32
+#define PCI_DEVICE_ID_INTEL_IGDNG_D_HB     0x0040
+#define PCI_DEVICE_ID_INTEL_IGDNG_D_IG     0x0042
+#define PCI_DEVICE_ID_INTEL_IGDNG_M_HB     0x0044
+#define PCI_DEVICE_ID_INTEL_IGDNG_M_IG     0x0046
 
 /* cover 915 and 945 variants */
 #define IS_I915 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_E7221_HB || \
@@ -75,7 +79,9 @@
                agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q45_HB || \
                agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G45_HB || \
                agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_GM45_HB || \
-               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G41_HB)
+               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G41_HB || \
+               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_D_HB || \
+               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_M_HB)
 
 extern int agp_memory_reserved;
 
@@ -1211,6 +1217,8 @@ static void intel_i965_get_gtt_range(int *gtt_offset, int *gtt_size)
        case PCI_DEVICE_ID_INTEL_Q45_HB:
        case PCI_DEVICE_ID_INTEL_G45_HB:
        case PCI_DEVICE_ID_INTEL_G41_HB:
+       case PCI_DEVICE_ID_INTEL_IGDNG_D_HB:
+       case PCI_DEVICE_ID_INTEL_IGDNG_M_HB:
                *gtt_offset = *gtt_size = MB(2);
                break;
        default:
@@ -2186,6 +2194,10 @@ static const struct intel_driver_description {
            "G45/G43", NULL, &intel_i965_driver },
        { PCI_DEVICE_ID_INTEL_G41_HB, PCI_DEVICE_ID_INTEL_G41_IG, 0,
            "G41", NULL, &intel_i965_driver },
+       { PCI_DEVICE_ID_INTEL_IGDNG_D_HB, PCI_DEVICE_ID_INTEL_IGDNG_D_IG, 0,
+           "IGDNG/D", NULL, &intel_i965_driver },
+       { PCI_DEVICE_ID_INTEL_IGDNG_M_HB, PCI_DEVICE_ID_INTEL_IGDNG_M_IG, 0,
+           "IGDNG/M", NULL, &intel_i965_driver },
        { 0, 0, 0, NULL, NULL, NULL }
 };
 
@@ -2387,6 +2399,8 @@ static struct pci_device_id agp_intel_pci_table[] = {
        ID(PCI_DEVICE_ID_INTEL_Q45_HB),
        ID(PCI_DEVICE_ID_INTEL_G45_HB),
        ID(PCI_DEVICE_ID_INTEL_G41_HB),
+       ID(PCI_DEVICE_ID_INTEL_IGDNG_D_HB),
+       ID(PCI_DEVICE_ID_INTEL_IGDNG_M_HB),
        { }
 };
 
index 86e83f883139e154d8f8621b2aa6650468ed74d8..32216b623248497b1bade240792aa0e77a0c080e 100644 (file)
@@ -35,13 +35,13 @@ static DECLARE_COMPLETION(have_data);
 
 static void random_recv_done(struct virtqueue *vq)
 {
-       int len;
+       unsigned int len;
 
        /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */
        if (!vq->vq_ops->get_buf(vq, &len))
                return;
 
-       data_left = len / sizeof(random_data[0]);
+       data_left += len;
        complete(&have_data);
 }
 
@@ -49,7 +49,7 @@ static void register_buffer(void)
 {
        struct scatterlist sg;
 
-       sg_init_one(&sg, random_data, RANDOM_DATA_SIZE);
+       sg_init_one(&sg, random_data+data_left, RANDOM_DATA_SIZE-data_left);
        /* There should always be room for one buffer. */
        if (vq->vq_ops->add_buf(vq, &sg, 0, 1, random_data) != 0)
                BUG();
@@ -59,24 +59,32 @@ static void register_buffer(void)
 /* At least we don't udelay() in a loop like some other drivers. */
 static int virtio_data_present(struct hwrng *rng, int wait)
 {
-       if (data_left)
+       if (data_left >= sizeof(u32))
                return 1;
 
+again:
        if (!wait)
                return 0;
 
        wait_for_completion(&have_data);
+
+       /* Not enough?  Re-register. */
+       if (unlikely(data_left < sizeof(u32))) {
+               register_buffer();
+               goto again;
+       }
+
        return 1;
 }
 
 /* virtio_data_present() must have succeeded before this is called. */
 static int virtio_data_read(struct hwrng *rng, u32 *data)
 {
-       BUG_ON(!data_left);
-
-       *data = random_data[--data_left];
+       BUG_ON(data_left < sizeof(u32));
+       data_left -= sizeof(u32);
+       *data = random_data[data_left / 4];
 
-       if (!data_left) {
+       if (data_left < sizeof(u32)) {
                init_completion(&have_data);
                register_buffer();
        }
@@ -94,13 +102,13 @@ static int virtrng_probe(struct virtio_device *vdev)
        int err;
 
        /* We expect a single virtqueue. */
-       vq = vdev->config->find_vq(vdev, 0, random_recv_done);
+       vq = virtio_find_single_vq(vdev, random_recv_done, "input");
        if (IS_ERR(vq))
                return PTR_ERR(vq);
 
        err = hwrng_register(&virtio_hwrng);
        if (err) {
-               vdev->config->del_vq(vq);
+               vdev->config->del_vqs(vdev);
                return err;
        }
 
@@ -112,7 +120,7 @@ static void virtrng_remove(struct virtio_device *vdev)
 {
        vdev->config->reset(vdev);
        hwrng_unregister(&virtio_hwrng);
-       vdev->config->del_vq(vq);
+       vdev->config->del_vqs(vdev);
 }
 
 static struct virtio_device_id id_table[] = {
index ff6f5a4b58fb5acd25622ad4fdd1b1a9c45b15f0..c74dacfa67950f870dcc4ffd7a71fa18dc915b27 100644 (file)
@@ -188,6 +188,9 @@ static void hvc_handle_input(struct virtqueue *vq)
  * Finally we put our input buffer in the input queue, ready to receive. */
 static int __devinit virtcons_probe(struct virtio_device *dev)
 {
+       vq_callback_t *callbacks[] = { hvc_handle_input, NULL};
+       const char *names[] = { "input", "output" };
+       struct virtqueue *vqs[2];
        int err;
 
        vdev = dev;
@@ -199,20 +202,15 @@ static int __devinit virtcons_probe(struct virtio_device *dev)
                goto fail;
        }
 
-       /* Find the input queue. */
+       /* Find the queues. */
        /* FIXME: This is why we want to wean off hvc: we do nothing
         * when input comes in. */
-       in_vq = vdev->config->find_vq(vdev, 0, hvc_handle_input);
-       if (IS_ERR(in_vq)) {
-               err = PTR_ERR(in_vq);
+       err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names);
+       if (err)
                goto free;
-       }
 
-       out_vq = vdev->config->find_vq(vdev, 1, NULL);
-       if (IS_ERR(out_vq)) {
-               err = PTR_ERR(out_vq);
-               goto free_in_vq;
-       }
+       in_vq = vqs[0];
+       out_vq = vqs[1];
 
        /* Start using the new console output. */
        virtio_cons.get_chars = get_chars;
@@ -233,17 +231,15 @@ static int __devinit virtcons_probe(struct virtio_device *dev)
        hvc = hvc_alloc(0, 0, &virtio_cons, PAGE_SIZE);
        if (IS_ERR(hvc)) {
                err = PTR_ERR(hvc);
-               goto free_out_vq;
+               goto free_vqs;
        }
 
        /* Register the input buffer the first time. */
        add_inbuf();
        return 0;
 
-free_out_vq:
-       vdev->config->del_vq(out_vq);
-free_in_vq:
-       vdev->config->del_vq(in_vq);
+free_vqs:
+       vdev->config->del_vqs(vdev);
 free:
        kfree(inbuf);
 fail:
index de9ebee8657b85ead0877f2f82037d80bcb0a0bc..c796a86ab7f36bbff85998425b31d78fb2417d32 100644 (file)
 #include <linux/io.h>
 #include <asm/system.h>
 #include <linux/uaccess.h>
-#include <linux/kmemleak.h>
 
 #define MAX_NR_CON_DRIVER 16
 
index edb02530e461af4f91e442cdd581f91aa934c588..11f373971fa5fe8cbedee6a5fce9a29bb4d02a83 100644 (file)
@@ -69,7 +69,7 @@ comment "Memory mapped GPIO expanders:"
 
 config GPIO_XILINX
        bool "Xilinx GPIO support"
-       depends on PPC_OF
+       depends on PPC_OF || MICROBLAZE
        help
          Say yes here to support the Xilinx FPGA GPIO device
 
index 0411d912d82abb9a82b812eb5e1639029659c60a..80a257554b307a4bc0346e5f38e3c154ec22f4c5 100644 (file)
@@ -371,7 +371,8 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
        list->user_token = list->hash.key << PAGE_SHIFT;
        mutex_unlock(&dev->struct_mutex);
 
-       list->master = dev->primary->master;
+       if (!(map->flags & _DRM_DRIVER))
+               list->master = dev->primary->master;
        *maplist = list;
        return 0;
        }
index 6f6b26479d82281e98ff1f9998d5f472e6938201..801a0d0e08103095dec0861a7fe9967d3759a3f2 100644 (file)
@@ -589,85 +589,13 @@ int drm_do_probe_ddc_edid(struct i2c_adapter *adapter,
 }
 EXPORT_SYMBOL(drm_do_probe_ddc_edid);
 
-/**
- * Get EDID information.
- *
- * \param adapter : i2c device adaptor.
- * \param buf     : EDID data buffer to be filled
- * \param len     : EDID data buffer length
- * \return 0 on success or -1 on failure.
- *
- * Initialize DDC, then fetch EDID information
- * by calling drm_do_probe_ddc_edid function.
- */
-static int drm_ddc_read(struct i2c_adapter *adapter,
-                       unsigned char *buf, int len)
-{
-       struct i2c_algo_bit_data *algo_data = adapter->algo_data;
-       int i, j;
-       int ret = -1;
-
-       algo_data->setscl(algo_data->data, 1);
-
-       for (i = 0; i < 1; i++) {
-               /* For some old monitors we need the
-                * following process to initialize/stop DDC
-                */
-               algo_data->setsda(algo_data->data, 1);
-               msleep(13);
-
-               algo_data->setscl(algo_data->data, 1);
-               for (j = 0; j < 5; j++) {
-                       msleep(10);
-                       if (algo_data->getscl(algo_data->data))
-                               break;
-               }
-               if (j == 5)
-                       continue;
-
-               algo_data->setsda(algo_data->data, 0);
-               msleep(15);
-               algo_data->setscl(algo_data->data, 0);
-               msleep(15);
-               algo_data->setsda(algo_data->data, 1);
-               msleep(15);
-
-               /* Do the real work */
-               ret = drm_do_probe_ddc_edid(adapter, buf, len);
-               algo_data->setsda(algo_data->data, 0);
-               algo_data->setscl(algo_data->data, 0);
-               msleep(15);
-
-               algo_data->setscl(algo_data->data, 1);
-               for (j = 0; j < 10; j++) {
-                       msleep(10);
-                       if (algo_data->getscl(algo_data->data))
-                               break;
-               }
-
-               algo_data->setsda(algo_data->data, 1);
-               msleep(15);
-               algo_data->setscl(algo_data->data, 0);
-               algo_data->setsda(algo_data->data, 0);
-               if (ret == 0)
-                       break;
-       }
-       /* Release the DDC lines when done or the Apple Cinema HD display
-        * will switch off
-        */
-       algo_data->setsda(algo_data->data, 1);
-       algo_data->setscl(algo_data->data, 1);
-
-       return ret;
-}
-
 static int drm_ddc_read_edid(struct drm_connector *connector,
                             struct i2c_adapter *adapter,
                             char *buf, int len)
 {
        int ret;
 
-       ret = drm_ddc_read(adapter, buf, len);
+       ret = drm_do_probe_ddc_edid(adapter, buf, len);
        if (ret != 0) {
                dev_info(&connector->dev->pdev->dev, "%s: no EDID data\n",
                         drm_get_connector_name(connector));
index 4984aa89cf3ddb5e805f3f2f0a6f628d211ea895..ec43005100d978877167721b081d4c985f2dabdf 100644 (file)
@@ -133,7 +133,7 @@ drm_gem_object_alloc(struct drm_device *dev, size_t size)
 
        BUG_ON((size & (PAGE_SIZE - 1)) != 0);
 
-       obj = kcalloc(1, sizeof(*obj), GFP_KERNEL);
+       obj = kzalloc(sizeof(*obj), GFP_KERNEL);
 
        obj->dev = dev;
        obj->filp = shmem_file_setup("drm mm object", size, VM_NORESERVE);
index af539f7d87dd2639bafe0c1576e069d4c971da1e..ac35145c3e20b279e7b17c1bdf40e9f442fc4fe1 100644 (file)
@@ -62,6 +62,7 @@ int drm_ht_create(struct drm_open_hash *ht, unsigned int order)
        }
        return 0;
 }
+EXPORT_SYMBOL(drm_ht_create);
 
 void drm_ht_verbose_list(struct drm_open_hash *ht, unsigned long key)
 {
@@ -156,6 +157,7 @@ int drm_ht_just_insert_please(struct drm_open_hash *ht, struct drm_hash_item *it
        }
        return 0;
 }
+EXPORT_SYMBOL(drm_ht_just_insert_please);
 
 int drm_ht_find_item(struct drm_open_hash *ht, unsigned long key,
                     struct drm_hash_item **item)
@@ -169,6 +171,7 @@ int drm_ht_find_item(struct drm_open_hash *ht, unsigned long key,
        *item = hlist_entry(list, struct drm_hash_item, head);
        return 0;
 }
+EXPORT_SYMBOL(drm_ht_find_item);
 
 int drm_ht_remove_key(struct drm_open_hash *ht, unsigned long key)
 {
@@ -202,3 +205,4 @@ void drm_ht_remove(struct drm_open_hash *ht)
                ht->table = NULL;
        }
 }
+EXPORT_SYMBOL(drm_ht_remove);
index 367c590ffbba2d83b5f516d76cb6efacdd1baff1..7819fd930a515ce8840c2b7f0b11c9e33cc08b0c 100644 (file)
  */
 
 #include "drmP.h"
+#include "drm_mm.h"
 #include <linux/slab.h>
 
+#define MM_UNUSED_TARGET 4
+
 unsigned long drm_mm_tail_space(struct drm_mm *mm)
 {
        struct list_head *tail_node;
@@ -74,16 +77,62 @@ int drm_mm_remove_space_from_tail(struct drm_mm *mm, unsigned long size)
        return 0;
 }
 
+static struct drm_mm_node *drm_mm_kmalloc(struct drm_mm *mm, int atomic)
+{
+       struct drm_mm_node *child;
+
+       if (atomic)
+               child = kmalloc(sizeof(*child), GFP_ATOMIC);
+       else
+               child = kmalloc(sizeof(*child), GFP_KERNEL);
+
+       if (unlikely(child == NULL)) {
+               spin_lock(&mm->unused_lock);
+               if (list_empty(&mm->unused_nodes))
+                       child = NULL;
+               else {
+                       child =
+                           list_entry(mm->unused_nodes.next,
+                                      struct drm_mm_node, fl_entry);
+                       list_del(&child->fl_entry);
+                       --mm->num_unused;
+               }
+               spin_unlock(&mm->unused_lock);
+       }
+       return child;
+}
+
+int drm_mm_pre_get(struct drm_mm *mm)
+{
+       struct drm_mm_node *node;
+
+       spin_lock(&mm->unused_lock);
+       while (mm->num_unused < MM_UNUSED_TARGET) {
+               spin_unlock(&mm->unused_lock);
+               node = kmalloc(sizeof(*node), GFP_KERNEL);
+               spin_lock(&mm->unused_lock);
+
+               if (unlikely(node == NULL)) {
+                       int ret = (mm->num_unused < 2) ? -ENOMEM : 0;
+                       spin_unlock(&mm->unused_lock);
+                       return ret;
+               }
+               ++mm->num_unused;
+               list_add_tail(&node->fl_entry, &mm->unused_nodes);
+       }
+       spin_unlock(&mm->unused_lock);
+       return 0;
+}
+EXPORT_SYMBOL(drm_mm_pre_get);
 
 static int drm_mm_create_tail_node(struct drm_mm *mm,
-                           unsigned long start,
-                           unsigned long size)
+                                  unsigned long start,
+                                  unsigned long size, int atomic)
 {
        struct drm_mm_node *child;
 
-       child = (struct drm_mm_node *)
-               drm_alloc(sizeof(*child), DRM_MEM_MM);
-       if (!child)
+       child = drm_mm_kmalloc(mm, atomic);
+       if (unlikely(child == NULL))
                return -ENOMEM;
 
        child->free = 1;
@@ -97,8 +146,7 @@ static int drm_mm_create_tail_node(struct drm_mm *mm,
        return 0;
 }
 
-
-int drm_mm_add_space_to_tail(struct drm_mm *mm, unsigned long size)
+int drm_mm_add_space_to_tail(struct drm_mm *mm, unsigned long size, int atomic)
 {
        struct list_head *tail_node;
        struct drm_mm_node *entry;
@@ -106,20 +154,21 @@ int drm_mm_add_space_to_tail(struct drm_mm *mm, unsigned long size)
        tail_node = mm->ml_entry.prev;
        entry = list_entry(tail_node, struct drm_mm_node, ml_entry);
        if (!entry->free) {
-               return drm_mm_create_tail_node(mm, entry->start + entry->size, size);
+               return drm_mm_create_tail_node(mm, entry->start + entry->size,
+                                              size, atomic);
        }
        entry->size += size;
        return 0;
 }
 
 static struct drm_mm_node *drm_mm_split_at_start(struct drm_mm_node *parent,
-                                           unsigned long size)
+                                                unsigned long size,
+                                                int atomic)
 {
        struct drm_mm_node *child;
 
-       child = (struct drm_mm_node *)
-               drm_alloc(sizeof(*child), DRM_MEM_MM);
-       if (!child)
+       child = drm_mm_kmalloc(parent->mm, atomic);
+       if (unlikely(child == NULL))
                return NULL;
 
        INIT_LIST_HEAD(&child->fl_entry);
@@ -151,8 +200,9 @@ struct drm_mm_node *drm_mm_get_block(struct drm_mm_node * parent,
                tmp = parent->start % alignment;
 
        if (tmp) {
-               align_splitoff = drm_mm_split_at_start(parent, alignment - tmp);
-               if (!align_splitoff)
+               align_splitoff =
+                   drm_mm_split_at_start(parent, alignment - tmp, 0);
+               if (unlikely(align_splitoff == NULL))
                        return NULL;
        }
 
@@ -161,7 +211,7 @@ struct drm_mm_node *drm_mm_get_block(struct drm_mm_node * parent,
                parent->free = 0;
                return parent;
        } else {
-               child = drm_mm_split_at_start(parent, size);
+               child = drm_mm_split_at_start(parent, size, 0);
        }
 
        if (align_splitoff)
@@ -169,14 +219,49 @@ struct drm_mm_node *drm_mm_get_block(struct drm_mm_node * parent,
 
        return child;
 }
+
 EXPORT_SYMBOL(drm_mm_get_block);
 
+struct drm_mm_node *drm_mm_get_block_atomic(struct drm_mm_node *parent,
+                                           unsigned long size,
+                                           unsigned alignment)
+{
+
+       struct drm_mm_node *align_splitoff = NULL;
+       struct drm_mm_node *child;
+       unsigned tmp = 0;
+
+       if (alignment)
+               tmp = parent->start % alignment;
+
+       if (tmp) {
+               align_splitoff =
+                   drm_mm_split_at_start(parent, alignment - tmp, 1);
+               if (unlikely(align_splitoff == NULL))
+                       return NULL;
+       }
+
+       if (parent->size == size) {
+               list_del_init(&parent->fl_entry);
+               parent->free = 0;
+               return parent;
+       } else {
+               child = drm_mm_split_at_start(parent, size, 1);
+       }
+
+       if (align_splitoff)
+               drm_mm_put_block(align_splitoff);
+
+       return child;
+}
+EXPORT_SYMBOL(drm_mm_get_block_atomic);
+
 /*
  * Put a block. Merge with the previous and / or next block if they are free.
  * Otherwise add to the free stack.
  */
 
-void drm_mm_put_block(struct drm_mm_node * cur)
+void drm_mm_put_block(struct drm_mm_node *cur)
 {
 
        struct drm_mm *mm = cur->mm;
@@ -188,21 +273,27 @@ void drm_mm_put_block(struct drm_mm_node * cur)
        int merged = 0;
 
        if (cur_head->prev != root_head) {
-               prev_node = list_entry(cur_head->prev, struct drm_mm_node, ml_entry);
+               prev_node =
+                   list_entry(cur_head->prev, struct drm_mm_node, ml_entry);
                if (prev_node->free) {
                        prev_node->size += cur->size;
                        merged = 1;
                }
        }
        if (cur_head->next != root_head) {
-               next_node = list_entry(cur_head->next, struct drm_mm_node, ml_entry);
+               next_node =
+                   list_entry(cur_head->next, struct drm_mm_node, ml_entry);
                if (next_node->free) {
                        if (merged) {
                                prev_node->size += next_node->size;
                                list_del(&next_node->ml_entry);
                                list_del(&next_node->fl_entry);
-                               drm_free(next_node, sizeof(*next_node),
-                                        DRM_MEM_MM);
+                               if (mm->num_unused < MM_UNUSED_TARGET) {
+                                       list_add(&next_node->fl_entry,
+                                                &mm->unused_nodes);
+                                       ++mm->num_unused;
+                               } else
+                                       kfree(next_node);
                        } else {
                                next_node->size += cur->size;
                                next_node->start = cur->start;
@@ -215,14 +306,19 @@ void drm_mm_put_block(struct drm_mm_node * cur)
                list_add(&cur->fl_entry, &mm->fl_entry);
        } else {
                list_del(&cur->ml_entry);
-               drm_free(cur, sizeof(*cur), DRM_MEM_MM);
+               if (mm->num_unused < MM_UNUSED_TARGET) {
+                       list_add(&cur->fl_entry, &mm->unused_nodes);
+                       ++mm->num_unused;
+               } else
+                       kfree(cur);
        }
 }
+
 EXPORT_SYMBOL(drm_mm_put_block);
 
-struct drm_mm_node *drm_mm_search_free(const struct drm_mm * mm,
-                                 unsigned long size,
-                                 unsigned alignment, int best_match)
+struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm,
+                                      unsigned long size,
+                                      unsigned alignment, int best_match)
 {
        struct list_head *list;
        const struct list_head *free_stack = &mm->fl_entry;
@@ -247,7 +343,6 @@ struct drm_mm_node *drm_mm_search_free(const struct drm_mm * mm,
                                wasted += alignment - tmp;
                }
 
-
                if (entry->size >= size + wasted) {
                        if (!best_match)
                                return entry;
@@ -260,6 +355,7 @@ struct drm_mm_node *drm_mm_search_free(const struct drm_mm * mm,
 
        return best;
 }
+EXPORT_SYMBOL(drm_mm_search_free);
 
 int drm_mm_clean(struct drm_mm * mm)
 {
@@ -267,14 +363,17 @@ int drm_mm_clean(struct drm_mm * mm)
 
        return (head->next->next == head);
 }
-EXPORT_SYMBOL(drm_mm_search_free);
+EXPORT_SYMBOL(drm_mm_clean);
 
 int drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size)
 {
        INIT_LIST_HEAD(&mm->ml_entry);
        INIT_LIST_HEAD(&mm->fl_entry);
+       INIT_LIST_HEAD(&mm->unused_nodes);
+       mm->num_unused = 0;
+       spin_lock_init(&mm->unused_lock);
 
-       return drm_mm_create_tail_node(mm, start, size);
+       return drm_mm_create_tail_node(mm, start, size, 0);
 }
 EXPORT_SYMBOL(drm_mm_init);
 
@@ -282,6 +381,7 @@ void drm_mm_takedown(struct drm_mm * mm)
 {
        struct list_head *bnode = mm->fl_entry.next;
        struct drm_mm_node *entry;
+       struct drm_mm_node *next;
 
        entry = list_entry(bnode, struct drm_mm_node, fl_entry);
 
@@ -293,7 +393,16 @@ void drm_mm_takedown(struct drm_mm * mm)
 
        list_del(&entry->fl_entry);
        list_del(&entry->ml_entry);
+       kfree(entry);
+
+       spin_lock(&mm->unused_lock);
+       list_for_each_entry_safe(entry, next, &mm->unused_nodes, fl_entry) {
+               list_del(&entry->fl_entry);
+               kfree(entry);
+               --mm->num_unused;
+       }
+       spin_unlock(&mm->unused_lock);
 
-       drm_free(entry, sizeof(*entry), DRM_MEM_MM);
+       BUG_ON(mm->num_unused != 0);
 }
 EXPORT_SYMBOL(drm_mm_takedown);
index c9b80fdd4630a386f5acda7c35a4610f6c13c9f3..54f492a488a93c68387bc388a6579c92edb7f988 100644 (file)
@@ -38,6 +38,7 @@
 #include "drm.h"
 #include "drm_crtc.h"
 
+#define DRM_MODESET_DEBUG      "drm_mode"
 /**
  * drm_mode_debug_printmodeline - debug print a mode
  * @dev: DRM device
  */
 void drm_mode_debug_printmodeline(struct drm_display_mode *mode)
 {
-       DRM_DEBUG("Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n",
-                 mode->base.id, mode->name, mode->vrefresh, mode->clock,
-                 mode->hdisplay, mode->hsync_start,
-                 mode->hsync_end, mode->htotal,
-                 mode->vdisplay, mode->vsync_start,
-                 mode->vsync_end, mode->vtotal, mode->type, mode->flags);
+       DRM_DEBUG_MODE(DRM_MODESET_DEBUG,
+               "Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n",
+               mode->base.id, mode->name, mode->vrefresh, mode->clock,
+               mode->hdisplay, mode->hsync_start,
+               mode->hsync_end, mode->htotal,
+               mode->vdisplay, mode->vsync_start,
+               mode->vsync_end, mode->vtotal, mode->type, mode->flags);
 }
 EXPORT_SYMBOL(drm_mode_debug_printmodeline);
 
@@ -401,7 +403,9 @@ void drm_mode_prune_invalid(struct drm_device *dev,
                        list_del(&mode->head);
                        if (verbose) {
                                drm_mode_debug_printmodeline(mode);
-                               DRM_DEBUG("Not using %s mode %d\n", mode->name, mode->status);
+                               DRM_DEBUG_MODE(DRM_MODESET_DEBUG,
+                                       "Not using %s mode %d\n",
+                                       mode->name, mode->status);
                        }
                        drm_mode_destroy(dev, mode);
                }
index b9631e3a1ea69f421177cfead3e1c2ce3be9993b..89050684fe0d2c7b33a2fdf0a31287c5955e5778 100644 (file)
@@ -51,7 +51,22 @@ struct idr drm_minors_idr;
 struct class *drm_class;
 struct proc_dir_entry *drm_proc_root;
 struct dentry *drm_debugfs_root;
-
+void drm_ut_debug_printk(unsigned int request_level,
+                        const char *prefix,
+                        const char *function_name,
+                        const char *format, ...)
+{
+       va_list args;
+
+       if (drm_debug & request_level) {
+               if (function_name)
+                       printk(KERN_DEBUG "[%s:%s], ", prefix, function_name);
+               va_start(args, format);
+               vprintk(format, args);
+               va_end(args);
+       }
+}
+EXPORT_SYMBOL(drm_ut_debug_printk);
 static int drm_minor_get_id(struct drm_device *dev, int type)
 {
        int new_id;
index 0ccb63ee50ee5fea5b2232e6b3b481c108d56f03..1a60626f6803733d656f7bb6dcb8b7c8a33e697c 100644 (file)
@@ -33,6 +33,8 @@
 #include "i915_drm.h"
 #include "i915_drv.h"
 
+#define I915_DRV       "i915_drv"
+
 /* Really want an OS-independent resettable timer.  Would like to have
  * this loop run for (eg) 3 sec, but have the timer reset every time
  * the head pointer changes, so that EBUSY only happens if the ring
@@ -99,7 +101,7 @@ static int i915_init_phys_hws(struct drm_device *dev)
        memset(dev_priv->hw_status_page, 0, PAGE_SIZE);
 
        I915_WRITE(HWS_PGA, dev_priv->dma_status_page);
-       DRM_DEBUG("Enabled hardware status page\n");
+       DRM_DEBUG_DRIVER(I915_DRV, "Enabled hardware status page\n");
        return 0;
 }
 
@@ -185,7 +187,8 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
                master_priv->sarea_priv = (drm_i915_sarea_t *)
                        ((u8 *)master_priv->sarea->handle + init->sarea_priv_offset);
        } else {
-               DRM_DEBUG("sarea not found assuming DRI2 userspace\n");
+               DRM_DEBUG_DRIVER(I915_DRV,
+                               "sarea not found assuming DRI2 userspace\n");
        }
 
        if (init->ring_size != 0) {
@@ -235,7 +238,7 @@ static int i915_dma_resume(struct drm_device * dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
 
-       DRM_DEBUG("%s\n", __func__);
+       DRM_DEBUG_DRIVER(I915_DRV, "%s\n", __func__);
 
        if (dev_priv->ring.map.handle == NULL) {
                DRM_ERROR("can not ioremap virtual address for"
@@ -248,13 +251,14 @@ static int i915_dma_resume(struct drm_device * dev)
                DRM_ERROR("Can not find hardware status page\n");
                return -EINVAL;
        }
-       DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page);
+       DRM_DEBUG_DRIVER(I915_DRV, "hw status page @ %p\n",
+                               dev_priv->hw_status_page);
 
        if (dev_priv->status_gfx_addr != 0)
                I915_WRITE(HWS_PGA, dev_priv->status_gfx_addr);
        else
                I915_WRITE(HWS_PGA, dev_priv->dma_status_page);
-       DRM_DEBUG("Enabled hardware status page\n");
+       DRM_DEBUG_DRIVER(I915_DRV, "Enabled hardware status page\n");
 
        return 0;
 }
@@ -548,10 +552,10 @@ static int i915_dispatch_flip(struct drm_device * dev)
        if (!master_priv->sarea_priv)
                return -EINVAL;
 
-       DRM_DEBUG("%s: page=%d pfCurrentPage=%d\n",
-                 __func__,
-                 dev_priv->current_page,
-                 master_priv->sarea_priv->pf_current_page);
+       DRM_DEBUG_DRIVER(I915_DRV, "%s: page=%d pfCurrentPage=%d\n",
+                         __func__,
+                        dev_priv->current_page,
+                        master_priv->sarea_priv->pf_current_page);
 
        i915_kernel_lost_context(dev);
 
@@ -629,8 +633,9 @@ static int i915_batchbuffer(struct drm_device *dev, void *data,
                return -EINVAL;
        }
 
-       DRM_DEBUG("i915 batchbuffer, start %x used %d cliprects %d\n",
-                 batch->start, batch->used, batch->num_cliprects);
+       DRM_DEBUG_DRIVER(I915_DRV,
+                       "i915 batchbuffer, start %x used %d cliprects %d\n",
+                       batch->start, batch->used, batch->num_cliprects);
 
        RING_LOCK_TEST_WITH_RETURN(dev, file_priv);
 
@@ -678,8 +683,9 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data,
        void *batch_data;
        int ret;
 
-       DRM_DEBUG("i915 cmdbuffer, buf %p sz %d cliprects %d\n",
-                 cmdbuf->buf, cmdbuf->sz, cmdbuf->num_cliprects);
+       DRM_DEBUG_DRIVER(I915_DRV,
+                       "i915 cmdbuffer, buf %p sz %d cliprects %d\n",
+                       cmdbuf->buf, cmdbuf->sz, cmdbuf->num_cliprects);
 
        RING_LOCK_TEST_WITH_RETURN(dev, file_priv);
 
@@ -734,7 +740,7 @@ static int i915_flip_bufs(struct drm_device *dev, void *data,
 {
        int ret;
 
-       DRM_DEBUG("%s\n", __func__);
+       DRM_DEBUG_DRIVER(I915_DRV, "%s\n", __func__);
 
        RING_LOCK_TEST_WITH_RETURN(dev, file_priv);
 
@@ -777,7 +783,8 @@ static int i915_getparam(struct drm_device *dev, void *data,
                value = dev_priv->num_fence_regs - dev_priv->fence_reg_start;
                break;
        default:
-               DRM_DEBUG("Unknown parameter %d\n", param->param);
+               DRM_DEBUG_DRIVER(I915_DRV, "Unknown parameter %d\n",
+                                       param->param);
                return -EINVAL;
        }
 
@@ -817,7 +824,8 @@ static int i915_setparam(struct drm_device *dev, void *data,
                dev_priv->fence_reg_start = param->value;
                break;
        default:
-               DRM_DEBUG("unknown parameter %d\n", param->param);
+               DRM_DEBUG_DRIVER(I915_DRV, "unknown parameter %d\n",
+                                       param->param);
                return -EINVAL;
        }
 
@@ -865,9 +873,10 @@ static int i915_set_status_page(struct drm_device *dev, void *data,
 
        memset(dev_priv->hw_status_page, 0, PAGE_SIZE);
        I915_WRITE(HWS_PGA, dev_priv->status_gfx_addr);
-       DRM_DEBUG("load hws HWS_PGA with gfx mem 0x%x\n",
-                       dev_priv->status_gfx_addr);
-       DRM_DEBUG("load hws at %p\n", dev_priv->hw_status_page);
+       DRM_DEBUG_DRIVER(I915_DRV, "load hws HWS_PGA with gfx mem 0x%x\n",
+                               dev_priv->status_gfx_addr);
+       DRM_DEBUG_DRIVER(I915_DRV, "load hws at %p\n",
+                               dev_priv->hw_status_page);
        return 0;
 }
 
@@ -922,7 +931,7 @@ static int i915_probe_agp(struct drm_device *dev, unsigned long *aperture_size,
         * Some of the preallocated space is taken by the GTT
         * and popup.  GTT is 1K per MB of aperture size, and popup is 4K.
         */
-       if (IS_G4X(dev) || IS_IGD(dev))
+       if (IS_G4X(dev) || IS_IGD(dev) || IS_IGDNG(dev))
                overhead = 4096;
        else
                overhead = (*aperture_size / 1024) + 4096;
@@ -1153,8 +1162,11 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
 #endif
 
        dev->driver->get_vblank_counter = i915_get_vblank_counter;
-       if (IS_GM45(dev))
+       dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
+       if (IS_G4X(dev) || IS_IGDNG(dev)) {
+               dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */
                dev->driver->get_vblank_counter = gm45_get_vblank_counter;
+       }
 
        i915_gem_load(dev);
 
@@ -1198,7 +1210,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
        }
 
        /* Must be done after probing outputs */
-       intel_opregion_init(dev, 0);
+       /* FIXME: verify on IGDNG */
+       if (!IS_IGDNG(dev))
+               intel_opregion_init(dev, 0);
 
        return 0;
 
@@ -1232,7 +1246,8 @@ int i915_driver_unload(struct drm_device *dev)
        if (dev_priv->regs != NULL)
                iounmap(dev_priv->regs);
 
-       intel_opregion_free(dev, 0);
+       if (!IS_IGDNG(dev))
+               intel_opregion_free(dev, 0);
 
        if (drm_core_check_feature(dev, DRIVER_MODESET)) {
                intel_modeset_cleanup(dev);
@@ -1256,7 +1271,7 @@ int i915_driver_open(struct drm_device *dev, struct drm_file *file_priv)
 {
        struct drm_i915_file_private *i915_file_priv;
 
-       DRM_DEBUG("\n");
+       DRM_DEBUG_DRIVER(I915_DRV, "\n");
        i915_file_priv = (struct drm_i915_file_private *)
            drm_alloc(sizeof(*i915_file_priv), DRM_MEM_FILES);
 
@@ -1265,8 +1280,7 @@ int i915_driver_open(struct drm_device *dev, struct drm_file *file_priv)
 
        file_priv->driver_priv = i915_file_priv;
 
-       i915_file_priv->mm.last_gem_seqno = 0;
-       i915_file_priv->mm.last_gem_throttle_seqno = 0;
+       INIT_LIST_HEAD(&i915_file_priv->mm.request_list);
 
        return 0;
 }
@@ -1303,6 +1317,7 @@ void i915_driver_lastclose(struct drm_device * dev)
 void i915_driver_preclose(struct drm_device * dev, struct drm_file *file_priv)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
+       i915_gem_release(dev, file_priv);
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                i915_mem_release(dev, file_priv, dev_priv->agp_heap);
 }
index c431fa54bbb55b4d5d8afc32d9ea1468d05f66e5..8ef6bcec211bbc3f6440ba79b198a3d53e633eb8 100644 (file)
@@ -126,6 +126,13 @@ struct drm_i915_fence_reg {
        struct drm_gem_object *obj;
 };
 
+struct sdvo_device_mapping {
+       u8 dvo_port;
+       u8 slave_addr;
+       u8 dvo_wiring;
+       u8 initialized;
+};
+
 typedef struct drm_i915_private {
        struct drm_device *dev;
 
@@ -143,6 +150,8 @@ typedef struct drm_i915_private {
        drm_local_map_t hws_map;
        struct drm_gem_object *hws_obj;
 
+       struct resource mch_res;
+
        unsigned int cpp;
        int back_offset;
        int front_offset;
@@ -158,6 +167,11 @@ typedef struct drm_i915_private {
        /** Cached value of IMR to avoid reads in updating the bitfield */
        u32 irq_mask_reg;
        u32 pipestat[2];
+       /** splitted irq regs for graphics and display engine on IGDNG,
+           irq_mask_reg is still used for display irq. */
+       u32 gt_irq_mask_reg;
+       u32 gt_irq_enable_reg;
+       u32 de_irq_enable_reg;
 
        u32 hotplug_supported_mask;
        struct work_struct hotplug_work;
@@ -285,6 +299,13 @@ typedef struct drm_i915_private {
        u8 saveDACMASK;
        u8 saveCR[37];
        uint64_t saveFENCE[16];
+       u32 saveCURACNTR;
+       u32 saveCURAPOS;
+       u32 saveCURABASE;
+       u32 saveCURBCNTR;
+       u32 saveCURBPOS;
+       u32 saveCURBBASE;
+       u32 saveCURSIZE;
 
        struct {
                struct drm_mm gtt_space;
@@ -382,6 +403,7 @@ typedef struct drm_i915_private {
                /* storage for physical objects */
                struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT];
        } mm;
+       struct sdvo_device_mapping sdvo_mappings[2];
 } drm_i915_private_t;
 
 /** driver private structure attached to each drm_gem_object */
@@ -491,13 +513,16 @@ struct drm_i915_gem_request {
        /** Time at which this request was emitted, in jiffies. */
        unsigned long emitted_jiffies;
 
+       /** global list entry for this request */
        struct list_head list;
+
+       /** file_priv list entry for this request */
+       struct list_head client_list;
 };
 
 struct drm_i915_file_private {
        struct {
-               uint32_t last_gem_seqno;
-               uint32_t last_gem_throttle_seqno;
+               struct list_head request_list;
        } mm;
 };
 
@@ -642,6 +667,7 @@ void i915_gem_detach_phys_object(struct drm_device *dev,
 void i915_gem_free_all_phys_object(struct drm_device *dev);
 int i915_gem_object_get_pages(struct drm_gem_object *obj);
 void i915_gem_object_put_pages(struct drm_gem_object *obj);
+void i915_gem_release(struct drm_device * dev, struct drm_file *file_priv);
 
 /* i915_gem_tiling.c */
 void i915_gem_detect_bit_6_swizzle(struct drm_device *dev);
@@ -785,7 +811,9 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
                       (dev)->pci_device == 0x2E02 || \
                       (dev)->pci_device == 0x2E12 || \
                       (dev)->pci_device == 0x2E22 || \
-                      (dev)->pci_device == 0x2E32)
+                      (dev)->pci_device == 0x2E32 || \
+                      (dev)->pci_device == 0x0042 || \
+                      (dev)->pci_device == 0x0046)
 
 #define IS_I965GM(dev) ((dev)->pci_device == 0x2A02 || \
                        (dev)->pci_device == 0x2A12)
@@ -807,20 +835,26 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
                        (dev)->pci_device == 0x29D2 ||  \
                        (IS_IGD(dev)))
 
+#define IS_IGDNG_D(dev)        ((dev)->pci_device == 0x0042)
+#define IS_IGDNG_M(dev)        ((dev)->pci_device == 0x0046)
+#define IS_IGDNG(dev)  (IS_IGDNG_D(dev) || IS_IGDNG_M(dev))
+
 #define IS_I9XX(dev) (IS_I915G(dev) || IS_I915GM(dev) || IS_I945G(dev) || \
-                     IS_I945GM(dev) || IS_I965G(dev) || IS_G33(dev))
+                     IS_I945GM(dev) || IS_I965G(dev) || IS_G33(dev) || \
+                     IS_IGDNG(dev))
 
 #define IS_MOBILE(dev) (IS_I830(dev) || IS_I85X(dev) || IS_I915GM(dev) || \
                        IS_I945GM(dev) || IS_I965GM(dev) || IS_GM45(dev) || \
-                       IS_IGD(dev))
+                       IS_IGD(dev) || IS_IGDNG_M(dev))
 
-#define I915_NEED_GFX_HWS(dev) (IS_G33(dev) || IS_GM45(dev) || IS_G4X(dev))
+#define I915_NEED_GFX_HWS(dev) (IS_G33(dev) || IS_GM45(dev) || IS_G4X(dev) || \
+                               IS_IGDNG(dev))
 /* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte
  * rows, which changed the alignment requirements and fence programming.
  */
 #define HAS_128_BYTE_Y_TILING(dev) (IS_I9XX(dev) && !(IS_I915G(dev) || \
                                                      IS_I915GM(dev)))
-#define SUPPORTS_INTEGRATED_HDMI(dev)  (IS_G4X(dev))
+#define SUPPORTS_INTEGRATED_HDMI(dev)  (IS_G4X(dev) || IS_IGDNG(dev))
 #define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_I965G(dev))
 
 #define PRIMARY_RINGBUFFER_SIZE         (128*1024)
index 39f5c658ef5e7c9754444672e86c166cd12a0191..c0ae6bbbd9b522380d5201a2a5bce7d505ec2b04 100644 (file)
@@ -989,10 +989,10 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
                return -ENODEV;
 
        /* Only handle setting domains to types used by the CPU. */
-       if (write_domain & ~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT))
+       if (write_domain & I915_GEM_GPU_DOMAINS)
                return -EINVAL;
 
-       if (read_domains & ~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT))
+       if (read_domains & I915_GEM_GPU_DOMAINS)
                return -EINVAL;
 
        /* Having something in the write domain implies it's in the read
@@ -1481,14 +1481,19 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj)
  * Returned sequence numbers are nonzero on success.
  */
 static uint32_t
-i915_add_request(struct drm_device *dev, uint32_t flush_domains)
+i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
+                uint32_t flush_domains)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_i915_file_private *i915_file_priv = NULL;
        struct drm_i915_gem_request *request;
        uint32_t seqno;
        int was_empty;
        RING_LOCALS;
 
+       if (file_priv != NULL)
+               i915_file_priv = file_priv->driver_priv;
+
        request = drm_calloc(1, sizeof(*request), DRM_MEM_DRIVER);
        if (request == NULL)
                return 0;
@@ -1515,6 +1520,12 @@ i915_add_request(struct drm_device *dev, uint32_t flush_domains)
        request->emitted_jiffies = jiffies;
        was_empty = list_empty(&dev_priv->mm.request_list);
        list_add_tail(&request->list, &dev_priv->mm.request_list);
+       if (i915_file_priv) {
+               list_add_tail(&request->client_list,
+                             &i915_file_priv->mm.request_list);
+       } else {
+               INIT_LIST_HEAD(&request->client_list);
+       }
 
        /* Associate any objects on the flushing list matching the write
         * domain we're flushing with our flush.
@@ -1664,6 +1675,7 @@ i915_gem_retire_requests(struct drm_device *dev)
                        i915_gem_retire_request(dev, request);
 
                        list_del(&request->list);
+                       list_del(&request->client_list);
                        drm_free(request, sizeof(*request), DRM_MEM_DRIVER);
                } else
                        break;
@@ -1702,7 +1714,10 @@ i915_wait_request(struct drm_device *dev, uint32_t seqno)
        BUG_ON(seqno == 0);
 
        if (!i915_seqno_passed(i915_get_gem_seqno(dev), seqno)) {
-               ier = I915_READ(IER);
+               if (IS_IGDNG(dev))
+                       ier = I915_READ(DEIER) | I915_READ(GTIER);
+               else
+                       ier = I915_READ(IER);
                if (!ier) {
                        DRM_ERROR("something (likely vbetool) disabled "
                                  "interrupts, re-enabling\n");
@@ -1754,8 +1769,7 @@ i915_gem_flush(struct drm_device *dev,
        if (flush_domains & I915_GEM_DOMAIN_CPU)
                drm_agp_chipset_flush(dev);
 
-       if ((invalidate_domains | flush_domains) & ~(I915_GEM_DOMAIN_CPU |
-                                                    I915_GEM_DOMAIN_GTT)) {
+       if ((invalidate_domains | flush_domains) & I915_GEM_GPU_DOMAINS) {
                /*
                 * read/write caches:
                 *
@@ -1977,7 +1991,7 @@ i915_gem_evict_something(struct drm_device *dev)
                        i915_gem_flush(dev,
                                       obj->write_domain,
                                       obj->write_domain);
-                       i915_add_request(dev, obj->write_domain);
+                       i915_add_request(dev, NULL, obj->write_domain);
 
                        obj = NULL;
                        continue;
@@ -1991,7 +2005,7 @@ i915_gem_evict_something(struct drm_device *dev)
                /* If we didn't do any of the above, there's nothing to be done
                 * and we just can't fit it in.
                 */
-               return -ENOMEM;
+               return -ENOSPC;
        }
        return ret;
 }
@@ -2006,7 +2020,7 @@ i915_gem_evict_everything(struct drm_device *dev)
                if (ret != 0)
                        break;
        }
-       if (ret == -ENOMEM)
+       if (ret == -ENOSPC)
                return 0;
        return ret;
 }
@@ -2215,7 +2229,7 @@ try_again:
                loff_t offset;
 
                if (avail == 0)
-                       return -ENOMEM;
+                       return -ENOSPC;
 
                for (i = dev_priv->fence_reg_start;
                     i < dev_priv->num_fence_regs; i++) {
@@ -2248,7 +2262,7 @@ try_again:
                                i915_gem_flush(dev,
                                               I915_GEM_GPU_DOMAINS,
                                               I915_GEM_GPU_DOMAINS);
-                               seqno = i915_add_request(dev,
+                               seqno = i915_add_request(dev, NULL,
                                                         I915_GEM_GPU_DOMAINS);
                                if (seqno == 0)
                                        return -ENOMEM;
@@ -2364,7 +2378,7 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
                spin_unlock(&dev_priv->mm.active_list_lock);
                if (lists_empty) {
                        DRM_ERROR("GTT full, but LRU list empty\n");
-                       return -ENOMEM;
+                       return -ENOSPC;
                }
 
                ret = i915_gem_evict_something(dev);
@@ -2409,8 +2423,8 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
         * wasn't in the GTT, there shouldn't be any way it could have been in
         * a GPU cache
         */
-       BUG_ON(obj->read_domains & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT));
-       BUG_ON(obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT));
+       BUG_ON(obj->read_domains & I915_GEM_GPU_DOMAINS);
+       BUG_ON(obj->write_domain & I915_GEM_GPU_DOMAINS);
 
        return 0;
 }
@@ -2452,7 +2466,7 @@ i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj)
 
        /* Queue the GPU write cache flushing we need. */
        i915_gem_flush(dev, 0, obj->write_domain);
-       seqno = i915_add_request(dev, obj->write_domain);
+       seqno = i915_add_request(dev, NULL, obj->write_domain);
        obj->write_domain = 0;
        i915_gem_object_move_to_active(obj, seqno);
 }
@@ -3035,20 +3049,12 @@ i915_dispatch_gem_execbuffer(struct drm_device *dev,
        drm_i915_private_t *dev_priv = dev->dev_private;
        int nbox = exec->num_cliprects;
        int i = 0, count;
-       uint32_t        exec_start, exec_len;
+       uint32_t exec_start, exec_len;
        RING_LOCALS;
 
        exec_start = (uint32_t) exec_offset + exec->batch_start_offset;
        exec_len = (uint32_t) exec->batch_len;
 
-       if ((exec_start | exec_len) & 0x7) {
-               DRM_ERROR("alignment\n");
-               return -EINVAL;
-       }
-
-       if (!exec_start)
-               return -EINVAL;
-
        count = nbox ? nbox : 1;
 
        for (i = 0; i < count; i++) {
@@ -3089,6 +3095,10 @@ i915_dispatch_gem_execbuffer(struct drm_device *dev,
 /* Throttle our rendering by waiting until the ring has completed our requests
  * emitted over 20 msec ago.
  *
+ * Note that if we were to use the current jiffies each time around the loop,
+ * we wouldn't escape the function with any frames outstanding if the time to
+ * render a frame was over 20ms.
+ *
  * This should get us reasonable parallelism between CPU and GPU but also
  * relatively low latency when blocking on a particular request to finish.
  */
@@ -3097,15 +3107,25 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file_priv)
 {
        struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv;
        int ret = 0;
-       uint32_t seqno;
+       unsigned long recent_enough = jiffies - msecs_to_jiffies(20);
 
        mutex_lock(&dev->struct_mutex);
-       seqno = i915_file_priv->mm.last_gem_throttle_seqno;
-       i915_file_priv->mm.last_gem_throttle_seqno =
-               i915_file_priv->mm.last_gem_seqno;
-       if (seqno)
-               ret = i915_wait_request(dev, seqno);
+       while (!list_empty(&i915_file_priv->mm.request_list)) {
+               struct drm_i915_gem_request *request;
+
+               request = list_first_entry(&i915_file_priv->mm.request_list,
+                                          struct drm_i915_gem_request,
+                                          client_list);
+
+               if (time_after_eq(request->emitted_jiffies, recent_enough))
+                       break;
+
+               ret = i915_wait_request(dev, request->seqno);
+               if (ret != 0)
+                       break;
+       }
        mutex_unlock(&dev->struct_mutex);
+
        return ret;
 }
 
@@ -3182,12 +3202,29 @@ err:
        return ret;
 }
 
+static int
+i915_gem_check_execbuffer (struct drm_i915_gem_execbuffer *exec,
+                          uint64_t exec_offset)
+{
+       uint32_t exec_start, exec_len;
+
+       exec_start = (uint32_t) exec_offset + exec->batch_start_offset;
+       exec_len = (uint32_t) exec->batch_len;
+
+       if ((exec_start | exec_len) & 0x7)
+               return -EINVAL;
+
+       if (!exec_start)
+               return -EINVAL;
+
+       return 0;
+}
+
 int
 i915_gem_execbuffer(struct drm_device *dev, void *data,
                    struct drm_file *file_priv)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
-       struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv;
        struct drm_i915_gem_execbuffer *args = data;
        struct drm_i915_gem_exec_object *exec_list = NULL;
        struct drm_gem_object **object_list = NULL;
@@ -3312,7 +3349,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
                        break;
 
                /* error other than GTT full, or we've already tried again */
-               if (ret != -ENOMEM || pin_tries >= 1) {
+               if (ret != -ENOSPC || pin_tries >= 1) {
                        if (ret != -ERESTARTSYS)
                                DRM_ERROR("Failed to pin buffers %d\n", ret);
                        goto err;
@@ -3331,8 +3368,20 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
 
        /* Set the pending read domains for the batch buffer to COMMAND */
        batch_obj = object_list[args->buffer_count-1];
-       batch_obj->pending_read_domains = I915_GEM_DOMAIN_COMMAND;
-       batch_obj->pending_write_domain = 0;
+       if (batch_obj->pending_write_domain) {
+               DRM_ERROR("Attempting to use self-modifying batch buffer\n");
+               ret = -EINVAL;
+               goto err;
+       }
+       batch_obj->pending_read_domains |= I915_GEM_DOMAIN_COMMAND;
+
+       /* Sanity check the batch buffer, prior to moving objects */
+       exec_offset = exec_list[args->buffer_count - 1].offset;
+       ret = i915_gem_check_execbuffer (args, exec_offset);
+       if (ret != 0) {
+               DRM_ERROR("execbuf with invalid offset/length\n");
+               goto err;
+       }
 
        i915_verify_inactive(dev, __FILE__, __LINE__);
 
@@ -3363,7 +3412,8 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
                               dev->invalidate_domains,
                               dev->flush_domains);
                if (dev->flush_domains)
-                       (void)i915_add_request(dev, dev->flush_domains);
+                       (void)i915_add_request(dev, file_priv,
+                                              dev->flush_domains);
        }
 
        for (i = 0; i < args->buffer_count; i++) {
@@ -3381,8 +3431,6 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
        }
 #endif
 
-       exec_offset = exec_list[args->buffer_count - 1].offset;
-
 #if WATCH_EXEC
        i915_gem_dump_object(batch_obj,
                              args->batch_len,
@@ -3412,9 +3460,8 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
         * *some* interrupts representing completion of buffers that we can
         * wait on when trying to clear up gtt space).
         */
-       seqno = i915_add_request(dev, flush_domains);
+       seqno = i915_add_request(dev, file_priv, flush_domains);
        BUG_ON(seqno == 0);
-       i915_file_priv->mm.last_gem_seqno = seqno;
        for (i = 0; i < args->buffer_count; i++) {
                struct drm_gem_object *obj = object_list[i];
 
@@ -3520,8 +3567,7 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment)
                atomic_inc(&dev->pin_count);
                atomic_add(obj->size, &dev->pin_memory);
                if (!obj_priv->active &&
-                   (obj->write_domain & ~(I915_GEM_DOMAIN_CPU |
-                                          I915_GEM_DOMAIN_GTT)) == 0 &&
+                   (obj->write_domain & I915_GEM_GPU_DOMAINS) == 0 &&
                    !list_empty(&obj_priv->list))
                        list_del_init(&obj_priv->list);
        }
@@ -3548,8 +3594,7 @@ i915_gem_object_unpin(struct drm_gem_object *obj)
         */
        if (obj_priv->pin_count == 0) {
                if (!obj_priv->active &&
-                   (obj->write_domain & ~(I915_GEM_DOMAIN_CPU |
-                                          I915_GEM_DOMAIN_GTT)) == 0)
+                   (obj->write_domain & I915_GEM_GPU_DOMAINS) == 0)
                        list_move_tail(&obj_priv->list,
                                       &dev_priv->mm.inactive_list);
                atomic_dec(&dev->pin_count);
@@ -3653,15 +3698,14 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
        struct drm_gem_object *obj;
        struct drm_i915_gem_object *obj_priv;
 
-       mutex_lock(&dev->struct_mutex);
        obj = drm_gem_object_lookup(dev, file_priv, args->handle);
        if (obj == NULL) {
                DRM_ERROR("Bad handle in i915_gem_busy_ioctl(): %d\n",
                          args->handle);
-               mutex_unlock(&dev->struct_mutex);
                return -EBADF;
        }
 
+       mutex_lock(&dev->struct_mutex);
        /* Update the active list for the hardware's current position.
         * Otherwise this only updates on a delayed timer or when irqs are
         * actually unmasked, and our working set ends up being larger than
@@ -3800,9 +3844,8 @@ i915_gem_idle(struct drm_device *dev)
 
        /* Flush the GPU along with all non-CPU write domains
         */
-       i915_gem_flush(dev, ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT),
-                      ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT));
-       seqno = i915_add_request(dev, ~I915_GEM_DOMAIN_CPU);
+       i915_gem_flush(dev, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
+       seqno = i915_add_request(dev, NULL, I915_GEM_GPU_DOMAINS);
 
        if (seqno == 0) {
                mutex_unlock(&dev->struct_mutex);
@@ -4352,3 +4395,17 @@ i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
        drm_agp_chipset_flush(dev);
        return 0;
 }
+
+void i915_gem_release(struct drm_device * dev, struct drm_file *file_priv)
+{
+       struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv;
+
+       /* Clean up our request list when the client is going away, so that
+        * later retire_requests won't dereference our soon-to-be-gone
+        * file_priv.
+        */
+       mutex_lock(&dev->struct_mutex);
+       while (!list_empty(&i915_file_priv->mm.request_list))
+               list_del_init(i915_file_priv->mm.request_list.next);
+       mutex_unlock(&dev->struct_mutex);
+}
index 540dd336e6ec682ade96cc0c56f9f7078a2e97ac..9a05cadaa4ad0efbcfd0d1082d5c37609b78cc5f 100644 (file)
@@ -25,6 +25,8 @@
  *
  */
 
+#include <linux/acpi.h>
+#include <linux/pnp.h>
 #include "linux/string.h"
 #include "linux/bitops.h"
 #include "drmP.h"
  * to match what the GPU expects.
  */
 
+#define MCHBAR_I915 0x44
+#define MCHBAR_I965 0x48
+#define MCHBAR_SIZE (4*4096)
+
+#define DEVEN_REG 0x54
+#define   DEVEN_MCHBAR_EN (1 << 28)
+
+/* Allocate space for the MCH regs if needed, return nonzero on error */
+static int
+intel_alloc_mchbar_resource(struct drm_device *dev)
+{
+       struct pci_dev *bridge_dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       int reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915;
+       u32 temp_lo, temp_hi = 0;
+       u64 mchbar_addr;
+       int ret = 0;
+
+       bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0,0));
+       if (!bridge_dev) {
+               DRM_DEBUG("no bridge dev?!\n");
+               ret = -ENODEV;
+               goto out;
+       }
+
+       if (IS_I965G(dev))
+               pci_read_config_dword(bridge_dev, reg + 4, &temp_hi);
+       pci_read_config_dword(bridge_dev, reg, &temp_lo);
+       mchbar_addr = ((u64)temp_hi << 32) | temp_lo;
+
+       /* If ACPI doesn't have it, assume we need to allocate it ourselves */
+       if (mchbar_addr &&
+           pnp_range_reserved(mchbar_addr, mchbar_addr + MCHBAR_SIZE)) {
+               ret = 0;
+               goto out_put;
+       }
+
+       /* Get some space for it */
+       ret = pci_bus_alloc_resource(bridge_dev->bus, &dev_priv->mch_res,
+                                    MCHBAR_SIZE, MCHBAR_SIZE,
+                                    PCIBIOS_MIN_MEM,
+                                    0,   pcibios_align_resource,
+                                    bridge_dev);
+       if (ret) {
+               DRM_DEBUG("failed bus alloc: %d\n", ret);
+               dev_priv->mch_res.start = 0;
+               goto out_put;
+       }
+
+       if (IS_I965G(dev))
+               pci_write_config_dword(bridge_dev, reg + 4,
+                                      upper_32_bits(dev_priv->mch_res.start));
+
+       pci_write_config_dword(bridge_dev, reg,
+                              lower_32_bits(dev_priv->mch_res.start));
+out_put:
+       pci_dev_put(bridge_dev);
+out:
+       return ret;
+}
+
+/* Setup MCHBAR if possible, return true if we should disable it again */
+static bool
+intel_setup_mchbar(struct drm_device *dev)
+{
+       struct pci_dev *bridge_dev;
+       int mchbar_reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915;
+       u32 temp;
+       bool need_disable = false, enabled;
+
+       bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0,0));
+       if (!bridge_dev) {
+               DRM_DEBUG("no bridge dev?!\n");
+               goto out;
+       }
+
+       if (IS_I915G(dev) || IS_I915GM(dev)) {
+               pci_read_config_dword(bridge_dev, DEVEN_REG, &temp);
+               enabled = !!(temp & DEVEN_MCHBAR_EN);
+       } else {
+               pci_read_config_dword(bridge_dev, mchbar_reg, &temp);
+               enabled = temp & 1;
+       }
+
+       /* If it's already enabled, don't have to do anything */
+       if (enabled)
+               goto out_put;
+
+       if (intel_alloc_mchbar_resource(dev))
+               goto out_put;
+
+       need_disable = true;
+
+       /* Space is allocated or reserved, so enable it. */
+       if (IS_I915G(dev) || IS_I915GM(dev)) {
+               pci_write_config_dword(bridge_dev, DEVEN_REG,
+                                      temp | DEVEN_MCHBAR_EN);
+       } else {
+               pci_read_config_dword(bridge_dev, mchbar_reg, &temp);
+               pci_write_config_dword(bridge_dev, mchbar_reg, temp | 1);
+       }
+out_put:
+       pci_dev_put(bridge_dev);
+out:
+       return need_disable;
+}
+
+static void
+intel_teardown_mchbar(struct drm_device *dev, bool disable)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct pci_dev *bridge_dev;
+       int mchbar_reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915;
+       u32 temp;
+
+       bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0,0));
+       if (!bridge_dev) {
+               DRM_DEBUG("no bridge dev?!\n");
+               return;
+       }
+
+       if (disable) {
+               if (IS_I915G(dev) || IS_I915GM(dev)) {
+                       pci_read_config_dword(bridge_dev, DEVEN_REG, &temp);
+                       temp &= ~DEVEN_MCHBAR_EN;
+                       pci_write_config_dword(bridge_dev, DEVEN_REG, temp);
+               } else {
+                       pci_read_config_dword(bridge_dev, mchbar_reg, &temp);
+                       temp &= ~1;
+                       pci_write_config_dword(bridge_dev, mchbar_reg, temp);
+               }
+       }
+
+       if (dev_priv->mch_res.start)
+               release_resource(&dev_priv->mch_res);
+}
+
 /**
  * Detects bit 6 swizzling of address lookup between IGD access and CPU
  * access through main memory.
@@ -91,6 +230,7 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
        drm_i915_private_t *dev_priv = dev->dev_private;
        uint32_t swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN;
        uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
+       bool need_disable;
 
        if (!IS_I9XX(dev)) {
                /* As far as we know, the 865 doesn't have these bit 6
@@ -101,6 +241,9 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
        } else if (IS_MOBILE(dev)) {
                uint32_t dcc;
 
+               /* Try to make sure MCHBAR is enabled before poking at it */
+               need_disable = intel_setup_mchbar(dev);
+
                /* On mobile 9xx chipsets, channel interleave by the CPU is
                 * determined by DCC.  For single-channel, neither the CPU
                 * nor the GPU do swizzling.  For dual channel interleaved,
@@ -140,6 +283,8 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
                        swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN;
                        swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
                }
+
+               intel_teardown_mchbar(dev, need_disable);
        } else {
                /* The 965, G33, and newer, have a very flexible memory
                 * configuration.  It will enable dual-channel mode
@@ -170,6 +315,13 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
                }
        }
 
+       /* FIXME: check with memory config on IGDNG */
+       if (IS_IGDNG(dev)) {
+               DRM_ERROR("disable tiling on IGDNG...\n");
+               swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN;
+               swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
+       }
+
        dev_priv->mm.bit_6_swizzle_x = swizzle_x;
        dev_priv->mm.bit_6_swizzle_y = swizzle_y;
 }
index 98bb4c878c4ef892a5aeb42b64a335e95eb45b24..b86b7b7130c603beca04456ccca0abc4c5bbfbcc 100644 (file)
 #define DRM_I915_VBLANK_PIPE_ALL       (DRM_I915_VBLANK_PIPE_A | \
                                         DRM_I915_VBLANK_PIPE_B)
 
+void
+igdng_enable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
+{
+       if ((dev_priv->gt_irq_mask_reg & mask) != 0) {
+               dev_priv->gt_irq_mask_reg &= ~mask;
+               I915_WRITE(GTIMR, dev_priv->gt_irq_mask_reg);
+               (void) I915_READ(GTIMR);
+       }
+}
+
+static inline void
+igdng_disable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
+{
+       if ((dev_priv->gt_irq_mask_reg & mask) != mask) {
+               dev_priv->gt_irq_mask_reg |= mask;
+               I915_WRITE(GTIMR, dev_priv->gt_irq_mask_reg);
+               (void) I915_READ(GTIMR);
+       }
+}
+
+/* For display hotplug interrupt */
+void
+igdng_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
+{
+       if ((dev_priv->irq_mask_reg & mask) != 0) {
+               dev_priv->irq_mask_reg &= ~mask;
+               I915_WRITE(DEIMR, dev_priv->irq_mask_reg);
+               (void) I915_READ(DEIMR);
+       }
+}
+
+static inline void
+igdng_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
+{
+       if ((dev_priv->irq_mask_reg & mask) != mask) {
+               dev_priv->irq_mask_reg |= mask;
+               I915_WRITE(DEIMR, dev_priv->irq_mask_reg);
+               (void) I915_READ(DEIMR);
+       }
+}
+
 void
 i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask)
 {
@@ -196,6 +237,47 @@ static void i915_hotplug_work_func(struct work_struct *work)
        drm_sysfs_hotplug_event(dev);
 }
 
+irqreturn_t igdng_irq_handler(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       int ret = IRQ_NONE;
+       u32 de_iir, gt_iir;
+       u32 new_de_iir, new_gt_iir;
+       struct drm_i915_master_private *master_priv;
+
+       de_iir = I915_READ(DEIIR);
+       gt_iir = I915_READ(GTIIR);
+
+       for (;;) {
+               if (de_iir == 0 && gt_iir == 0)
+                       break;
+
+               ret = IRQ_HANDLED;
+
+               I915_WRITE(DEIIR, de_iir);
+               new_de_iir = I915_READ(DEIIR);
+               I915_WRITE(GTIIR, gt_iir);
+               new_gt_iir = I915_READ(GTIIR);
+
+               if (dev->primary->master) {
+                       master_priv = dev->primary->master->driver_priv;
+                       if (master_priv->sarea_priv)
+                               master_priv->sarea_priv->last_dispatch =
+                                       READ_BREADCRUMB(dev_priv);
+               }
+
+               if (gt_iir & GT_USER_INTERRUPT) {
+                       dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev);
+                       DRM_WAKEUP(&dev_priv->irq_queue);
+               }
+
+               de_iir = new_de_iir;
+               gt_iir = new_gt_iir;
+       }
+
+       return ret;
+}
+
 irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
 {
        struct drm_device *dev = (struct drm_device *) arg;
@@ -212,6 +294,9 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
 
        atomic_inc(&dev_priv->irq_received);
 
+       if (IS_IGDNG(dev))
+               return igdng_irq_handler(dev);
+
        iir = I915_READ(IIR);
 
        if (IS_I965G(dev)) {
@@ -349,8 +434,12 @@ void i915_user_irq_get(struct drm_device *dev)
        unsigned long irqflags;
 
        spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
-       if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1))
-               i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
+       if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1)) {
+               if (IS_IGDNG(dev))
+                       igdng_enable_graphics_irq(dev_priv, GT_USER_INTERRUPT);
+               else
+                       i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
+       }
        spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
 }
 
@@ -361,8 +450,12 @@ void i915_user_irq_put(struct drm_device *dev)
 
        spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
        BUG_ON(dev->irq_enabled && dev_priv->user_irq_refcount <= 0);
-       if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0))
-               i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
+       if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0)) {
+               if (IS_IGDNG(dev))
+                       igdng_disable_graphics_irq(dev_priv, GT_USER_INTERRUPT);
+               else
+                       i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
+       }
        spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
 }
 
@@ -455,6 +548,9 @@ int i915_enable_vblank(struct drm_device *dev, int pipe)
        if (!(pipeconf & PIPEACONF_ENABLE))
                return -EINVAL;
 
+       if (IS_IGDNG(dev))
+               return 0;
+
        spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
        if (IS_I965G(dev))
                i915_enable_pipestat(dev_priv, pipe,
@@ -474,6 +570,9 @@ void i915_disable_vblank(struct drm_device *dev, int pipe)
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        unsigned long irqflags;
 
+       if (IS_IGDNG(dev))
+               return;
+
        spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
        i915_disable_pipestat(dev_priv, pipe,
                              PIPE_VBLANK_INTERRUPT_ENABLE |
@@ -484,7 +583,9 @@ void i915_disable_vblank(struct drm_device *dev, int pipe)
 void i915_enable_interrupt (struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       opregion_enable_asle(dev);
+
+       if (!IS_IGDNG(dev))
+               opregion_enable_asle(dev);
        dev_priv->irq_enabled = 1;
 }
 
@@ -545,12 +646,65 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
 
 /* drm_dma.h hooks
 */
+static void igdng_irq_preinstall(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+       I915_WRITE(HWSTAM, 0xeffe);
+
+       /* XXX hotplug from PCH */
+
+       I915_WRITE(DEIMR, 0xffffffff);
+       I915_WRITE(DEIER, 0x0);
+       (void) I915_READ(DEIER);
+
+       /* and GT */
+       I915_WRITE(GTIMR, 0xffffffff);
+       I915_WRITE(GTIER, 0x0);
+       (void) I915_READ(GTIER);
+}
+
+static int igdng_irq_postinstall(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       /* enable kind of interrupts always enabled */
+       u32 display_mask = DE_MASTER_IRQ_CONTROL /*| DE_PCH_EVENT */;
+       u32 render_mask = GT_USER_INTERRUPT;
+
+       dev_priv->irq_mask_reg = ~display_mask;
+       dev_priv->de_irq_enable_reg = display_mask;
+
+       /* should always can generate irq */
+       I915_WRITE(DEIIR, I915_READ(DEIIR));
+       I915_WRITE(DEIMR, dev_priv->irq_mask_reg);
+       I915_WRITE(DEIER, dev_priv->de_irq_enable_reg);
+       (void) I915_READ(DEIER);
+
+       /* user interrupt should be enabled, but masked initial */
+       dev_priv->gt_irq_mask_reg = 0xffffffff;
+       dev_priv->gt_irq_enable_reg = render_mask;
+
+       I915_WRITE(GTIIR, I915_READ(GTIIR));
+       I915_WRITE(GTIMR, dev_priv->gt_irq_mask_reg);
+       I915_WRITE(GTIER, dev_priv->gt_irq_enable_reg);
+       (void) I915_READ(GTIER);
+
+       return 0;
+}
+
 void i915_driver_irq_preinstall(struct drm_device * dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
 
        atomic_set(&dev_priv->irq_received, 0);
 
+       INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
+
+       if (IS_IGDNG(dev)) {
+               igdng_irq_preinstall(dev);
+               return;
+       }
+
        if (I915_HAS_HOTPLUG(dev)) {
                I915_WRITE(PORT_HOTPLUG_EN, 0);
                I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
@@ -562,7 +716,6 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
        I915_WRITE(IMR, 0xffffffff);
        I915_WRITE(IER, 0x0);
        (void) I915_READ(IER);
-       INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
 }
 
 int i915_driver_irq_postinstall(struct drm_device *dev)
@@ -570,9 +723,12 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        u32 enable_mask = I915_INTERRUPT_ENABLE_FIX | I915_INTERRUPT_ENABLE_VAR;
 
+       DRM_INIT_WAITQUEUE(&dev_priv->irq_queue);
+
        dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
 
-       dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
+       if (IS_IGDNG(dev))
+               return igdng_irq_postinstall(dev);
 
        /* Unmask the interrupts that we always want on. */
        dev_priv->irq_mask_reg = ~I915_INTERRUPT_ENABLE_FIX;
@@ -613,11 +769,24 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
        (void) I915_READ(IER);
 
        opregion_enable_asle(dev);
-       DRM_INIT_WAITQUEUE(&dev_priv->irq_queue);
 
        return 0;
 }
 
+static void igdng_irq_uninstall(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       I915_WRITE(HWSTAM, 0xffffffff);
+
+       I915_WRITE(DEIMR, 0xffffffff);
+       I915_WRITE(DEIER, 0x0);
+       I915_WRITE(DEIIR, I915_READ(DEIIR));
+
+       I915_WRITE(GTIMR, 0xffffffff);
+       I915_WRITE(GTIER, 0x0);
+       I915_WRITE(GTIIR, I915_READ(GTIIR));
+}
+
 void i915_driver_irq_uninstall(struct drm_device * dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
@@ -627,6 +796,11 @@ void i915_driver_irq_uninstall(struct drm_device * dev)
 
        dev_priv->vblank_pipe = 0;
 
+       if (IS_IGDNG(dev)) {
+               igdng_irq_uninstall(dev);
+               return;
+       }
+
        if (I915_HAS_HOTPLUG(dev)) {
                I915_WRITE(PORT_HOTPLUG_EN, 0);
                I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
index 375569d01d011fc00be4b94dbae0db06668af6de..f6237a0b1133c0429cc27a2a0c26282bd6051b8c 100644 (file)
 #define   PLLB_REF_INPUT_SPREADSPECTRUMIN (3 << 13)
 #define   PLL_REF_INPUT_MASK           (3 << 13)
 #define   PLL_LOAD_PULSE_PHASE_SHIFT           9
+/* IGDNG */
+# define PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT     9
+# define PLL_REF_SDVO_HDMI_MULTIPLIER_MASK      (7 << 9)
+# define PLL_REF_SDVO_HDMI_MULTIPLIER(x)       (((x)-1) << 9)
+# define DPLL_FPA1_P1_POST_DIV_SHIFT            0
+# define DPLL_FPA1_P1_POST_DIV_MASK             0xff
+
 /*
  * Parallel to Serial Load Pulse phase selection.
  * Selects the phase for the 10X DPLL clock for the PCIe
 /* Hotplug control (945+ only) */
 #define PORT_HOTPLUG_EN                0x61110
 #define   HDMIB_HOTPLUG_INT_EN                 (1 << 29)
+#define   DPB_HOTPLUG_INT_EN                   (1 << 29)
 #define   HDMIC_HOTPLUG_INT_EN                 (1 << 28)
+#define   DPC_HOTPLUG_INT_EN                   (1 << 28)
 #define   HDMID_HOTPLUG_INT_EN                 (1 << 27)
+#define   DPD_HOTPLUG_INT_EN                   (1 << 27)
 #define   SDVOB_HOTPLUG_INT_EN                 (1 << 26)
 #define   SDVOC_HOTPLUG_INT_EN                 (1 << 25)
 #define   TV_HOTPLUG_INT_EN                    (1 << 18)
 
 #define PORT_HOTPLUG_STAT      0x61114
 #define   HDMIB_HOTPLUG_INT_STATUS             (1 << 29)
+#define   DPB_HOTPLUG_INT_STATUS               (1 << 29)
 #define   HDMIC_HOTPLUG_INT_STATUS             (1 << 28)
+#define   DPC_HOTPLUG_INT_STATUS               (1 << 28)
 #define   HDMID_HOTPLUG_INT_STATUS             (1 << 27)
+#define   DPD_HOTPLUG_INT_STATUS               (1 << 27)
 #define   CRT_HOTPLUG_INT_STATUS               (1 << 11)
 #define   TV_HOTPLUG_INT_STATUS                        (1 << 10)
 #define   CRT_HOTPLUG_MONITOR_MASK             (3 << 8)
 # define DAC_A_1_3_V                   (0 << 4)
 # define DAC_A_1_1_V                   (1 << 4)
 # define DAC_A_0_7_V                   (2 << 4)
-# define DAC_A_OFF                     (3 << 4)
+# define DAC_A_MASK                    (3 << 4)
 # define DAC_B_1_3_V                   (0 << 2)
 # define DAC_B_1_1_V                   (1 << 2)
 # define DAC_B_0_7_V                   (2 << 2)
-# define DAC_B_OFF                     (3 << 2)
+# define DAC_B_MASK                    (3 << 2)
 # define DAC_C_1_3_V                   (0 << 0)
 # define DAC_C_1_1_V                   (1 << 0)
 # define DAC_C_0_7_V                   (2 << 0)
-# define DAC_C_OFF                     (3 << 0)
+# define DAC_C_MASK                    (3 << 0)
 
 /**
  * CSC coefficients are stored in a floating point format with 9 bits of
 #define TV_V_CHROMA_0          0x68400
 #define TV_V_CHROMA_42         0x684a8
 
+/* Display Port */
+#define DP_B                           0x64100
+#define DP_C                           0x64200
+#define DP_D                           0x64300
+
+#define   DP_PORT_EN                   (1 << 31)
+#define   DP_PIPEB_SELECT              (1 << 30)
+
+/* Link training mode - select a suitable mode for each stage */
+#define   DP_LINK_TRAIN_PAT_1          (0 << 28)
+#define   DP_LINK_TRAIN_PAT_2          (1 << 28)
+#define   DP_LINK_TRAIN_PAT_IDLE       (2 << 28)
+#define   DP_LINK_TRAIN_OFF            (3 << 28)
+#define   DP_LINK_TRAIN_MASK           (3 << 28)
+#define   DP_LINK_TRAIN_SHIFT          28
+
+/* Signal voltages. These are mostly controlled by the other end */
+#define   DP_VOLTAGE_0_4               (0 << 25)
+#define   DP_VOLTAGE_0_6               (1 << 25)
+#define   DP_VOLTAGE_0_8               (2 << 25)
+#define   DP_VOLTAGE_1_2               (3 << 25)
+#define   DP_VOLTAGE_MASK              (7 << 25)
+#define   DP_VOLTAGE_SHIFT             25
+
+/* Signal pre-emphasis levels, like voltages, the other end tells us what
+ * they want
+ */
+#define   DP_PRE_EMPHASIS_0            (0 << 22)
+#define   DP_PRE_EMPHASIS_3_5          (1 << 22)
+#define   DP_PRE_EMPHASIS_6            (2 << 22)
+#define   DP_PRE_EMPHASIS_9_5          (3 << 22)
+#define   DP_PRE_EMPHASIS_MASK         (7 << 22)
+#define   DP_PRE_EMPHASIS_SHIFT                22
+
+/* How many wires to use. I guess 3 was too hard */
+#define   DP_PORT_WIDTH_1              (0 << 19)
+#define   DP_PORT_WIDTH_2              (1 << 19)
+#define   DP_PORT_WIDTH_4              (3 << 19)
+#define   DP_PORT_WIDTH_MASK           (7 << 19)
+
+/* Mystic DPCD version 1.1 special mode */
+#define   DP_ENHANCED_FRAMING          (1 << 18)
+
+/** locked once port is enabled */
+#define   DP_PORT_REVERSAL             (1 << 15)
+
+/** sends the clock on lane 15 of the PEG for debug */
+#define   DP_CLOCK_OUTPUT_ENABLE       (1 << 13)
+
+#define   DP_SCRAMBLING_DISABLE                (1 << 12)
+
+/** limit RGB values to avoid confusing TVs */
+#define   DP_COLOR_RANGE_16_235                (1 << 8)
+
+/** Turn on the audio link */
+#define   DP_AUDIO_OUTPUT_ENABLE       (1 << 6)
+
+/** vs and hs sync polarity */
+#define   DP_SYNC_VS_HIGH              (1 << 4)
+#define   DP_SYNC_HS_HIGH              (1 << 3)
+
+/** A fantasy */
+#define   DP_DETECTED                  (1 << 2)
+
+/** The aux channel provides a way to talk to the
+ * signal sink for DDC etc. Max packet size supported
+ * is 20 bytes in each direction, hence the 5 fixed
+ * data registers
+ */
+#define DPB_AUX_CH_CTL                 0x64110
+#define DPB_AUX_CH_DATA1               0x64114
+#define DPB_AUX_CH_DATA2               0x64118
+#define DPB_AUX_CH_DATA3               0x6411c
+#define DPB_AUX_CH_DATA4               0x64120
+#define DPB_AUX_CH_DATA5               0x64124
+
+#define DPC_AUX_CH_CTL                 0x64210
+#define DPC_AUX_CH_DATA1               0x64214
+#define DPC_AUX_CH_DATA2               0x64218
+#define DPC_AUX_CH_DATA3               0x6421c
+#define DPC_AUX_CH_DATA4               0x64220
+#define DPC_AUX_CH_DATA5               0x64224
+
+#define DPD_AUX_CH_CTL                 0x64310
+#define DPD_AUX_CH_DATA1               0x64314
+#define DPD_AUX_CH_DATA2               0x64318
+#define DPD_AUX_CH_DATA3               0x6431c
+#define DPD_AUX_CH_DATA4               0x64320
+#define DPD_AUX_CH_DATA5               0x64324
+
+#define   DP_AUX_CH_CTL_SEND_BUSY          (1 << 31)
+#define   DP_AUX_CH_CTL_DONE               (1 << 30)
+#define   DP_AUX_CH_CTL_INTERRUPT          (1 << 29)
+#define   DP_AUX_CH_CTL_TIME_OUT_ERROR     (1 << 28)
+#define   DP_AUX_CH_CTL_TIME_OUT_400us     (0 << 26)
+#define   DP_AUX_CH_CTL_TIME_OUT_600us     (1 << 26)
+#define   DP_AUX_CH_CTL_TIME_OUT_800us     (2 << 26)
+#define   DP_AUX_CH_CTL_TIME_OUT_1600us            (3 << 26)
+#define   DP_AUX_CH_CTL_TIME_OUT_MASK      (3 << 26)
+#define   DP_AUX_CH_CTL_RECEIVE_ERROR      (1 << 25)
+#define   DP_AUX_CH_CTL_MESSAGE_SIZE_MASK    (0x1f << 20)
+#define   DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT   20
+#define   DP_AUX_CH_CTL_PRECHARGE_2US_MASK   (0xf << 16)
+#define   DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT  16
+#define   DP_AUX_CH_CTL_AUX_AKSV_SELECT            (1 << 15)
+#define   DP_AUX_CH_CTL_MANCHESTER_TEST            (1 << 14)
+#define   DP_AUX_CH_CTL_SYNC_TEST          (1 << 13)
+#define   DP_AUX_CH_CTL_DEGLITCH_TEST      (1 << 12)
+#define   DP_AUX_CH_CTL_PRECHARGE_TEST     (1 << 11)
+#define   DP_AUX_CH_CTL_BIT_CLOCK_2X_MASK    (0x7ff)
+#define   DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT   0
+
+/*
+ * Computing GMCH M and N values for the Display Port link
+ *
+ * GMCH M/N = dot clock * bytes per pixel / ls_clk * # of lanes
+ *
+ * ls_clk (we assume) is the DP link clock (1.62 or 2.7 GHz)
+ *
+ * The GMCH value is used internally
+ *
+ * bytes_per_pixel is the number of bytes coming out of the plane,
+ * which is after the LUTs, so we want the bytes for our color format.
+ * For our current usage, this is always 3, one byte for R, G and B.
+ */
+#define PIPEA_GMCH_DATA_M                      0x70050
+#define PIPEB_GMCH_DATA_M                      0x71050
+
+/* Transfer unit size for display port - 1, default is 0x3f (for TU size 64) */
+#define   PIPE_GMCH_DATA_M_TU_SIZE_MASK                (0x3f << 25)
+#define   PIPE_GMCH_DATA_M_TU_SIZE_SHIFT       25
+
+#define   PIPE_GMCH_DATA_M_MASK                        (0xffffff)
+
+#define PIPEA_GMCH_DATA_N                      0x70054
+#define PIPEB_GMCH_DATA_N                      0x71054
+#define   PIPE_GMCH_DATA_N_MASK                        (0xffffff)
+
+/*
+ * Computing Link M and N values for the Display Port link
+ *
+ * Link M / N = pixel_clock / ls_clk
+ *
+ * (the DP spec calls pixel_clock the 'strm_clk')
+ *
+ * The Link value is transmitted in the Main Stream
+ * Attributes and VB-ID.
+ */
+
+#define PIPEA_DP_LINK_M                                0x70060
+#define PIPEB_DP_LINK_M                                0x71060
+#define   PIPEA_DP_LINK_M_MASK                 (0xffffff)
+
+#define PIPEA_DP_LINK_N                                0x70064
+#define PIPEB_DP_LINK_N                                0x71064
+#define   PIPEA_DP_LINK_N_MASK                 (0xffffff)
+
 /* Display & cursor control */
 
 /* Pipe A */
 # define VGA_2X_MODE                           (1 << 30)
 # define VGA_PIPE_B_SELECT                     (1 << 29)
 
+/* IGDNG */
+
+#define CPU_VGACNTRL   0x41000
+
+#define DIGITAL_PORT_HOTPLUG_CNTRL      0x44030
+#define  DIGITAL_PORTA_HOTPLUG_ENABLE           (1 << 4)
+#define  DIGITAL_PORTA_SHORT_PULSE_2MS          (0 << 2)
+#define  DIGITAL_PORTA_SHORT_PULSE_4_5MS        (1 << 2)
+#define  DIGITAL_PORTA_SHORT_PULSE_6MS          (2 << 2)
+#define  DIGITAL_PORTA_SHORT_PULSE_100MS        (3 << 2)
+#define  DIGITAL_PORTA_NO_DETECT                (0 << 0)
+#define  DIGITAL_PORTA_LONG_PULSE_DETECT_MASK   (1 << 1)
+#define  DIGITAL_PORTA_SHORT_PULSE_DETECT_MASK  (1 << 0)
+
+/* refresh rate hardware control */
+#define RR_HW_CTL       0x45300
+#define  RR_HW_LOW_POWER_FRAMES_MASK    0xff
+#define  RR_HW_HIGH_POWER_FRAMES_MASK   0xff00
+
+#define FDI_PLL_BIOS_0  0x46000
+#define FDI_PLL_BIOS_1  0x46004
+#define FDI_PLL_BIOS_2  0x46008
+#define DISPLAY_PORT_PLL_BIOS_0         0x4600c
+#define DISPLAY_PORT_PLL_BIOS_1         0x46010
+#define DISPLAY_PORT_PLL_BIOS_2         0x46014
+
+#define FDI_PLL_FREQ_CTL        0x46030
+#define  FDI_PLL_FREQ_CHANGE_REQUEST    (1<<24)
+#define  FDI_PLL_FREQ_LOCK_LIMIT_MASK   0xfff00
+#define  FDI_PLL_FREQ_DISABLE_COUNT_LIMIT_MASK  0xff
+
+
+#define PIPEA_DATA_M1           0x60030
+#define  TU_SIZE(x)             (((x)-1) << 25) /* default size 64 */
+#define  TU_SIZE_MASK           0x7e000000
+#define  PIPEA_DATA_M1_OFFSET   0
+#define PIPEA_DATA_N1           0x60034
+#define  PIPEA_DATA_N1_OFFSET   0
+
+#define PIPEA_DATA_M2           0x60038
+#define  PIPEA_DATA_M2_OFFSET   0
+#define PIPEA_DATA_N2           0x6003c
+#define  PIPEA_DATA_N2_OFFSET   0
+
+#define PIPEA_LINK_M1           0x60040
+#define  PIPEA_LINK_M1_OFFSET   0
+#define PIPEA_LINK_N1           0x60044
+#define  PIPEA_LINK_N1_OFFSET   0
+
+#define PIPEA_LINK_M2           0x60048
+#define  PIPEA_LINK_M2_OFFSET   0
+#define PIPEA_LINK_N2           0x6004c
+#define  PIPEA_LINK_N2_OFFSET   0
+
+/* PIPEB timing regs are same start from 0x61000 */
+
+#define PIPEB_DATA_M1           0x61030
+#define  PIPEB_DATA_M1_OFFSET   0
+#define PIPEB_DATA_N1           0x61034
+#define  PIPEB_DATA_N1_OFFSET   0
+
+#define PIPEB_DATA_M2           0x61038
+#define  PIPEB_DATA_M2_OFFSET   0
+#define PIPEB_DATA_N2           0x6103c
+#define  PIPEB_DATA_N2_OFFSET   0
+
+#define PIPEB_LINK_M1           0x61040
+#define  PIPEB_LINK_M1_OFFSET   0
+#define PIPEB_LINK_N1           0x61044
+#define  PIPEB_LINK_N1_OFFSET   0
+
+#define PIPEB_LINK_M2           0x61048
+#define  PIPEB_LINK_M2_OFFSET   0
+#define PIPEB_LINK_N2           0x6104c
+#define  PIPEB_LINK_N2_OFFSET   0
+
+/* CPU panel fitter */
+#define PFA_CTL_1               0x68080
+#define PFB_CTL_1               0x68880
+#define  PF_ENABLE              (1<<31)
+
+/* legacy palette */
+#define LGC_PALETTE_A           0x4a000
+#define LGC_PALETTE_B           0x4a800
+
+/* interrupts */
+#define DE_MASTER_IRQ_CONTROL   (1 << 31)
+#define DE_SPRITEB_FLIP_DONE    (1 << 29)
+#define DE_SPRITEA_FLIP_DONE    (1 << 28)
+#define DE_PLANEB_FLIP_DONE     (1 << 27)
+#define DE_PLANEA_FLIP_DONE     (1 << 26)
+#define DE_PCU_EVENT            (1 << 25)
+#define DE_GTT_FAULT            (1 << 24)
+#define DE_POISON               (1 << 23)
+#define DE_PERFORM_COUNTER      (1 << 22)
+#define DE_PCH_EVENT            (1 << 21)
+#define DE_AUX_CHANNEL_A        (1 << 20)
+#define DE_DP_A_HOTPLUG         (1 << 19)
+#define DE_GSE                  (1 << 18)
+#define DE_PIPEB_VBLANK         (1 << 15)
+#define DE_PIPEB_EVEN_FIELD     (1 << 14)
+#define DE_PIPEB_ODD_FIELD      (1 << 13)
+#define DE_PIPEB_LINE_COMPARE   (1 << 12)
+#define DE_PIPEB_VSYNC          (1 << 11)
+#define DE_PIPEB_FIFO_UNDERRUN  (1 << 8)
+#define DE_PIPEA_VBLANK         (1 << 7)
+#define DE_PIPEA_EVEN_FIELD     (1 << 6)
+#define DE_PIPEA_ODD_FIELD      (1 << 5)
+#define DE_PIPEA_LINE_COMPARE   (1 << 4)
+#define DE_PIPEA_VSYNC          (1 << 3)
+#define DE_PIPEA_FIFO_UNDERRUN  (1 << 0)
+
+#define DEISR   0x44000
+#define DEIMR   0x44004
+#define DEIIR   0x44008
+#define DEIER   0x4400c
+
+/* GT interrupt */
+#define GT_SYNC_STATUS          (1 << 2)
+#define GT_USER_INTERRUPT       (1 << 0)
+
+#define GTISR   0x44010
+#define GTIMR   0x44014
+#define GTIIR   0x44018
+#define GTIER   0x4401c
+
+/* PCH */
+
+/* south display engine interrupt */
+#define SDE_CRT_HOTPLUG         (1 << 11)
+#define SDE_PORTD_HOTPLUG       (1 << 10)
+#define SDE_PORTC_HOTPLUG       (1 << 9)
+#define SDE_PORTB_HOTPLUG       (1 << 8)
+#define SDE_SDVOB_HOTPLUG       (1 << 6)
+
+#define SDEISR  0xc4000
+#define SDEIMR  0xc4004
+#define SDEIIR  0xc4008
+#define SDEIER  0xc400c
+
+/* digital port hotplug */
+#define PCH_PORT_HOTPLUG        0xc4030
+#define PORTD_HOTPLUG_ENABLE            (1 << 20)
+#define PORTD_PULSE_DURATION_2ms        (0)
+#define PORTD_PULSE_DURATION_4_5ms      (1 << 18)
+#define PORTD_PULSE_DURATION_6ms        (2 << 18)
+#define PORTD_PULSE_DURATION_100ms      (3 << 18)
+#define PORTD_HOTPLUG_NO_DETECT         (0)
+#define PORTD_HOTPLUG_SHORT_DETECT      (1 << 16)
+#define PORTD_HOTPLUG_LONG_DETECT       (1 << 17)
+#define PORTC_HOTPLUG_ENABLE            (1 << 12)
+#define PORTC_PULSE_DURATION_2ms        (0)
+#define PORTC_PULSE_DURATION_4_5ms      (1 << 10)
+#define PORTC_PULSE_DURATION_6ms        (2 << 10)
+#define PORTC_PULSE_DURATION_100ms      (3 << 10)
+#define PORTC_HOTPLUG_NO_DETECT         (0)
+#define PORTC_HOTPLUG_SHORT_DETECT      (1 << 8)
+#define PORTC_HOTPLUG_LONG_DETECT       (1 << 9)
+#define PORTB_HOTPLUG_ENABLE            (1 << 4)
+#define PORTB_PULSE_DURATION_2ms        (0)
+#define PORTB_PULSE_DURATION_4_5ms      (1 << 2)
+#define PORTB_PULSE_DURATION_6ms        (2 << 2)
+#define PORTB_PULSE_DURATION_100ms      (3 << 2)
+#define PORTB_HOTPLUG_NO_DETECT         (0)
+#define PORTB_HOTPLUG_SHORT_DETECT      (1 << 0)
+#define PORTB_HOTPLUG_LONG_DETECT       (1 << 1)
+
+#define PCH_GPIOA               0xc5010
+#define PCH_GPIOB               0xc5014
+#define PCH_GPIOC               0xc5018
+#define PCH_GPIOD               0xc501c
+#define PCH_GPIOE               0xc5020
+#define PCH_GPIOF               0xc5024
+
+#define PCH_DPLL_A              0xc6014
+#define PCH_DPLL_B              0xc6018
+
+#define PCH_FPA0                0xc6040
+#define PCH_FPA1                0xc6044
+#define PCH_FPB0                0xc6048
+#define PCH_FPB1                0xc604c
+
+#define PCH_DPLL_TEST           0xc606c
+
+#define PCH_DREF_CONTROL        0xC6200
+#define  DREF_CONTROL_MASK      0x7fc3
+#define  DREF_CPU_SOURCE_OUTPUT_DISABLE         (0<<13)
+#define  DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD      (2<<13)
+#define  DREF_CPU_SOURCE_OUTPUT_NONSPREAD       (3<<13)
+#define  DREF_CPU_SOURCE_OUTPUT_MASK           (3<<13)
+#define  DREF_SSC_SOURCE_DISABLE                (0<<11)
+#define  DREF_SSC_SOURCE_ENABLE                 (2<<11)
+#define  DREF_SSC_SOURCE_MASK                  (2<<11)
+#define  DREF_NONSPREAD_SOURCE_DISABLE          (0<<9)
+#define  DREF_NONSPREAD_CK505_ENABLE           (1<<9)
+#define  DREF_NONSPREAD_SOURCE_ENABLE           (2<<9)
+#define  DREF_NONSPREAD_SOURCE_MASK            (2<<9)
+#define  DREF_SUPERSPREAD_SOURCE_DISABLE        (0<<7)
+#define  DREF_SUPERSPREAD_SOURCE_ENABLE         (2<<7)
+#define  DREF_SSC4_DOWNSPREAD                   (0<<6)
+#define  DREF_SSC4_CENTERSPREAD                 (1<<6)
+#define  DREF_SSC1_DISABLE                      (0<<1)
+#define  DREF_SSC1_ENABLE                       (1<<1)
+#define  DREF_SSC4_DISABLE                      (0)
+#define  DREF_SSC4_ENABLE                       (1)
+
+#define PCH_RAWCLK_FREQ         0xc6204
+#define  FDL_TP1_TIMER_SHIFT    12
+#define  FDL_TP1_TIMER_MASK     (3<<12)
+#define  FDL_TP2_TIMER_SHIFT    10
+#define  FDL_TP2_TIMER_MASK     (3<<10)
+#define  RAWCLK_FREQ_MASK       0x3ff
+
+#define PCH_DPLL_TMR_CFG        0xc6208
+
+#define PCH_SSC4_PARMS          0xc6210
+#define PCH_SSC4_AUX_PARMS      0xc6214
+
+/* transcoder */
+
+#define TRANS_HTOTAL_A          0xe0000
+#define  TRANS_HTOTAL_SHIFT     16
+#define  TRANS_HACTIVE_SHIFT    0
+#define TRANS_HBLANK_A          0xe0004
+#define  TRANS_HBLANK_END_SHIFT 16
+#define  TRANS_HBLANK_START_SHIFT 0
+#define TRANS_HSYNC_A           0xe0008
+#define  TRANS_HSYNC_END_SHIFT  16
+#define  TRANS_HSYNC_START_SHIFT 0
+#define TRANS_VTOTAL_A          0xe000c
+#define  TRANS_VTOTAL_SHIFT     16
+#define  TRANS_VACTIVE_SHIFT    0
+#define TRANS_VBLANK_A          0xe0010
+#define  TRANS_VBLANK_END_SHIFT 16
+#define  TRANS_VBLANK_START_SHIFT 0
+#define TRANS_VSYNC_A           0xe0014
+#define  TRANS_VSYNC_END_SHIFT  16
+#define  TRANS_VSYNC_START_SHIFT 0
+
+#define TRANSA_DATA_M1          0xe0030
+#define TRANSA_DATA_N1          0xe0034
+#define TRANSA_DATA_M2          0xe0038
+#define TRANSA_DATA_N2          0xe003c
+#define TRANSA_DP_LINK_M1       0xe0040
+#define TRANSA_DP_LINK_N1       0xe0044
+#define TRANSA_DP_LINK_M2       0xe0048
+#define TRANSA_DP_LINK_N2       0xe004c
+
+#define TRANS_HTOTAL_B          0xe1000
+#define TRANS_HBLANK_B          0xe1004
+#define TRANS_HSYNC_B           0xe1008
+#define TRANS_VTOTAL_B          0xe100c
+#define TRANS_VBLANK_B          0xe1010
+#define TRANS_VSYNC_B           0xe1014
+
+#define TRANSB_DATA_M1          0xe1030
+#define TRANSB_DATA_N1          0xe1034
+#define TRANSB_DATA_M2          0xe1038
+#define TRANSB_DATA_N2          0xe103c
+#define TRANSB_DP_LINK_M1       0xe1040
+#define TRANSB_DP_LINK_N1       0xe1044
+#define TRANSB_DP_LINK_M2       0xe1048
+#define TRANSB_DP_LINK_N2       0xe104c
+
+#define TRANSACONF              0xf0008
+#define TRANSBCONF              0xf1008
+#define  TRANS_DISABLE          (0<<31)
+#define  TRANS_ENABLE           (1<<31)
+#define  TRANS_STATE_MASK       (1<<30)
+#define  TRANS_STATE_DISABLE    (0<<30)
+#define  TRANS_STATE_ENABLE     (1<<30)
+#define  TRANS_FSYNC_DELAY_HB1  (0<<27)
+#define  TRANS_FSYNC_DELAY_HB2  (1<<27)
+#define  TRANS_FSYNC_DELAY_HB3  (2<<27)
+#define  TRANS_FSYNC_DELAY_HB4  (3<<27)
+#define  TRANS_DP_AUDIO_ONLY    (1<<26)
+#define  TRANS_DP_VIDEO_AUDIO   (0<<26)
+#define  TRANS_PROGRESSIVE      (0<<21)
+#define  TRANS_8BPC             (0<<5)
+#define  TRANS_10BPC            (1<<5)
+#define  TRANS_6BPC             (2<<5)
+#define  TRANS_12BPC            (3<<5)
+
+#define FDI_RXA_CHICKEN         0xc200c
+#define FDI_RXB_CHICKEN         0xc2010
+#define  FDI_RX_PHASE_SYNC_POINTER_ENABLE       (1)
+
+/* CPU: FDI_TX */
+#define FDI_TXA_CTL             0x60100
+#define FDI_TXB_CTL             0x61100
+#define  FDI_TX_DISABLE         (0<<31)
+#define  FDI_TX_ENABLE          (1<<31)
+#define  FDI_LINK_TRAIN_PATTERN_1       (0<<28)
+#define  FDI_LINK_TRAIN_PATTERN_2       (1<<28)
+#define  FDI_LINK_TRAIN_PATTERN_IDLE    (2<<28)
+#define  FDI_LINK_TRAIN_NONE            (3<<28)
+#define  FDI_LINK_TRAIN_VOLTAGE_0_4V    (0<<25)
+#define  FDI_LINK_TRAIN_VOLTAGE_0_6V    (1<<25)
+#define  FDI_LINK_TRAIN_VOLTAGE_0_8V    (2<<25)
+#define  FDI_LINK_TRAIN_VOLTAGE_1_2V    (3<<25)
+#define  FDI_LINK_TRAIN_PRE_EMPHASIS_NONE (0<<22)
+#define  FDI_LINK_TRAIN_PRE_EMPHASIS_1_5X (1<<22)
+#define  FDI_LINK_TRAIN_PRE_EMPHASIS_2X   (2<<22)
+#define  FDI_LINK_TRAIN_PRE_EMPHASIS_3X   (3<<22)
+#define  FDI_DP_PORT_WIDTH_X1           (0<<19)
+#define  FDI_DP_PORT_WIDTH_X2           (1<<19)
+#define  FDI_DP_PORT_WIDTH_X3           (2<<19)
+#define  FDI_DP_PORT_WIDTH_X4           (3<<19)
+#define  FDI_TX_ENHANCE_FRAME_ENABLE    (1<<18)
+/* IGDNG: hardwired to 1 */
+#define  FDI_TX_PLL_ENABLE              (1<<14)
+/* both Tx and Rx */
+#define  FDI_SCRAMBLING_ENABLE          (0<<7)
+#define  FDI_SCRAMBLING_DISABLE         (1<<7)
+
+/* FDI_RX, FDI_X is hard-wired to Transcoder_X */
+#define FDI_RXA_CTL             0xf000c
+#define FDI_RXB_CTL             0xf100c
+#define  FDI_RX_ENABLE          (1<<31)
+#define  FDI_RX_DISABLE         (0<<31)
+/* train, dp width same as FDI_TX */
+#define  FDI_DP_PORT_WIDTH_X8           (7<<19)
+#define  FDI_8BPC                       (0<<16)
+#define  FDI_10BPC                      (1<<16)
+#define  FDI_6BPC                       (2<<16)
+#define  FDI_12BPC                      (3<<16)
+#define  FDI_LINK_REVERSE_OVERWRITE     (1<<15)
+#define  FDI_DMI_LINK_REVERSE_MASK      (1<<14)
+#define  FDI_RX_PLL_ENABLE              (1<<13)
+#define  FDI_FS_ERR_CORRECT_ENABLE      (1<<11)
+#define  FDI_FE_ERR_CORRECT_ENABLE      (1<<10)
+#define  FDI_FS_ERR_REPORT_ENABLE       (1<<9)
+#define  FDI_FE_ERR_REPORT_ENABLE       (1<<8)
+#define  FDI_RX_ENHANCE_FRAME_ENABLE    (1<<6)
+#define  FDI_SEL_RAWCLK                 (0<<4)
+#define  FDI_SEL_PCDCLK                 (1<<4)
+
+#define FDI_RXA_MISC            0xf0010
+#define FDI_RXB_MISC            0xf1010
+#define FDI_RXA_TUSIZE1         0xf0030
+#define FDI_RXA_TUSIZE2         0xf0038
+#define FDI_RXB_TUSIZE1         0xf1030
+#define FDI_RXB_TUSIZE2         0xf1038
+
+/* FDI_RX interrupt register format */
+#define FDI_RX_INTER_LANE_ALIGN         (1<<10)
+#define FDI_RX_SYMBOL_LOCK              (1<<9) /* train 2 */
+#define FDI_RX_BIT_LOCK                 (1<<8) /* train 1 */
+#define FDI_RX_TRAIN_PATTERN_2_FAIL     (1<<7)
+#define FDI_RX_FS_CODE_ERR              (1<<6)
+#define FDI_RX_FE_CODE_ERR              (1<<5)
+#define FDI_RX_SYMBOL_ERR_RATE_ABOVE    (1<<4)
+#define FDI_RX_HDCP_LINK_FAIL           (1<<3)
+#define FDI_RX_PIXEL_FIFO_OVERFLOW      (1<<2)
+#define FDI_RX_CROSS_CLOCK_OVERFLOW     (1<<1)
+#define FDI_RX_SYMBOL_QUEUE_OVERFLOW    (1<<0)
+
+#define FDI_RXA_IIR             0xf0014
+#define FDI_RXA_IMR             0xf0018
+#define FDI_RXB_IIR             0xf1014
+#define FDI_RXB_IMR             0xf1018
+
+#define FDI_PLL_CTL_1           0xfe000
+#define FDI_PLL_CTL_2           0xfe004
+
+/* CRT */
+#define PCH_ADPA                0xe1100
+#define  ADPA_TRANS_SELECT_MASK (1<<30)
+#define  ADPA_TRANS_A_SELECT    0
+#define  ADPA_TRANS_B_SELECT    (1<<30)
+#define  ADPA_CRT_HOTPLUG_MASK  0x03ff0000 /* bit 25-16 */
+#define  ADPA_CRT_HOTPLUG_MONITOR_NONE  (0<<24)
+#define  ADPA_CRT_HOTPLUG_MONITOR_MASK  (3<<24)
+#define  ADPA_CRT_HOTPLUG_MONITOR_COLOR (3<<24)
+#define  ADPA_CRT_HOTPLUG_MONITOR_MONO  (2<<24)
+#define  ADPA_CRT_HOTPLUG_ENABLE        (1<<23)
+#define  ADPA_CRT_HOTPLUG_PERIOD_64     (0<<22)
+#define  ADPA_CRT_HOTPLUG_PERIOD_128    (1<<22)
+#define  ADPA_CRT_HOTPLUG_WARMUP_5MS    (0<<21)
+#define  ADPA_CRT_HOTPLUG_WARMUP_10MS   (1<<21)
+#define  ADPA_CRT_HOTPLUG_SAMPLE_2S     (0<<20)
+#define  ADPA_CRT_HOTPLUG_SAMPLE_4S     (1<<20)
+#define  ADPA_CRT_HOTPLUG_VOLTAGE_40    (0<<18)
+#define  ADPA_CRT_HOTPLUG_VOLTAGE_50    (1<<18)
+#define  ADPA_CRT_HOTPLUG_VOLTAGE_60    (2<<18)
+#define  ADPA_CRT_HOTPLUG_VOLTAGE_70    (3<<18)
+#define  ADPA_CRT_HOTPLUG_VOLREF_325MV  (0<<17)
+#define  ADPA_CRT_HOTPLUG_VOLREF_475MV  (1<<17)
+#define  ADPA_CRT_HOTPLUG_FORCE_TRIGGER (1<<16)
+
+/* or SDVOB */
+#define HDMIB   0xe1140
+#define  PORT_ENABLE    (1 << 31)
+#define  TRANSCODER_A   (0)
+#define  TRANSCODER_B   (1 << 30)
+#define  COLOR_FORMAT_8bpc      (0)
+#define  COLOR_FORMAT_12bpc     (3 << 26)
+#define  SDVOB_HOTPLUG_ENABLE   (1 << 23)
+#define  SDVO_ENCODING          (0)
+#define  TMDS_ENCODING          (2 << 10)
+#define  NULL_PACKET_VSYNC_ENABLE       (1 << 9)
+#define  SDVOB_BORDER_ENABLE    (1 << 7)
+#define  AUDIO_ENABLE           (1 << 6)
+#define  VSYNC_ACTIVE_HIGH      (1 << 4)
+#define  HSYNC_ACTIVE_HIGH      (1 << 3)
+#define  PORT_DETECTED          (1 << 2)
+
+#define HDMIC   0xe1150
+#define HDMID   0xe1160
+
+#define PCH_LVDS       0xe1180
+#define  LVDS_DETECTED (1 << 1)
+
+#define BLC_PWM_CPU_CTL2       0x48250
+#define  PWM_ENABLE            (1 << 31)
+#define  PWM_PIPE_A            (0 << 29)
+#define  PWM_PIPE_B            (1 << 29)
+#define BLC_PWM_CPU_CTL                0x48254
+
+#define BLC_PWM_PCH_CTL1       0xc8250
+#define  PWM_PCH_ENABLE                (1 << 31)
+#define  PWM_POLARITY_ACTIVE_LOW       (1 << 29)
+#define  PWM_POLARITY_ACTIVE_HIGH      (0 << 29)
+#define  PWM_POLARITY_ACTIVE_LOW2      (1 << 28)
+#define  PWM_POLARITY_ACTIVE_HIGH2     (0 << 28)
+
+#define BLC_PWM_PCH_CTL2       0xc8254
+
+#define PCH_PP_STATUS          0xc7200
+#define PCH_PP_CONTROL         0xc7204
+#define  EDP_FORCE_VDD         (1 << 3)
+#define  EDP_BLC_ENABLE                (1 << 2)
+#define  PANEL_POWER_RESET     (1 << 1)
+#define  PANEL_POWER_OFF       (0 << 0)
+#define  PANEL_POWER_ON                (1 << 0)
+#define PCH_PP_ON_DELAYS       0xc7208
+#define  EDP_PANEL             (1 << 30)
+#define PCH_PP_OFF_DELAYS      0xc720c
+#define PCH_PP_DIVISOR         0xc7210
+
 #endif /* _I915_REG_H_ */
index ce8a21344a71e14add15215c318577fbdb569648..a98e2831ed31787ee6985fb058bdc3d32725367c 100644 (file)
@@ -295,6 +295,16 @@ int i915_save_state(struct drm_device *dev)
        i915_save_palette(dev, PIPE_B);
        dev_priv->savePIPEBSTAT = I915_READ(PIPEBSTAT);
 
+       /* Cursor state */
+       dev_priv->saveCURACNTR = I915_READ(CURACNTR);
+       dev_priv->saveCURAPOS = I915_READ(CURAPOS);
+       dev_priv->saveCURABASE = I915_READ(CURABASE);
+       dev_priv->saveCURBCNTR = I915_READ(CURBCNTR);
+       dev_priv->saveCURBPOS = I915_READ(CURBPOS);
+       dev_priv->saveCURBBASE = I915_READ(CURBBASE);
+       if (!IS_I9XX(dev))
+               dev_priv->saveCURSIZE = I915_READ(CURSIZE);
+
        /* CRT state */
        dev_priv->saveADPA = I915_READ(ADPA);
 
@@ -480,6 +490,16 @@ int i915_restore_state(struct drm_device *dev)
        I915_WRITE(DSPBCNTR, dev_priv->saveDSPBCNTR);
        I915_WRITE(DSPBADDR, I915_READ(DSPBADDR));
 
+       /* Cursor state */
+       I915_WRITE(CURAPOS, dev_priv->saveCURAPOS);
+       I915_WRITE(CURACNTR, dev_priv->saveCURACNTR);
+       I915_WRITE(CURABASE, dev_priv->saveCURABASE);
+       I915_WRITE(CURBPOS, dev_priv->saveCURBPOS);
+       I915_WRITE(CURBCNTR, dev_priv->saveCURBCNTR);
+       I915_WRITE(CURBBASE, dev_priv->saveCURBBASE);
+       if (!IS_I9XX(dev))
+               I915_WRITE(CURSIZE, dev_priv->saveCURSIZE);
+
        /* CRT state */
        I915_WRITE(ADPA, dev_priv->saveADPA);
 
index 9d78cff33b2478198a72646e183544fafa1aec8d..754dd22fdd778f4f1ba0bab2deb10a51f40ba9df 100644 (file)
@@ -30,6 +30,8 @@
 #include "i915_drv.h"
 #include "intel_bios.h"
 
+#define        SLAVE_ADDR1     0x70
+#define        SLAVE_ADDR2     0x72
 
 static void *
 find_section(struct bdb_header *bdb, int section_id)
@@ -193,6 +195,88 @@ parse_general_features(struct drm_i915_private *dev_priv,
        }
 }
 
+static void
+parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
+                      struct bdb_header *bdb)
+{
+       struct sdvo_device_mapping *p_mapping;
+       struct bdb_general_definitions *p_defs;
+       struct child_device_config *p_child;
+       int i, child_device_num, count;
+       u16     block_size, *block_ptr;
+
+       p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS);
+       if (!p_defs) {
+               DRM_DEBUG("No general definition block is found\n");
+               return;
+       }
+       /* judge whether the size of child device meets the requirements.
+        * If the child device size obtained from general definition block
+        * is different with sizeof(struct child_device_config), skip the
+        * parsing of sdvo device info
+        */
+       if (p_defs->child_dev_size != sizeof(*p_child)) {
+               /* different child dev size . Ignore it */
+               DRM_DEBUG("different child size is found. Invalid.\n");
+               return;
+       }
+       /* get the block size of general definitions */
+       block_ptr = (u16 *)((char *)p_defs - 2);
+       block_size = *block_ptr;
+       /* get the number of child device */
+       child_device_num = (block_size - sizeof(*p_defs)) /
+                               sizeof(*p_child);
+       count = 0;
+       for (i = 0; i < child_device_num; i++) {
+               p_child = &(p_defs->devices[i]);
+               if (!p_child->device_type) {
+                       /* skip the device block if device type is invalid */
+                       continue;
+               }
+               if (p_child->slave_addr != SLAVE_ADDR1 &&
+                       p_child->slave_addr != SLAVE_ADDR2) {
+                       /*
+                        * If the slave address is neither 0x70 nor 0x72,
+                        * it is not a SDVO device. Skip it.
+                        */
+                       continue;
+               }
+               if (p_child->dvo_port != DEVICE_PORT_DVOB &&
+                       p_child->dvo_port != DEVICE_PORT_DVOC) {
+                       /* skip the incorrect SDVO port */
+                       DRM_DEBUG("Incorrect SDVO port. Skip it \n");
+                       continue;
+               }
+               DRM_DEBUG("the SDVO device with slave addr %2x is found on "
+                               "%s port\n",
+                               p_child->slave_addr,
+                               (p_child->dvo_port == DEVICE_PORT_DVOB) ?
+                                       "SDVOB" : "SDVOC");
+               p_mapping = &(dev_priv->sdvo_mappings[p_child->dvo_port - 1]);
+               if (!p_mapping->initialized) {
+                       p_mapping->dvo_port = p_child->dvo_port;
+                       p_mapping->slave_addr = p_child->slave_addr;
+                       p_mapping->dvo_wiring = p_child->dvo_wiring;
+                       p_mapping->initialized = 1;
+               } else {
+                       DRM_DEBUG("Maybe one SDVO port is shared by "
+                                        "two SDVO device.\n");
+               }
+               if (p_child->slave2_addr) {
+                       /* Maybe this is a SDVO device with multiple inputs */
+                       /* And the mapping info is not added */
+                       DRM_DEBUG("there exists the slave2_addr. Maybe this "
+                               "is a SDVO device with multiple inputs.\n");
+               }
+               count++;
+       }
+
+       if (!count) {
+               /* No SDVO device info is found */
+               DRM_DEBUG("No SDVO device info is found in VBT\n");
+       }
+       return;
+}
 /**
  * intel_init_bios - initialize VBIOS settings & find VBT
  * @dev: DRM device
@@ -242,7 +326,7 @@ intel_init_bios(struct drm_device *dev)
        parse_general_features(dev_priv, bdb);
        parse_lfp_panel_data(dev_priv, bdb);
        parse_sdvo_panel_data(dev_priv, bdb);
-
+       parse_sdvo_device_mapping(dev_priv, bdb);
        pci_unmap_rom(pdev, bios);
 
        return 0;
index 8ca2cde15804598374acf140671db2747bdd0010..fe72e1c225d8ab1085a752f3ccbf6d298c44fca3 100644 (file)
@@ -135,6 +135,86 @@ struct bdb_general_features {
        u8 rsvd11:6; /* finish byte */
 } __attribute__((packed));
 
+/* pre-915 */
+#define GPIO_PIN_DVI_LVDS      0x03 /* "DVI/LVDS DDC GPIO pins" */
+#define GPIO_PIN_ADD_I2C       0x05 /* "ADDCARD I2C GPIO pins" */
+#define GPIO_PIN_ADD_DDC       0x04 /* "ADDCARD DDC GPIO pins" */
+#define GPIO_PIN_ADD_DDC_I2C   0x06 /* "ADDCARD DDC/I2C GPIO pins" */
+
+/* Pre 915 */
+#define DEVICE_TYPE_NONE       0x00
+#define DEVICE_TYPE_CRT                0x01
+#define DEVICE_TYPE_TV         0x09
+#define DEVICE_TYPE_EFP                0x12
+#define DEVICE_TYPE_LFP                0x22
+/* On 915+ */
+#define DEVICE_TYPE_CRT_DPMS           0x6001
+#define DEVICE_TYPE_CRT_DPMS_HOTPLUG   0x4001
+#define DEVICE_TYPE_TV_COMPOSITE       0x0209
+#define DEVICE_TYPE_TV_MACROVISION     0x0289
+#define DEVICE_TYPE_TV_RF_COMPOSITE    0x020c
+#define DEVICE_TYPE_TV_SVIDEO_COMPOSITE        0x0609
+#define DEVICE_TYPE_TV_SCART           0x0209
+#define DEVICE_TYPE_TV_CODEC_HOTPLUG_PWR 0x6009
+#define DEVICE_TYPE_EFP_HOTPLUG_PWR    0x6012
+#define DEVICE_TYPE_EFP_DVI_HOTPLUG_PWR        0x6052
+#define DEVICE_TYPE_EFP_DVI_I          0x6053
+#define DEVICE_TYPE_EFP_DVI_D_DUAL     0x6152
+#define DEVICE_TYPE_EFP_DVI_D_HDCP     0x60d2
+#define DEVICE_TYPE_OPENLDI_HOTPLUG_PWR        0x6062
+#define DEVICE_TYPE_OPENLDI_DUALPIX    0x6162
+#define DEVICE_TYPE_LFP_PANELLINK      0x5012
+#define DEVICE_TYPE_LFP_CMOS_PWR       0x5042
+#define DEVICE_TYPE_LFP_LVDS_PWR       0x5062
+#define DEVICE_TYPE_LFP_LVDS_DUAL      0x5162
+#define DEVICE_TYPE_LFP_LVDS_DUAL_HDCP 0x51e2
+
+#define DEVICE_CFG_NONE                0x00
+#define DEVICE_CFG_12BIT_DVOB  0x01
+#define DEVICE_CFG_12BIT_DVOC  0x02
+#define DEVICE_CFG_24BIT_DVOBC 0x09
+#define DEVICE_CFG_24BIT_DVOCB 0x0a
+#define DEVICE_CFG_DUAL_DVOB   0x11
+#define DEVICE_CFG_DUAL_DVOC   0x12
+#define DEVICE_CFG_DUAL_DVOBC  0x13
+#define DEVICE_CFG_DUAL_LINK_DVOBC     0x19
+#define DEVICE_CFG_DUAL_LINK_DVOCB     0x1a
+
+#define DEVICE_WIRE_NONE       0x00
+#define DEVICE_WIRE_DVOB       0x01
+#define DEVICE_WIRE_DVOC       0x02
+#define DEVICE_WIRE_DVOBC      0x03
+#define DEVICE_WIRE_DVOBB      0x05
+#define DEVICE_WIRE_DVOCC      0x06
+#define DEVICE_WIRE_DVOB_MASTER 0x0d
+#define DEVICE_WIRE_DVOC_MASTER 0x0e
+
+#define DEVICE_PORT_DVOA       0x00 /* none on 845+ */
+#define DEVICE_PORT_DVOB       0x01
+#define DEVICE_PORT_DVOC       0x02
+
+struct child_device_config {
+       u16 handle;
+       u16 device_type;
+       u8  device_id[10]; /* See DEVICE_TYPE_* above */
+       u16 addin_offset;
+       u8  dvo_port; /* See Device_PORT_* above */
+       u8  i2c_pin;
+       u8  slave_addr;
+       u8  ddc_pin;
+       u16 edid_ptr;
+       u8  dvo_cfg; /* See DEVICE_CFG_* above */
+       u8  dvo2_port;
+       u8  i2c2_pin;
+       u8  slave2_addr;
+       u8  ddc2_pin;
+       u8  capabilities;
+       u8  dvo_wiring;/* See DEVICE_WIRE_* above */
+       u8  dvo2_wiring;
+       u16 extended_type;
+       u8  dvo_function;
+} __attribute__((packed));
+
 struct bdb_general_definitions {
        /* DDC GPIO */
        u8 crt_ddc_gmbus_pin;
@@ -149,14 +229,19 @@ struct bdb_general_definitions {
        u8 boot_display[2];
        u8 child_dev_size;
 
-       /* device info */
-       u8 tv_or_lvds_info[33];
-       u8 dev1[33];
-       u8 dev2[33];
-       u8 dev3[33];
-       u8 dev4[33];
-       /* may be another device block here on some platforms */
-};
+       /*
+        * Device info:
+        * If TV is present, it'll be at devices[0].
+        * LVDS will be next, either devices[0] or [1], if present.
+        * On some platforms the number of device is 6. But could be as few as
+        * 4 if both TV and LVDS are missing.
+        * And the device num is related with the size of general definition
+        * block. It is obtained by using the following formula:
+        * number = (block_size - sizeof(bdb_general_definitions))/
+        *              sizeof(child_device_config);
+        */
+       struct child_device_config devices[0];
+} __attribute__((packed));
 
 struct bdb_lvds_options {
        u8 panel_type;
index 79acc4f4c1f803911ba7a99c72846b1f9f0b2db3..6de97fc66029ed2f28707649f65dfd19ea87d042 100644 (file)
@@ -37,9 +37,14 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
 {
        struct drm_device *dev = encoder->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 temp;
+       u32 temp, reg;
 
-       temp = I915_READ(ADPA);
+       if (IS_IGDNG(dev))
+               reg = PCH_ADPA;
+       else
+               reg = ADPA;
+
+       temp = I915_READ(reg);
        temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
        temp |= ADPA_DAC_ENABLE;
 
@@ -58,7 +63,7 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
                break;
        }
 
-       I915_WRITE(ADPA, temp);
+       I915_WRITE(reg, temp);
 }
 
 static int intel_crt_mode_valid(struct drm_connector *connector,
@@ -101,17 +106,23 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
        struct drm_i915_private *dev_priv = dev->dev_private;
        int dpll_md_reg;
        u32 adpa, dpll_md;
+       u32 adpa_reg;
 
        if (intel_crtc->pipe == 0)
                dpll_md_reg = DPLL_A_MD;
        else
                dpll_md_reg = DPLL_B_MD;
 
+       if (IS_IGDNG(dev))
+               adpa_reg = PCH_ADPA;
+       else
+               adpa_reg = ADPA;
+
        /*
         * Disable separate mode multiplier used when cloning SDVO to CRT
         * XXX this needs to be adjusted when we really are cloning
         */
-       if (IS_I965G(dev)) {
+       if (IS_I965G(dev) && !IS_IGDNG(dev)) {
                dpll_md = I915_READ(dpll_md_reg);
                I915_WRITE(dpll_md_reg,
                           dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK);
@@ -125,13 +136,53 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
 
        if (intel_crtc->pipe == 0) {
                adpa |= ADPA_PIPE_A_SELECT;
-               I915_WRITE(BCLRPAT_A, 0);
+               if (!IS_IGDNG(dev))
+                       I915_WRITE(BCLRPAT_A, 0);
        } else {
                adpa |= ADPA_PIPE_B_SELECT;
-               I915_WRITE(BCLRPAT_B, 0);
+               if (!IS_IGDNG(dev))
+                       I915_WRITE(BCLRPAT_B, 0);
        }
 
-       I915_WRITE(ADPA, adpa);
+       I915_WRITE(adpa_reg, adpa);
+}
+
+static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 adpa, temp;
+       bool ret;
+
+       temp = adpa = I915_READ(PCH_ADPA);
+
+       adpa &= ~ADPA_CRT_HOTPLUG_MASK;
+
+       adpa |= (ADPA_CRT_HOTPLUG_PERIOD_128 |
+                       ADPA_CRT_HOTPLUG_WARMUP_10MS |
+                       ADPA_CRT_HOTPLUG_SAMPLE_4S |
+                       ADPA_CRT_HOTPLUG_VOLTAGE_50 | /* default */
+                       ADPA_CRT_HOTPLUG_VOLREF_325MV |
+                       ADPA_CRT_HOTPLUG_ENABLE |
+                       ADPA_CRT_HOTPLUG_FORCE_TRIGGER);
+
+       DRM_DEBUG("pch crt adpa 0x%x", adpa);
+       I915_WRITE(PCH_ADPA, adpa);
+
+       /* This might not be needed as not specified in spec...*/
+       udelay(1000);
+
+       /* Check the status to see if both blue and green are on now */
+       adpa = I915_READ(PCH_ADPA);
+       if ((adpa & ADPA_CRT_HOTPLUG_MONITOR_MASK) ==
+                       ADPA_CRT_HOTPLUG_MONITOR_COLOR)
+               ret = true;
+       else
+               ret = false;
+
+       /* restore origin register */
+       I915_WRITE(PCH_ADPA, temp);
+       return ret;
 }
 
 /**
@@ -148,6 +199,10 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 hotplug_en;
        int i, tries = 0;
+
+       if (IS_IGDNG(dev))
+               return intel_igdng_crt_detect_hotplug(connector);
+
        /*
         * On 4 series desktop, CRT detect sequence need to be done twice
         * to get a reliable result.
@@ -423,6 +478,7 @@ void intel_crt_init(struct drm_device *dev)
 {
        struct drm_connector *connector;
        struct intel_output *intel_output;
+       u32 i2c_reg;
 
        intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
        if (!intel_output)
@@ -439,7 +495,11 @@ void intel_crt_init(struct drm_device *dev)
                                          &intel_output->enc);
 
        /* Set up the DDC bus. */
-       intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A");
+       if (IS_IGDNG(dev))
+               i2c_reg = PCH_GPIOA;
+       else
+               i2c_reg = GPIOA;
+       intel_output->ddc_bus = intel_i2c_create(dev, i2c_reg, "CRTDDC_A");
        if (!intel_output->ddc_bus) {
                dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
                           "failed.\n");
index c9d6f10ba92eb833620d1c686c234ffc574eb81a..028f5b66e3d83eaedd66eeaea168ca68cde185ef 100644 (file)
@@ -137,6 +137,8 @@ struct intel_limit {
 #define INTEL_LIMIT_G4X_DUAL_CHANNEL_LVDS   7
 #define INTEL_LIMIT_IGD_SDVO_DAC    8
 #define INTEL_LIMIT_IGD_LVDS       9
+#define INTEL_LIMIT_IGDNG_SDVO_DAC  10
+#define INTEL_LIMIT_IGDNG_LVDS     11
 
 /*The parameter is for SDVO on G4x platform*/
 #define G4X_DOT_SDVO_MIN           25000
@@ -216,12 +218,43 @@ struct intel_limit {
 #define G4X_P2_DUAL_CHANNEL_LVDS_FAST           7
 #define G4X_P2_DUAL_CHANNEL_LVDS_LIMIT          0
 
+/* IGDNG */
+/* as we calculate clock using (register_value + 2) for
+   N/M1/M2, so here the range value for them is (actual_value-2).
+ */
+#define IGDNG_DOT_MIN         25000
+#define IGDNG_DOT_MAX         350000
+#define IGDNG_VCO_MIN         1760000
+#define IGDNG_VCO_MAX         3510000
+#define IGDNG_N_MIN           1
+#define IGDNG_N_MAX           5
+#define IGDNG_M_MIN           79
+#define IGDNG_M_MAX           118
+#define IGDNG_M1_MIN          12
+#define IGDNG_M1_MAX          23
+#define IGDNG_M2_MIN          5
+#define IGDNG_M2_MAX          9
+#define IGDNG_P_SDVO_DAC_MIN  5
+#define IGDNG_P_SDVO_DAC_MAX  80
+#define IGDNG_P_LVDS_MIN      28
+#define IGDNG_P_LVDS_MAX      112
+#define IGDNG_P1_MIN          1
+#define IGDNG_P1_MAX          8
+#define IGDNG_P2_SDVO_DAC_SLOW 10
+#define IGDNG_P2_SDVO_DAC_FAST 5
+#define IGDNG_P2_LVDS_SLOW    14 /* single channel */
+#define IGDNG_P2_LVDS_FAST    7  /* double channel */
+#define IGDNG_P2_DOT_LIMIT    225000 /* 225Mhz */
+
 static bool
 intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
                    int target, int refclk, intel_clock_t *best_clock);
 static bool
 intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
                        int target, int refclk, intel_clock_t *best_clock);
+static bool
+intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+                       int target, int refclk, intel_clock_t *best_clock);
 
 static const intel_limit_t intel_limits[] = {
     { /* INTEL_LIMIT_I8XX_DVO_DAC */
@@ -383,9 +416,47 @@ static const intel_limit_t intel_limits[] = {
                 .p2_slow = I9XX_P2_LVDS_SLOW,  .p2_fast = I9XX_P2_LVDS_SLOW },
        .find_pll = intel_find_best_PLL,
     },
-
+    { /* INTEL_LIMIT_IGDNG_SDVO_DAC */
+       .dot = { .min = IGDNG_DOT_MIN,          .max = IGDNG_DOT_MAX },
+       .vco = { .min = IGDNG_VCO_MIN,          .max = IGDNG_VCO_MAX },
+       .n   = { .min = IGDNG_N_MIN,            .max = IGDNG_N_MAX },
+       .m   = { .min = IGDNG_M_MIN,            .max = IGDNG_M_MAX },
+       .m1  = { .min = IGDNG_M1_MIN,           .max = IGDNG_M1_MAX },
+       .m2  = { .min = IGDNG_M2_MIN,           .max = IGDNG_M2_MAX },
+       .p   = { .min = IGDNG_P_SDVO_DAC_MIN,   .max = IGDNG_P_SDVO_DAC_MAX },
+       .p1  = { .min = IGDNG_P1_MIN,           .max = IGDNG_P1_MAX },
+       .p2  = { .dot_limit = IGDNG_P2_DOT_LIMIT,
+                .p2_slow = IGDNG_P2_SDVO_DAC_SLOW,
+                .p2_fast = IGDNG_P2_SDVO_DAC_FAST },
+       .find_pll = intel_igdng_find_best_PLL,
+    },
+    { /* INTEL_LIMIT_IGDNG_LVDS */
+       .dot = { .min = IGDNG_DOT_MIN,          .max = IGDNG_DOT_MAX },
+       .vco = { .min = IGDNG_VCO_MIN,          .max = IGDNG_VCO_MAX },
+       .n   = { .min = IGDNG_N_MIN,            .max = IGDNG_N_MAX },
+       .m   = { .min = IGDNG_M_MIN,            .max = IGDNG_M_MAX },
+       .m1  = { .min = IGDNG_M1_MIN,           .max = IGDNG_M1_MAX },
+       .m2  = { .min = IGDNG_M2_MIN,           .max = IGDNG_M2_MAX },
+       .p   = { .min = IGDNG_P_LVDS_MIN,       .max = IGDNG_P_LVDS_MAX },
+       .p1  = { .min = IGDNG_P1_MIN,           .max = IGDNG_P1_MAX },
+       .p2  = { .dot_limit = IGDNG_P2_DOT_LIMIT,
+                .p2_slow = IGDNG_P2_LVDS_SLOW,
+                .p2_fast = IGDNG_P2_LVDS_FAST },
+       .find_pll = intel_igdng_find_best_PLL,
+    },
 };
 
+static const intel_limit_t *intel_igdng_limit(struct drm_crtc *crtc)
+{
+       const intel_limit_t *limit;
+       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+               limit = &intel_limits[INTEL_LIMIT_IGDNG_LVDS];
+       else
+               limit = &intel_limits[INTEL_LIMIT_IGDNG_SDVO_DAC];
+
+       return limit;
+}
+
 static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
@@ -418,7 +489,9 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
        struct drm_device *dev = crtc->dev;
        const intel_limit_t *limit;
 
-       if (IS_G4X(dev)) {
+       if (IS_IGDNG(dev))
+               limit = intel_igdng_limit(crtc);
+       else if (IS_G4X(dev)) {
                limit = intel_g4x_limit(crtc);
        } else if (IS_I9XX(dev) && !IS_IGD(dev)) {
                if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
@@ -630,7 +703,64 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
                        }
                }
        }
+       return found;
+}
 
+static bool
+intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+                       int target, int refclk, intel_clock_t *best_clock)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       intel_clock_t clock;
+       int max_n;
+       bool found;
+       int err_most = 47;
+       found = false;
+
+       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+               if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
+                   LVDS_CLKB_POWER_UP)
+                       clock.p2 = limit->p2.p2_fast;
+               else
+                       clock.p2 = limit->p2.p2_slow;
+       } else {
+               if (target < limit->p2.dot_limit)
+                       clock.p2 = limit->p2.p2_slow;
+               else
+                       clock.p2 = limit->p2.p2_fast;
+       }
+
+       memset(best_clock, 0, sizeof(*best_clock));
+       max_n = limit->n.max;
+       /* based on hardware requriment prefer smaller n to precision */
+       for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) {
+               /* based on hardware requirment prefere larger m1,m2, p1 */
+               for (clock.m1 = limit->m1.max;
+                    clock.m1 >= limit->m1.min; clock.m1--) {
+                       for (clock.m2 = limit->m2.max;
+                            clock.m2 >= limit->m2.min; clock.m2--) {
+                               for (clock.p1 = limit->p1.max;
+                                    clock.p1 >= limit->p1.min; clock.p1--) {
+                                       int this_err;
+
+                                       intel_clock(dev, refclk, &clock);
+                                       if (!intel_PLL_is_valid(crtc, &clock))
+                                               continue;
+                                       this_err = abs((10000 - (target*10000/clock.dot)));
+                                       if (this_err < err_most) {
+                                               *best_clock = clock;
+                                               err_most = this_err;
+                                               max_n = clock.n;
+                                               found = true;
+                                               /* found on first matching */
+                                               goto out;
+                                       }
+                               }
+                       }
+               }
+       }
+out:
        return found;
 }
 
@@ -785,18 +915,292 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
        return 0;
 }
 
+static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int pipe = intel_crtc->pipe;
+       int plane = intel_crtc->pipe;
+       int pch_dpll_reg = (pipe == 0) ? PCH_DPLL_A : PCH_DPLL_B;
+       int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+       int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR;
+       int dspbase_reg = (plane == 0) ? DSPAADDR : DSPBADDR;
+       int fdi_tx_reg = (pipe == 0) ? FDI_TXA_CTL : FDI_TXB_CTL;
+       int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL;
+       int fdi_rx_iir_reg = (pipe == 0) ? FDI_RXA_IIR : FDI_RXB_IIR;
+       int fdi_rx_imr_reg = (pipe == 0) ? FDI_RXA_IMR : FDI_RXB_IMR;
+       int transconf_reg = (pipe == 0) ? TRANSACONF : TRANSBCONF;
+       int pf_ctl_reg = (pipe == 0) ? PFA_CTL_1 : PFB_CTL_1;
+       int cpu_htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B;
+       int cpu_hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B;
+       int cpu_hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B;
+       int cpu_vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B;
+       int cpu_vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B;
+       int cpu_vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B;
+       int trans_htot_reg = (pipe == 0) ? TRANS_HTOTAL_A : TRANS_HTOTAL_B;
+       int trans_hblank_reg = (pipe == 0) ? TRANS_HBLANK_A : TRANS_HBLANK_B;
+       int trans_hsync_reg = (pipe == 0) ? TRANS_HSYNC_A : TRANS_HSYNC_B;
+       int trans_vtot_reg = (pipe == 0) ? TRANS_VTOTAL_A : TRANS_VTOTAL_B;
+       int trans_vblank_reg = (pipe == 0) ? TRANS_VBLANK_A : TRANS_VBLANK_B;
+       int trans_vsync_reg = (pipe == 0) ? TRANS_VSYNC_A : TRANS_VSYNC_B;
+       u32 temp;
+       int tries = 5, j;
 
+       /* XXX: When our outputs are all unaware of DPMS modes other than off
+        * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
+        */
+       switch (mode) {
+       case DRM_MODE_DPMS_ON:
+       case DRM_MODE_DPMS_STANDBY:
+       case DRM_MODE_DPMS_SUSPEND:
+               DRM_DEBUG("crtc %d dpms on\n", pipe);
+               /* enable PCH DPLL */
+               temp = I915_READ(pch_dpll_reg);
+               if ((temp & DPLL_VCO_ENABLE) == 0) {
+                       I915_WRITE(pch_dpll_reg, temp | DPLL_VCO_ENABLE);
+                       I915_READ(pch_dpll_reg);
+               }
 
-/**
- * Sets the power management mode of the pipe and plane.
- *
- * This code should probably grow support for turning the cursor off and back
- * on appropriately at the same time as we're turning the pipe off/on.
- */
-static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
+               /* enable PCH FDI RX PLL, wait warmup plus DMI latency */
+               temp = I915_READ(fdi_rx_reg);
+               I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE |
+                               FDI_SEL_PCDCLK |
+                               FDI_DP_PORT_WIDTH_X4); /* default 4 lanes */
+               I915_READ(fdi_rx_reg);
+               udelay(200);
+
+               /* Enable CPU FDI TX PLL, always on for IGDNG */
+               temp = I915_READ(fdi_tx_reg);
+               if ((temp & FDI_TX_PLL_ENABLE) == 0) {
+                       I915_WRITE(fdi_tx_reg, temp | FDI_TX_PLL_ENABLE);
+                       I915_READ(fdi_tx_reg);
+                       udelay(100);
+               }
+
+               /* Enable CPU pipe */
+               temp = I915_READ(pipeconf_reg);
+               if ((temp & PIPEACONF_ENABLE) == 0) {
+                       I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE);
+                       I915_READ(pipeconf_reg);
+                       udelay(100);
+               }
+
+               /* configure and enable CPU plane */
+               temp = I915_READ(dspcntr_reg);
+               if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
+                       I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE);
+                       /* Flush the plane changes */
+                       I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+               }
+
+               /* enable CPU FDI TX and PCH FDI RX */
+               temp = I915_READ(fdi_tx_reg);
+               temp |= FDI_TX_ENABLE;
+               temp |= FDI_DP_PORT_WIDTH_X4; /* default */
+               temp &= ~FDI_LINK_TRAIN_NONE;
+               temp |= FDI_LINK_TRAIN_PATTERN_1;
+               I915_WRITE(fdi_tx_reg, temp);
+               I915_READ(fdi_tx_reg);
+
+               temp = I915_READ(fdi_rx_reg);
+               temp &= ~FDI_LINK_TRAIN_NONE;
+               temp |= FDI_LINK_TRAIN_PATTERN_1;
+               I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENABLE);
+               I915_READ(fdi_rx_reg);
+
+               udelay(150);
+
+               /* Train FDI. */
+               /* umask FDI RX Interrupt symbol_lock and bit_lock bit
+                  for train result */
+               temp = I915_READ(fdi_rx_imr_reg);
+               temp &= ~FDI_RX_SYMBOL_LOCK;
+               temp &= ~FDI_RX_BIT_LOCK;
+               I915_WRITE(fdi_rx_imr_reg, temp);
+               I915_READ(fdi_rx_imr_reg);
+               udelay(150);
+
+               temp = I915_READ(fdi_rx_iir_reg);
+               DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+
+               if ((temp & FDI_RX_BIT_LOCK) == 0) {
+                       for (j = 0; j < tries; j++) {
+                               temp = I915_READ(fdi_rx_iir_reg);
+                               DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+                               if (temp & FDI_RX_BIT_LOCK)
+                                       break;
+                               udelay(200);
+                       }
+                       if (j != tries)
+                               I915_WRITE(fdi_rx_iir_reg,
+                                               temp | FDI_RX_BIT_LOCK);
+                       else
+                               DRM_DEBUG("train 1 fail\n");
+               } else {
+                       I915_WRITE(fdi_rx_iir_reg,
+                                       temp | FDI_RX_BIT_LOCK);
+                       DRM_DEBUG("train 1 ok 2!\n");
+               }
+               temp = I915_READ(fdi_tx_reg);
+               temp &= ~FDI_LINK_TRAIN_NONE;
+               temp |= FDI_LINK_TRAIN_PATTERN_2;
+               I915_WRITE(fdi_tx_reg, temp);
+
+               temp = I915_READ(fdi_rx_reg);
+               temp &= ~FDI_LINK_TRAIN_NONE;
+               temp |= FDI_LINK_TRAIN_PATTERN_2;
+               I915_WRITE(fdi_rx_reg, temp);
+
+               udelay(150);
+
+               temp = I915_READ(fdi_rx_iir_reg);
+               DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+
+               if ((temp & FDI_RX_SYMBOL_LOCK) == 0) {
+                       for (j = 0; j < tries; j++) {
+                               temp = I915_READ(fdi_rx_iir_reg);
+                               DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+                               if (temp & FDI_RX_SYMBOL_LOCK)
+                                       break;
+                               udelay(200);
+                       }
+                       if (j != tries) {
+                               I915_WRITE(fdi_rx_iir_reg,
+                                               temp | FDI_RX_SYMBOL_LOCK);
+                               DRM_DEBUG("train 2 ok 1!\n");
+                       } else
+                               DRM_DEBUG("train 2 fail\n");
+               } else {
+                       I915_WRITE(fdi_rx_iir_reg, temp | FDI_RX_SYMBOL_LOCK);
+                       DRM_DEBUG("train 2 ok 2!\n");
+               }
+               DRM_DEBUG("train done\n");
+
+               /* set transcoder timing */
+               I915_WRITE(trans_htot_reg, I915_READ(cpu_htot_reg));
+               I915_WRITE(trans_hblank_reg, I915_READ(cpu_hblank_reg));
+               I915_WRITE(trans_hsync_reg, I915_READ(cpu_hsync_reg));
+
+               I915_WRITE(trans_vtot_reg, I915_READ(cpu_vtot_reg));
+               I915_WRITE(trans_vblank_reg, I915_READ(cpu_vblank_reg));
+               I915_WRITE(trans_vsync_reg, I915_READ(cpu_vsync_reg));
+
+               /* enable PCH transcoder */
+               temp = I915_READ(transconf_reg);
+               I915_WRITE(transconf_reg, temp | TRANS_ENABLE);
+               I915_READ(transconf_reg);
+
+               while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) == 0)
+                       ;
+
+               /* enable normal */
+
+               temp = I915_READ(fdi_tx_reg);
+               temp &= ~FDI_LINK_TRAIN_NONE;
+               I915_WRITE(fdi_tx_reg, temp | FDI_LINK_TRAIN_NONE |
+                               FDI_TX_ENHANCE_FRAME_ENABLE);
+               I915_READ(fdi_tx_reg);
+
+               temp = I915_READ(fdi_rx_reg);
+               temp &= ~FDI_LINK_TRAIN_NONE;
+               I915_WRITE(fdi_rx_reg, temp | FDI_LINK_TRAIN_NONE |
+                               FDI_RX_ENHANCE_FRAME_ENABLE);
+               I915_READ(fdi_rx_reg);
+
+               /* wait one idle pattern time */
+               udelay(100);
+
+               intel_crtc_load_lut(crtc);
+
+       break;
+       case DRM_MODE_DPMS_OFF:
+               DRM_DEBUG("crtc %d dpms off\n", pipe);
+
+               /* Disable the VGA plane that we never use */
+               I915_WRITE(CPU_VGACNTRL, VGA_DISP_DISABLE);
+
+               /* Disable display plane */
+               temp = I915_READ(dspcntr_reg);
+               if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
+                       I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE);
+                       /* Flush the plane changes */
+                       I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+                       I915_READ(dspbase_reg);
+               }
+
+               /* disable cpu pipe, disable after all planes disabled */
+               temp = I915_READ(pipeconf_reg);
+               if ((temp & PIPEACONF_ENABLE) != 0) {
+                       I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE);
+                       I915_READ(pipeconf_reg);
+                       /* wait for cpu pipe off, pipe state */
+                       while ((I915_READ(pipeconf_reg) & I965_PIPECONF_ACTIVE) != 0)
+                               ;
+               } else
+                       DRM_DEBUG("crtc %d is disabled\n", pipe);
+
+               /* IGDNG-A : disable cpu panel fitter ? */
+               temp = I915_READ(pf_ctl_reg);
+               if ((temp & PF_ENABLE) != 0) {
+                       I915_WRITE(pf_ctl_reg, temp & ~PF_ENABLE);
+                       I915_READ(pf_ctl_reg);
+               }
+
+               /* disable CPU FDI tx and PCH FDI rx */
+               temp = I915_READ(fdi_tx_reg);
+               I915_WRITE(fdi_tx_reg, temp & ~FDI_TX_ENABLE);
+               I915_READ(fdi_tx_reg);
+
+               temp = I915_READ(fdi_rx_reg);
+               I915_WRITE(fdi_rx_reg, temp & ~FDI_RX_ENABLE);
+               I915_READ(fdi_rx_reg);
+
+               /* still set train pattern 1 */
+               temp = I915_READ(fdi_tx_reg);
+               temp &= ~FDI_LINK_TRAIN_NONE;
+               temp |= FDI_LINK_TRAIN_PATTERN_1;
+               I915_WRITE(fdi_tx_reg, temp);
+
+               temp = I915_READ(fdi_rx_reg);
+               temp &= ~FDI_LINK_TRAIN_NONE;
+               temp |= FDI_LINK_TRAIN_PATTERN_1;
+               I915_WRITE(fdi_rx_reg, temp);
+
+               /* disable PCH transcoder */
+               temp = I915_READ(transconf_reg);
+               if ((temp & TRANS_ENABLE) != 0) {
+                       I915_WRITE(transconf_reg, temp & ~TRANS_ENABLE);
+                       I915_READ(transconf_reg);
+                       /* wait for PCH transcoder off, transcoder state */
+                       while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) != 0)
+                               ;
+               }
+
+               /* disable PCH DPLL */
+               temp = I915_READ(pch_dpll_reg);
+               if ((temp & DPLL_VCO_ENABLE) != 0) {
+                       I915_WRITE(pch_dpll_reg, temp & ~DPLL_VCO_ENABLE);
+                       I915_READ(pch_dpll_reg);
+               }
+
+               temp = I915_READ(fdi_rx_reg);
+               if ((temp & FDI_RX_PLL_ENABLE) != 0) {
+                       temp &= ~FDI_SEL_PCDCLK;
+                       temp &= ~FDI_RX_PLL_ENABLE;
+                       I915_WRITE(fdi_rx_reg, temp);
+                       I915_READ(fdi_rx_reg);
+               }
+
+               /* Wait for the clocks to turn off. */
+               udelay(150);
+               break;
+       }
+}
+
+static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
 {
        struct drm_device *dev = crtc->dev;
-       struct drm_i915_master_private *master_priv;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int pipe = intel_crtc->pipe;
@@ -805,7 +1209,6 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
        int dspbase_reg = (pipe == 0) ? DSPAADDR : DSPBADDR;
        int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
        u32 temp;
-       bool enabled;
 
        /* XXX: When our outputs are all unaware of DPMS modes other than off
         * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
@@ -890,6 +1293,26 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
                udelay(150);
                break;
        }
+}
+
+/**
+ * Sets the power management mode of the pipe and plane.
+ *
+ * This code should probably grow support for turning the cursor off and back
+ * on appropriately at the same time as we're turning the pipe off/on.
+ */
+static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_master_private *master_priv;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int pipe = intel_crtc->pipe;
+       bool enabled;
+
+       if (IS_IGDNG(dev))
+               igdng_crtc_dpms(crtc, mode);
+       else
+               i9xx_crtc_dpms(crtc, mode);
 
        if (!dev->primary->master)
                return;
@@ -947,6 +1370,12 @@ static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
                                  struct drm_display_mode *mode,
                                  struct drm_display_mode *adjusted_mode)
 {
+       struct drm_device *dev = crtc->dev;
+       if (IS_IGDNG(dev)) {
+               /* FDI link clock is fixed at 2.7G */
+               if (mode->clock * 3 > 27000 * 4)
+                       return MODE_CLOCK_HIGH;
+       }
        return true;
 }
 
@@ -1030,6 +1459,48 @@ static int intel_panel_fitter_pipe (struct drm_device *dev)
        return 1;
 }
 
+struct fdi_m_n {
+       u32        tu;
+       u32        gmch_m;
+       u32        gmch_n;
+       u32        link_m;
+       u32        link_n;
+};
+
+static void
+fdi_reduce_ratio(u32 *num, u32 *den)
+{
+       while (*num > 0xffffff || *den > 0xffffff) {
+               *num >>= 1;
+               *den >>= 1;
+       }
+}
+
+#define DATA_N 0x800000
+#define LINK_N 0x80000
+
+static void
+igdng_compute_m_n(int bytes_per_pixel, int nlanes,
+               int pixel_clock, int link_clock,
+               struct fdi_m_n *m_n)
+{
+       u64 temp;
+
+       m_n->tu = 64; /* default size */
+
+       temp = (u64) DATA_N * pixel_clock;
+       temp = div_u64(temp, link_clock);
+       m_n->gmch_m = (temp * bytes_per_pixel) / nlanes;
+       m_n->gmch_n = DATA_N;
+       fdi_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n);
+
+       temp = (u64) LINK_N * pixel_clock;
+       m_n->link_m = div_u64(temp, link_clock);
+       m_n->link_n = LINK_N;
+       fdi_reduce_ratio(&m_n->link_m, &m_n->link_n);
+}
+
+
 static int intel_crtc_mode_set(struct drm_crtc *crtc,
                               struct drm_display_mode *mode,
                               struct drm_display_mode *adjusted_mode,
@@ -1063,6 +1534,17 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        struct drm_connector *connector;
        const intel_limit_t *limit;
        int ret;
+       struct fdi_m_n m_n = {0};
+       int data_m1_reg = (pipe == 0) ? PIPEA_DATA_M1 : PIPEB_DATA_M1;
+       int data_n1_reg = (pipe == 0) ? PIPEA_DATA_N1 : PIPEB_DATA_N1;
+       int link_m1_reg = (pipe == 0) ? PIPEA_LINK_M1 : PIPEB_LINK_M1;
+       int link_n1_reg = (pipe == 0) ? PIPEA_LINK_N1 : PIPEB_LINK_N1;
+       int pch_fp_reg = (pipe == 0) ? PCH_FPA0 : PCH_FPB0;
+       int pch_dpll_reg = (pipe == 0) ? PCH_DPLL_A : PCH_DPLL_B;
+       int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL;
+       int lvds_reg = LVDS;
+       u32 temp;
+       int sdvo_pixel_multiply;
 
        drm_vblank_pre_modeset(dev, pipe);
 
@@ -1101,6 +1583,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                DRM_DEBUG("using SSC reference clock of %d MHz\n", refclk / 1000);
        } else if (IS_I9XX(dev)) {
                refclk = 96000;
+               if (IS_IGDNG(dev))
+                       refclk = 120000; /* 120Mhz refclk */
        } else {
                refclk = 48000;
        }
@@ -1114,6 +1598,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock);
        if (!ok) {
                DRM_ERROR("Couldn't find PLL settings for mode!\n");
+               drm_vblank_post_modeset(dev, pipe);
                return -EINVAL;
        }
 
@@ -1137,12 +1622,21 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                }
        }
 
+       /* FDI link */
+       if (IS_IGDNG(dev))
+               igdng_compute_m_n(3, 4, /* lane num 4 */
+                               adjusted_mode->clock,
+                               270000, /* lane clock */
+                               &m_n);
+
        if (IS_IGD(dev))
                fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2;
        else
                fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
 
-       dpll = DPLL_VGA_MODE_DIS;
+       if (!IS_IGDNG(dev))
+               dpll = DPLL_VGA_MODE_DIS;
+
        if (IS_I9XX(dev)) {
                if (is_lvds)
                        dpll |= DPLLB_MODE_LVDS;
@@ -1150,17 +1644,22 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                        dpll |= DPLLB_MODE_DAC_SERIAL;
                if (is_sdvo) {
                        dpll |= DPLL_DVO_HIGH_SPEED;
-                       if (IS_I945G(dev) || IS_I945GM(dev)) {
-                               int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+                       sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+                       if (IS_I945G(dev) || IS_I945GM(dev))
                                dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
-                       }
+                       else if (IS_IGDNG(dev))
+                               dpll |= (sdvo_pixel_multiply - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
                }
 
                /* compute bitmask from p1 value */
                if (IS_IGD(dev))
                        dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT_IGD;
-               else
+               else {
                        dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
+                       /* also FPA1 */
+                       if (IS_IGDNG(dev))
+                               dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
+               }
                switch (clock.p2) {
                case 5:
                        dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5;
@@ -1175,7 +1674,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                        dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
                        break;
                }
-               if (IS_I965G(dev))
+               if (IS_I965G(dev) && !IS_IGDNG(dev))
                        dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
        } else {
                if (is_lvds) {
@@ -1207,10 +1706,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        /* Set up the display plane register */
        dspcntr = DISPPLANE_GAMMA_ENABLE;
 
-       if (pipe == 0)
-               dspcntr |= DISPPLANE_SEL_PIPE_A;
-       else
-               dspcntr |= DISPPLANE_SEL_PIPE_B;
+       /* IGDNG's plane is forced to pipe, bit 24 is to
+          enable color space conversion */
+       if (!IS_IGDNG(dev)) {
+               if (pipe == 0)
+                       dspcntr |= DISPPLANE_SEL_PIPE_A;
+               else
+                       dspcntr |= DISPPLANE_SEL_PIPE_B;
+       }
 
        if (pipe == 0 && !IS_I965G(dev)) {
                /* Enable pixel doubling when the dot clock is > 90% of the (display)
@@ -1231,12 +1734,17 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 
 
        /* Disable the panel fitter if it was on our pipe */
-       if (intel_panel_fitter_pipe(dev) == pipe)
+       if (!IS_IGDNG(dev) && intel_panel_fitter_pipe(dev) == pipe)
                I915_WRITE(PFIT_CONTROL, 0);
 
        DRM_DEBUG("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
        drm_mode_debug_printmodeline(mode);
 
+       /* assign to IGDNG registers */
+       if (IS_IGDNG(dev)) {
+               fp_reg = pch_fp_reg;
+               dpll_reg = pch_dpll_reg;
+       }
 
        if (dpll & DPLL_VCO_ENABLE) {
                I915_WRITE(fp_reg, fp);
@@ -1245,13 +1753,33 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                udelay(150);
        }
 
+       if (IS_IGDNG(dev)) {
+               /* enable PCH clock reference source */
+               /* XXX need to change the setting for other outputs */
+               u32 temp;
+               temp = I915_READ(PCH_DREF_CONTROL);
+               temp &= ~DREF_NONSPREAD_SOURCE_MASK;
+               temp |= DREF_NONSPREAD_CK505_ENABLE;
+               temp &= ~DREF_SSC_SOURCE_MASK;
+               temp |= DREF_SSC_SOURCE_ENABLE;
+               temp &= ~DREF_SSC1_ENABLE;
+               /* if no eDP, disable source output to CPU */
+               temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
+               temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
+               I915_WRITE(PCH_DREF_CONTROL, temp);
+       }
+
        /* The LVDS pin pair needs to be on before the DPLLs are enabled.
         * This is an exception to the general rule that mode_set doesn't turn
         * things on.
         */
        if (is_lvds) {
-               u32 lvds = I915_READ(LVDS);
+               u32 lvds;
 
+               if (IS_IGDNG(dev))
+                       lvds_reg = PCH_LVDS;
+
+               lvds = I915_READ(lvds_reg);
                lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT;
                /* Set the B0-B3 data pairs corresponding to whether we're going to
                 * set the DPLLs for dual-channel mode or not.
@@ -1266,8 +1794,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                 * panels behave in the two modes.
                 */
 
-               I915_WRITE(LVDS, lvds);
-               I915_READ(LVDS);
+               I915_WRITE(lvds_reg, lvds);
+               I915_READ(lvds_reg);
        }
 
        I915_WRITE(fp_reg, fp);
@@ -1276,8 +1804,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        /* Wait for the clocks to stabilize. */
        udelay(150);
 
-       if (IS_I965G(dev)) {
-               int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+       if (IS_I965G(dev) && !IS_IGDNG(dev)) {
+               sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
                I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
                           ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT));
        } else {
@@ -1303,9 +1831,25 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        /* pipesrc and dspsize control the size that is scaled from, which should
         * always be the user's requested size.
         */
-       I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1));
-       I915_WRITE(dsppos_reg, 0);
+       if (!IS_IGDNG(dev)) {
+               I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) |
+                               (mode->hdisplay - 1));
+               I915_WRITE(dsppos_reg, 0);
+       }
        I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
+
+       if (IS_IGDNG(dev)) {
+               I915_WRITE(data_m1_reg, TU_SIZE(m_n.tu) | m_n.gmch_m);
+               I915_WRITE(data_n1_reg, TU_SIZE(m_n.tu) | m_n.gmch_n);
+               I915_WRITE(link_m1_reg, m_n.link_m);
+               I915_WRITE(link_n1_reg, m_n.link_n);
+
+                /* enable FDI RX PLL too */
+               temp = I915_READ(fdi_rx_reg);
+               I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE);
+               udelay(200);
+       }
+
        I915_WRITE(pipeconf_reg, pipeconf);
        I915_READ(pipeconf_reg);
 
@@ -1315,12 +1859,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 
        /* Flush the plane changes */
        ret = intel_pipe_set_base(crtc, x, y, old_fb);
-       if (ret != 0)
-           return ret;
-
        drm_vblank_post_modeset(dev, pipe);
 
-       return 0;
+       return ret;
 }
 
 /** Loads the palette/gamma unit for the CRTC with the prepared values */
@@ -1336,6 +1877,11 @@ void intel_crtc_load_lut(struct drm_crtc *crtc)
        if (!crtc->enabled)
                return;
 
+       /* use legacy palette for IGDNG */
+       if (IS_IGDNG(dev))
+               palreg = (intel_crtc->pipe == 0) ? LGC_PALETTE_A :
+                                                  LGC_PALETTE_B;
+
        for (i = 0; i < 256; i++) {
                I915_WRITE(palreg + 4 * i,
                           (intel_crtc->lut_r[i] << 16) |
@@ -1464,16 +2010,16 @@ static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
        uint32_t adder;
 
        if (x < 0) {
-               temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT);
+               temp |= CURSOR_POS_SIGN << CURSOR_X_SHIFT;
                x = -x;
        }
        if (y < 0) {
-               temp |= (CURSOR_POS_SIGN << CURSOR_Y_SHIFT);
+               temp |= CURSOR_POS_SIGN << CURSOR_Y_SHIFT;
                y = -y;
        }
 
-       temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT);
-       temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT);
+       temp |= x << CURSOR_X_SHIFT;
+       temp |= y << CURSOR_Y_SHIFT;
 
        adder = intel_crtc->cursor_addr;
        I915_WRITE((pipe == 0) ? CURAPOS : CURBPOS, temp);
@@ -1590,6 +2136,7 @@ struct drm_crtc *intel_get_load_detect_pipe(struct intel_output *intel_output,
        }
 
        encoder->crtc = crtc;
+       intel_output->base.encoder = encoder;
        intel_output->load_detect_temp = true;
 
        intel_crtc = to_intel_crtc(crtc);
@@ -1625,6 +2172,7 @@ void intel_release_load_detect_pipe(struct intel_output *intel_output, int dpms_
 
        if (intel_output->load_detect_temp) {
                encoder->crtc = NULL;
+               intel_output->base.encoder = NULL;
                intel_output->load_detect_temp = false;
                crtc->enabled = drm_helper_crtc_in_use(crtc);
                drm_helper_disable_unused_functions(dev);
@@ -1762,6 +2310,8 @@ static void intel_crtc_destroy(struct drm_crtc *crtc)
 {
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 
+       if (intel_crtc->mode_set.mode)
+               drm_mode_destroy(crtc->dev, intel_crtc->mode_set.mode);
        drm_crtc_cleanup(crtc);
        kfree(intel_crtc);
 }
@@ -1888,7 +2438,24 @@ static void intel_setup_outputs(struct drm_device *dev)
        if (IS_MOBILE(dev) && !IS_I830(dev))
                intel_lvds_init(dev);
 
-       if (IS_I9XX(dev)) {
+       if (IS_IGDNG(dev)) {
+               int found;
+
+               if (I915_READ(HDMIB) & PORT_DETECTED) {
+                       /* check SDVOB */
+                       /* found = intel_sdvo_init(dev, HDMIB); */
+                       found = 0;
+                       if (!found)
+                               intel_hdmi_init(dev, HDMIB);
+               }
+
+               if (I915_READ(HDMIC) & PORT_DETECTED)
+                       intel_hdmi_init(dev, HDMIC);
+
+               if (I915_READ(HDMID) & PORT_DETECTED)
+                       intel_hdmi_init(dev, HDMID);
+
+       } else if (IS_I9XX(dev)) {
                int found;
                u32 reg;
 
@@ -1912,7 +2479,7 @@ static void intel_setup_outputs(struct drm_device *dev)
        } else
                intel_dvo_init(dev);
 
-       if (IS_I9XX(dev) && IS_MOBILE(dev))
+       if (IS_I9XX(dev) && IS_MOBILE(dev) && !IS_IGDNG(dev))
                intel_tv_init(dev);
 
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
index e4652dcdd9bb648f585da248127199ccd816bdac..0ecf6b76a401c043e00a12b3b027a07ef14cce01 100644 (file)
@@ -207,7 +207,7 @@ static int intelfb_set_par(struct fb_info *info)
 
        if (var->pixclock != -1) {
 
-               DRM_ERROR("PIXEL CLCOK SET\n");
+               DRM_ERROR("PIXEL CLOCK SET\n");
                return -EINVAL;
        } else {
                struct drm_crtc *crtc;
@@ -674,8 +674,12 @@ static int intelfb_multi_fb_probe_crtc(struct drm_device *dev, struct drm_crtc *
        par->crtc_ids[0] = crtc->base.id;
 
        modeset->num_connectors = conn_count;
-       if (modeset->mode != modeset->crtc->desired_mode)
-               modeset->mode = modeset->crtc->desired_mode;
+       if (modeset->crtc->desired_mode) {
+               if (modeset->mode)
+                       drm_mode_destroy(dev, modeset->mode);
+               modeset->mode = drm_mode_duplicate(dev,
+                                                  modeset->crtc->desired_mode);
+       }
 
        par->crtc_count = 1;
 
@@ -824,8 +828,12 @@ static int intelfb_single_fb_probe(struct drm_device *dev)
                par->crtc_ids[crtc_count++] = crtc->base.id;
 
                modeset->num_connectors = conn_count;
-               if (modeset->mode != modeset->crtc->desired_mode)
-                       modeset->mode = modeset->crtc->desired_mode;
+               if (modeset->crtc->desired_mode) {
+                       if (modeset->mode)
+                               drm_mode_destroy(dev, modeset->mode);
+                       modeset->mode = drm_mode_duplicate(dev,
+                                                          modeset->crtc->desired_mode);
+               }
        }
        par->crtc_count = crtc_count;
 
@@ -857,9 +865,15 @@ void intelfb_restore(void)
        drm_crtc_helper_set_config(&kernelfb_mode);
 }
 
+static void intelfb_restore_work_fn(struct work_struct *ignored)
+{
+       intelfb_restore();
+}
+static DECLARE_WORK(intelfb_restore_work, intelfb_restore_work_fn);
+
 static void intelfb_sysrq(int dummy1, struct tty_struct *dummy3)
 {
-        intelfb_restore();
+        schedule_work(&intelfb_restore_work);
 }
 
 static struct sysrq_key_op sysrq_intelfb_restore_op = {
index 7d6bdd705326e4443b5dfe1b66981e8c06375307..4ea2a651b92c43079a4c3185a829e241822ca488 100644 (file)
@@ -56,7 +56,8 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
        sdvox = SDVO_ENCODING_HDMI |
                SDVO_BORDER_ENABLE |
                SDVO_VSYNC_ACTIVE_HIGH |
-               SDVO_HSYNC_ACTIVE_HIGH;
+               SDVO_HSYNC_ACTIVE_HIGH |
+               SDVO_NULL_PACKETS_DURING_VSYNC;
 
        if (hdmi_priv->has_hdmi_sink)
                sdvox |= SDVO_AUDIO_ENABLE;
@@ -144,6 +145,22 @@ intel_hdmi_sink_detect(struct drm_connector *connector)
        }
 }
 
+static enum drm_connector_status
+igdng_hdmi_detect(struct drm_connector *connector)
+{
+       struct intel_output *intel_output = to_intel_output(connector);
+       struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv;
+
+       /* FIXME hotplug detect */
+
+       hdmi_priv->has_hdmi_sink = false;
+       intel_hdmi_sink_detect(connector);
+       if (hdmi_priv->has_hdmi_sink)
+               return connector_status_connected;
+       else
+               return connector_status_disconnected;
+}
+
 static enum drm_connector_status
 intel_hdmi_detect(struct drm_connector *connector)
 {
@@ -153,6 +170,9 @@ intel_hdmi_detect(struct drm_connector *connector)
        struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv;
        u32 temp, bit;
 
+       if (IS_IGDNG(dev))
+               return igdng_hdmi_detect(connector);
+
        temp = I915_READ(PORT_HOTPLUG_EN);
 
        switch (hdmi_priv->sdvox_reg) {
@@ -269,8 +289,17 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
        /* Set up the DDC bus. */
        if (sdvox_reg == SDVOB)
                intel_output->ddc_bus = intel_i2c_create(dev, GPIOE, "HDMIB");
-       else
+       else if (sdvox_reg == SDVOC)
                intel_output->ddc_bus = intel_i2c_create(dev, GPIOD, "HDMIC");
+       else if (sdvox_reg == HDMIB)
+               intel_output->ddc_bus = intel_i2c_create(dev, PCH_GPIOE,
+                                                               "HDMIB");
+       else if (sdvox_reg == HDMIC)
+               intel_output->ddc_bus = intel_i2c_create(dev, PCH_GPIOD,
+                                                               "HDMIC");
+       else if (sdvox_reg == HDMID)
+               intel_output->ddc_bus = intel_i2c_create(dev, PCH_GPIOF,
+                                                               "HDMID");
 
        if (!intel_output->ddc_bus)
                goto err_connector;
index 53cccfa58b951012f127ce79891a6d7eca79d288..f073ed8432e8fb9547d272653b51315a3bc90de3 100644 (file)
@@ -37,6 +37,8 @@
 #include "i915_drm.h"
 #include "i915_drv.h"
 
+#define I915_LVDS "i915_lvds"
+
 /**
  * Sets the backlight level.
  *
 static void intel_lvds_set_backlight(struct drm_device *dev, int level)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 blc_pwm_ctl;
+       u32 blc_pwm_ctl, reg;
+
+       if (IS_IGDNG(dev))
+               reg = BLC_PWM_CPU_CTL;
+       else
+               reg = BLC_PWM_CTL;
 
-       blc_pwm_ctl = I915_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
-       I915_WRITE(BLC_PWM_CTL, (blc_pwm_ctl |
+       blc_pwm_ctl = I915_READ(reg) & ~BACKLIGHT_DUTY_CYCLE_MASK;
+       I915_WRITE(reg, (blc_pwm_ctl |
                                 (level << BACKLIGHT_DUTY_CYCLE_SHIFT)));
 }
 
@@ -58,8 +65,14 @@ static void intel_lvds_set_backlight(struct drm_device *dev, int level)
 static u32 intel_lvds_get_max_backlight(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 reg;
+
+       if (IS_IGDNG(dev))
+               reg = BLC_PWM_PCH_CTL2;
+       else
+               reg = BLC_PWM_CTL;
 
-       return ((I915_READ(BLC_PWM_CTL) & BACKLIGHT_MODULATION_FREQ_MASK) >>
+       return ((I915_READ(reg) & BACKLIGHT_MODULATION_FREQ_MASK) >>
                BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
 }
 
@@ -69,23 +82,31 @@ static u32 intel_lvds_get_max_backlight(struct drm_device *dev)
 static void intel_lvds_set_power(struct drm_device *dev, bool on)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 pp_status;
+       u32 pp_status, ctl_reg, status_reg;
+
+       if (IS_IGDNG(dev)) {
+               ctl_reg = PCH_PP_CONTROL;
+               status_reg = PCH_PP_STATUS;
+       } else {
+               ctl_reg = PP_CONTROL;
+               status_reg = PP_STATUS;
+       }
 
        if (on) {
-               I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) |
+               I915_WRITE(ctl_reg, I915_READ(ctl_reg) |
                           POWER_TARGET_ON);
                do {
-                       pp_status = I915_READ(PP_STATUS);
+                       pp_status = I915_READ(status_reg);
                } while ((pp_status & PP_ON) == 0);
 
                intel_lvds_set_backlight(dev, dev_priv->backlight_duty_cycle);
        } else {
                intel_lvds_set_backlight(dev, 0);
 
-               I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) &
+               I915_WRITE(ctl_reg, I915_READ(ctl_reg) &
                           ~POWER_TARGET_ON);
                do {
-                       pp_status = I915_READ(PP_STATUS);
+                       pp_status = I915_READ(status_reg);
                } while (pp_status & PP_ON);
        }
 }
@@ -106,12 +127,28 @@ static void intel_lvds_save(struct drm_connector *connector)
 {
        struct drm_device *dev = connector->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 pp_on_reg, pp_off_reg, pp_ctl_reg, pp_div_reg;
+       u32 pwm_ctl_reg;
+
+       if (IS_IGDNG(dev)) {
+               pp_on_reg = PCH_PP_ON_DELAYS;
+               pp_off_reg = PCH_PP_OFF_DELAYS;
+               pp_ctl_reg = PCH_PP_CONTROL;
+               pp_div_reg = PCH_PP_DIVISOR;
+               pwm_ctl_reg = BLC_PWM_CPU_CTL;
+       } else {
+               pp_on_reg = PP_ON_DELAYS;
+               pp_off_reg = PP_OFF_DELAYS;
+               pp_ctl_reg = PP_CONTROL;
+               pp_div_reg = PP_DIVISOR;
+               pwm_ctl_reg = BLC_PWM_CTL;
+       }
 
-       dev_priv->savePP_ON = I915_READ(PP_ON_DELAYS);
-       dev_priv->savePP_OFF = I915_READ(PP_OFF_DELAYS);
-       dev_priv->savePP_CONTROL = I915_READ(PP_CONTROL);
-       dev_priv->savePP_DIVISOR = I915_READ(PP_DIVISOR);
-       dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL);
+       dev_priv->savePP_ON = I915_READ(pp_on_reg);
+       dev_priv->savePP_OFF = I915_READ(pp_off_reg);
+       dev_priv->savePP_CONTROL = I915_READ(pp_ctl_reg);
+       dev_priv->savePP_DIVISOR = I915_READ(pp_div_reg);
+       dev_priv->saveBLC_PWM_CTL = I915_READ(pwm_ctl_reg);
        dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL &
                                       BACKLIGHT_DUTY_CYCLE_MASK);
 
@@ -127,12 +164,28 @@ static void intel_lvds_restore(struct drm_connector *connector)
 {
        struct drm_device *dev = connector->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 pp_on_reg, pp_off_reg, pp_ctl_reg, pp_div_reg;
+       u32 pwm_ctl_reg;
+
+       if (IS_IGDNG(dev)) {
+               pp_on_reg = PCH_PP_ON_DELAYS;
+               pp_off_reg = PCH_PP_OFF_DELAYS;
+               pp_ctl_reg = PCH_PP_CONTROL;
+               pp_div_reg = PCH_PP_DIVISOR;
+               pwm_ctl_reg = BLC_PWM_CPU_CTL;
+       } else {
+               pp_on_reg = PP_ON_DELAYS;
+               pp_off_reg = PP_OFF_DELAYS;
+               pp_ctl_reg = PP_CONTROL;
+               pp_div_reg = PP_DIVISOR;
+               pwm_ctl_reg = BLC_PWM_CTL;
+       }
 
-       I915_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL);
-       I915_WRITE(PP_ON_DELAYS, dev_priv->savePP_ON);
-       I915_WRITE(PP_OFF_DELAYS, dev_priv->savePP_OFF);
-       I915_WRITE(PP_DIVISOR, dev_priv->savePP_DIVISOR);
-       I915_WRITE(PP_CONTROL, dev_priv->savePP_CONTROL);
+       I915_WRITE(pwm_ctl_reg, dev_priv->saveBLC_PWM_CTL);
+       I915_WRITE(pp_on_reg, dev_priv->savePP_ON);
+       I915_WRITE(pp_off_reg, dev_priv->savePP_OFF);
+       I915_WRITE(pp_div_reg, dev_priv->savePP_DIVISOR);
+       I915_WRITE(pp_ctl_reg, dev_priv->savePP_CONTROL);
        if (dev_priv->savePP_CONTROL & POWER_TARGET_ON)
                intel_lvds_set_power(dev, true);
        else
@@ -216,8 +269,14 @@ static void intel_lvds_prepare(struct drm_encoder *encoder)
 {
        struct drm_device *dev = encoder->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 reg;
+
+       if (IS_IGDNG(dev))
+               reg = BLC_PWM_CPU_CTL;
+       else
+               reg = BLC_PWM_CTL;
 
-       dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL);
+       dev_priv->saveBLC_PWM_CTL = I915_READ(reg);
        dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL &
                                       BACKLIGHT_DUTY_CYCLE_MASK);
 
@@ -251,6 +310,10 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder,
         * settings.
         */
 
+       /* No panel fitting yet, fixme */
+       if (IS_IGDNG(dev))
+               return;
+
        /*
         * Enable automatic panel scaling so that non-native modes fill the
         * screen.  Should be enabled before the pipe is enabled, according to
@@ -382,7 +445,8 @@ static const struct drm_encoder_funcs intel_lvds_enc_funcs = {
 
 static int __init intel_no_lvds_dmi_callback(const struct dmi_system_id *id)
 {
-       DRM_DEBUG("Skipping LVDS initialization for %s\n", id->ident);
+       DRM_DEBUG_KMS(I915_LVDS,
+                     "Skipping LVDS initialization for %s\n", id->ident);
        return 1;
 }
 
@@ -420,8 +484,21 @@ static const struct dmi_system_id intel_no_lvds[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "Studio Hybrid 140g"),
                },
        },
-
-       /* FIXME: add a check for the Aopen Mini PC */
+       {
+               .callback = intel_no_lvds_dmi_callback,
+               .ident = "AOpen Mini PC",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "AOpen"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "i965GMx-IF"),
+               },
+       },
+       {
+               .callback = intel_no_lvds_dmi_callback,
+               .ident = "Aopen i945GTt-VFA",
+               .matches = {
+                       DMI_MATCH(DMI_PRODUCT_VERSION, "AO00001JW"),
+               },
+       },
 
        { }     /* terminating entry */
 };
@@ -442,12 +519,18 @@ void intel_lvds_init(struct drm_device *dev)
        struct drm_display_mode *scan; /* *modes, *bios_mode; */
        struct drm_crtc *crtc;
        u32 lvds;
-       int pipe;
+       int pipe, gpio = GPIOC;
 
        /* Skip init on machines we know falsely report LVDS */
        if (dmi_check_system(intel_no_lvds))
                return;
 
+       if (IS_IGDNG(dev)) {
+               if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0)
+                       return;
+               gpio = PCH_GPIOC;
+       }
+
        intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
        if (!intel_output) {
                return;
@@ -482,7 +565,7 @@ void intel_lvds_init(struct drm_device *dev)
         */
 
        /* Set up the DDC bus. */
-       intel_output->ddc_bus = intel_i2c_create(dev, GPIOC, "LVDSDDC_C");
+       intel_output->ddc_bus = intel_i2c_create(dev, gpio, "LVDSDDC_C");
        if (!intel_output->ddc_bus) {
                dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
                           "failed.\n");
@@ -524,6 +607,11 @@ void intel_lvds_init(struct drm_device *dev)
         * on.  If so, assume that whatever is currently programmed is the
         * correct mode.
         */
+
+       /* IGDNG: FIXME if still fail, not try pipe mode now */
+       if (IS_IGDNG(dev))
+               goto failed;
+
        lvds = I915_READ(LVDS);
        pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0;
        crtc = intel_get_crtc_from_pipe(dev, pipe);
@@ -542,11 +630,22 @@ void intel_lvds_init(struct drm_device *dev)
                goto failed;
 
 out:
+       if (IS_IGDNG(dev)) {
+               u32 pwm;
+               /* make sure PWM is enabled */
+               pwm = I915_READ(BLC_PWM_CPU_CTL2);
+               pwm |= (PWM_ENABLE | PWM_PIPE_B);
+               I915_WRITE(BLC_PWM_CPU_CTL2, pwm);
+
+               pwm = I915_READ(BLC_PWM_PCH_CTL1);
+               pwm |= PWM_PCH_ENABLE;
+               I915_WRITE(BLC_PWM_PCH_CTL1, pwm);
+       }
        drm_sysfs_connector_add(connector);
        return;
 
 failed:
-       DRM_DEBUG("No LVDS modes found, disabling.\n");
+       DRM_DEBUG_KMS(I915_LVDS, "No LVDS modes found, disabling.\n");
        if (intel_output->ddc_bus)
                intel_i2c_destroy(intel_output->ddc_bus);
        drm_connector_cleanup(connector);
index 3093b4d4a4ddb2329d407a09aa17bce413d5e77f..9a00adb3a50896033302bb70b6d715de72408dce 100644 (file)
@@ -36,7 +36,7 @@
 #include "intel_sdvo_regs.h"
 
 #undef SDVO_DEBUG
-
+#define I915_SDVO      "i915_sdvo"
 struct intel_sdvo_priv {
        struct intel_i2c_chan *i2c_bus;
        int slaveaddr;
@@ -277,20 +277,21 @@ static void intel_sdvo_debug_write(struct intel_output *intel_output, u8 cmd,
        struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
        int i;
 
-       printk(KERN_DEBUG "%s: W: %02X ", SDVO_NAME(sdvo_priv), cmd);
+       DRM_DEBUG_KMS(I915_SDVO, "%s: W: %02X ",
+                               SDVO_NAME(sdvo_priv), cmd);
        for (i = 0; i < args_len; i++)
-               printk(KERN_DEBUG "%02X ", ((u8 *)args)[i]);
+               DRM_LOG_KMS("%02X ", ((u8 *)args)[i]);
        for (; i < 8; i++)
-               printk(KERN_DEBUG "   ");
+               DRM_LOG_KMS("   ");
        for (i = 0; i < sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0]); i++) {
                if (cmd == sdvo_cmd_names[i].cmd) {
-                       printk(KERN_DEBUG "(%s)", sdvo_cmd_names[i].name);
+                       DRM_LOG_KMS("(%s)", sdvo_cmd_names[i].name);
                        break;
                }
        }
        if (i == sizeof(sdvo_cmd_names)/ sizeof(sdvo_cmd_names[0]))
-               printk(KERN_DEBUG "(%02X)", cmd);
-       printk(KERN_DEBUG "\n");
+               DRM_LOG_KMS("(%02X)", cmd);
+       DRM_LOG_KMS("\n");
 }
 #else
 #define intel_sdvo_debug_write(o, c, a, l)
@@ -329,16 +330,16 @@ static void intel_sdvo_debug_response(struct intel_output *intel_output,
        struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
        int i;
 
-       printk(KERN_DEBUG "%s: R: ", SDVO_NAME(sdvo_priv));
+       DRM_DEBUG_KMS(I915_SDVO, "%s: R: ", SDVO_NAME(sdvo_priv));
        for (i = 0; i < response_len; i++)
-               printk(KERN_DEBUG "%02X ", ((u8 *)response)[i]);
+               DRM_LOG_KMS("%02X ", ((u8 *)response)[i]);
        for (; i < 8; i++)
-               printk(KERN_DEBUG "   ");
+               DRM_LOG_KMS("   ");
        if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP)
-               printk(KERN_DEBUG "(%s)", cmd_status_names[status]);
+               DRM_LOG_KMS("(%s)", cmd_status_names[status]);
        else
-               printk(KERN_DEBUG "(??? %d)", status);
-       printk(KERN_DEBUG "\n");
+               DRM_LOG_KMS("(??? %d)", status);
+       DRM_LOG_KMS("\n");
 }
 #else
 #define intel_sdvo_debug_response(o, r, l, s)
@@ -1742,6 +1743,43 @@ static struct i2c_algorithm intel_sdvo_i2c_bit_algo = {
        .master_xfer    = intel_sdvo_master_xfer,
 };
 
+static u8
+intel_sdvo_get_slave_addr(struct drm_device *dev, int output_device)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct sdvo_device_mapping *my_mapping, *other_mapping;
+
+       if (output_device == SDVOB) {
+               my_mapping = &dev_priv->sdvo_mappings[0];
+               other_mapping = &dev_priv->sdvo_mappings[1];
+       } else {
+               my_mapping = &dev_priv->sdvo_mappings[1];
+               other_mapping = &dev_priv->sdvo_mappings[0];
+       }
+
+       /* If the BIOS described our SDVO device, take advantage of it. */
+       if (my_mapping->slave_addr)
+               return my_mapping->slave_addr;
+
+       /* If the BIOS only described a different SDVO device, use the
+        * address that it isn't using.
+        */
+       if (other_mapping->slave_addr) {
+               if (other_mapping->slave_addr == 0x70)
+                       return 0x72;
+               else
+                       return 0x70;
+       }
+
+       /* No SDVO device info is found for another DVO port,
+        * so use mapping assumption we had before BIOS parsing.
+        */
+       if (output_device == SDVOB)
+               return 0x70;
+       else
+               return 0x72;
+}
+
 bool intel_sdvo_init(struct drm_device *dev, int output_device)
 {
        struct drm_connector *connector;
@@ -1753,6 +1791,7 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
        u8 ch[0x40];
        int i;
        int encoder_type, output_id;
+       u8 slave_addr;
 
        intel_output = kcalloc(sizeof(struct intel_output)+sizeof(struct intel_sdvo_priv), 1, GFP_KERNEL);
        if (!intel_output) {
@@ -1771,16 +1810,15 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
        if (!i2cbus)
                goto err_inteloutput;
 
+       slave_addr = intel_sdvo_get_slave_addr(dev, output_device);
        sdvo_priv->i2c_bus = i2cbus;
 
        if (output_device == SDVOB) {
                output_id = 1;
-               sdvo_priv->i2c_bus->slave_addr = 0x38;
        } else {
                output_id = 2;
-               sdvo_priv->i2c_bus->slave_addr = 0x39;
        }
-
+       sdvo_priv->i2c_bus->slave_addr = slave_addr >> 1;
        sdvo_priv->output_device = output_device;
        intel_output->i2c_bus = i2cbus;
        intel_output->dev_priv = sdvo_priv;
@@ -1788,8 +1826,9 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
        /* Read the regs to test if we can talk to the device */
        for (i = 0; i < 0x40; i++) {
                if (!intel_sdvo_read_byte(intel_output, i, &ch[i])) {
-                       DRM_DEBUG("No SDVO device found on SDVO%c\n",
-                                 output_device == SDVOB ? 'B' : 'C');
+                       DRM_DEBUG_KMS(I915_SDVO,
+                                       "No SDVO device found on SDVO%c\n",
+                                       output_device == SDVOB ? 'B' : 'C');
                        goto err_i2c;
                }
        }
@@ -1873,9 +1912,10 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
 
                sdvo_priv->controlled_output = 0;
                memcpy (bytes, &sdvo_priv->caps.output_flags, 2);
-               DRM_DEBUG("%s: Unknown SDVO output type (0x%02x%02x)\n",
-                         SDVO_NAME(sdvo_priv),
-                         bytes[0], bytes[1]);
+               DRM_DEBUG_KMS(I915_SDVO,
+                               "%s: Unknown SDVO output type (0x%02x%02x)\n",
+                                 SDVO_NAME(sdvo_priv),
+                                 bytes[0], bytes[1]);
                encoder_type = DRM_MODE_ENCODER_NONE;
                connector_type = DRM_MODE_CONNECTOR_Unknown;
                goto err_i2c;
@@ -1905,21 +1945,21 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
                                               &sdvo_priv->pixel_clock_max);
 
 
-       DRM_DEBUG("%s device VID/DID: %02X:%02X.%02X, "
-                 "clock range %dMHz - %dMHz, "
-                 "input 1: %c, input 2: %c, "
-                 "output 1: %c, output 2: %c\n",
-                 SDVO_NAME(sdvo_priv),
-                 sdvo_priv->caps.vendor_id, sdvo_priv->caps.device_id,
-                 sdvo_priv->caps.device_rev_id,
-                 sdvo_priv->pixel_clock_min / 1000,
-                 sdvo_priv->pixel_clock_max / 1000,
-                 (sdvo_priv->caps.sdvo_inputs_mask & 0x1) ? 'Y' : 'N',
-                 (sdvo_priv->caps.sdvo_inputs_mask & 0x2) ? 'Y' : 'N',
-                 /* check currently supported outputs */
-                 sdvo_priv->caps.output_flags &
+       DRM_DEBUG_KMS(I915_SDVO, "%s device VID/DID: %02X:%02X.%02X, "
+                       "clock range %dMHz - %dMHz, "
+                       "input 1: %c, input 2: %c, "
+                       "output 1: %c, output 2: %c\n",
+                       SDVO_NAME(sdvo_priv),
+                       sdvo_priv->caps.vendor_id, sdvo_priv->caps.device_id,
+                       sdvo_priv->caps.device_rev_id,
+                       sdvo_priv->pixel_clock_min / 1000,
+                       sdvo_priv->pixel_clock_max / 1000,
+                       (sdvo_priv->caps.sdvo_inputs_mask & 0x1) ? 'Y' : 'N',
+                       (sdvo_priv->caps.sdvo_inputs_mask & 0x2) ? 'Y' : 'N',
+                       /* check currently supported outputs */
+                       sdvo_priv->caps.output_flags &
                        (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_RGB0) ? 'Y' : 'N',
-                 sdvo_priv->caps.output_flags &
+                       sdvo_priv->caps.output_flags &
                        (SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1) ? 'Y' : 'N');
 
        return true;
index 98ac0546b7bd97ea924eb2d44e3e456ca91c8c2c..50d7ed70b338a2c867eccb7caa1562e1ce6cbf53 100644 (file)
@@ -1392,6 +1392,9 @@ intel_tv_detect_type (struct drm_crtc *crtc, struct intel_output *intel_output)
                tv_ctl &= ~TV_TEST_MODE_MASK;
                tv_ctl |= TV_TEST_MODE_MONITOR_DETECT;
                tv_dac &= ~TVDAC_SENSE_MASK;
+               tv_dac &= ~DAC_A_MASK;
+               tv_dac &= ~DAC_B_MASK;
+               tv_dac &= ~DAC_C_MASK;
                tv_dac |= (TVDAC_STATE_CHG_EN |
                           TVDAC_A_SENSE_CTL |
                           TVDAC_B_SENSE_CTL |
index bc9d09dfa8e7de7063ebea70c6371d0cecf89a9d..146f3570af8e710f689d3f9823981b993a5e3632 100644 (file)
@@ -478,26 +478,27 @@ static void r700_cp_load_microcode(drm_radeon_private_t *dev_priv)
 
        if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV770)) {
                RADEON_WRITE(R600_CP_PFP_UCODE_ADDR, 0);
-               DRM_INFO("Loading RV770 PFP Microcode\n");
+               DRM_INFO("Loading RV770/RV790 PFP Microcode\n");
                for (i = 0; i < R700_PFP_UCODE_SIZE; i++)
                        RADEON_WRITE(R600_CP_PFP_UCODE_DATA, RV770_pfp_microcode[i]);
                RADEON_WRITE(R600_CP_PFP_UCODE_ADDR, 0);
 
                RADEON_WRITE(R600_CP_ME_RAM_WADDR, 0);
-               DRM_INFO("Loading RV770 CP Microcode\n");
+               DRM_INFO("Loading RV770/RV790 CP Microcode\n");
                for (i = 0; i < R700_PM4_UCODE_SIZE; i++)
                        RADEON_WRITE(R600_CP_ME_RAM_DATA, RV770_cp_microcode[i]);
                RADEON_WRITE(R600_CP_ME_RAM_WADDR, 0);
 
-       } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV730)) {
+       } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV730) ||
+                  ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV740)) {
                RADEON_WRITE(R600_CP_PFP_UCODE_ADDR, 0);
-               DRM_INFO("Loading RV730 PFP Microcode\n");
+               DRM_INFO("Loading RV730/RV740 PFP Microcode\n");
                for (i = 0; i < R700_PFP_UCODE_SIZE; i++)
                        RADEON_WRITE(R600_CP_PFP_UCODE_DATA, RV730_pfp_microcode[i]);
                RADEON_WRITE(R600_CP_PFP_UCODE_ADDR, 0);
 
                RADEON_WRITE(R600_CP_ME_RAM_WADDR, 0);
-               DRM_INFO("Loading RV730 CP Microcode\n");
+               DRM_INFO("Loading RV730/RV740 CP Microcode\n");
                for (i = 0; i < R700_PM4_UCODE_SIZE; i++)
                        RADEON_WRITE(R600_CP_ME_RAM_DATA, RV730_cp_microcode[i]);
                RADEON_WRITE(R600_CP_ME_RAM_WADDR, 0);
@@ -1324,6 +1325,10 @@ static void r700_gfx_init(struct drm_device *dev,
                dev_priv->r700_sc_prim_fifo_size = 0xf9;
                dev_priv->r700_sc_hiz_tile_fifo_size = 0x30;
                dev_priv->r700_sc_earlyz_tile_fifo_fize = 0x130;
+               if (dev_priv->r600_sx_max_export_pos_size > 16) {
+                       dev_priv->r600_sx_max_export_pos_size -= 16;
+                       dev_priv->r600_sx_max_export_smx_size += 16;
+               }
                break;
        case CHIP_RV710:
                dev_priv->r600_max_pipes = 2;
@@ -1345,6 +1350,31 @@ static void r700_gfx_init(struct drm_device *dev,
                dev_priv->r700_sc_hiz_tile_fifo_size = 0x30;
                dev_priv->r700_sc_earlyz_tile_fifo_fize = 0x130;
                break;
+       case CHIP_RV740:
+               dev_priv->r600_max_pipes = 4;
+               dev_priv->r600_max_tile_pipes = 4;
+               dev_priv->r600_max_simds = 8;
+               dev_priv->r600_max_backends = 4;
+               dev_priv->r600_max_gprs = 256;
+               dev_priv->r600_max_threads = 248;
+               dev_priv->r600_max_stack_entries = 512;
+               dev_priv->r600_max_hw_contexts = 8;
+               dev_priv->r600_max_gs_threads = 16 * 2;
+               dev_priv->r600_sx_max_export_size = 256;
+               dev_priv->r600_sx_max_export_pos_size = 32;
+               dev_priv->r600_sx_max_export_smx_size = 224;
+               dev_priv->r600_sq_num_cf_insts = 2;
+
+               dev_priv->r700_sx_num_of_sets = 7;
+               dev_priv->r700_sc_prim_fifo_size = 0x100;
+               dev_priv->r700_sc_hiz_tile_fifo_size = 0x30;
+               dev_priv->r700_sc_earlyz_tile_fifo_fize = 0x130;
+
+               if (dev_priv->r600_sx_max_export_pos_size > 16) {
+                       dev_priv->r600_sx_max_export_pos_size -= 16;
+                       dev_priv->r600_sx_max_export_smx_size += 16;
+               }
+               break;
        default:
                break;
        }
@@ -1493,6 +1523,7 @@ static void r700_gfx_init(struct drm_device *dev,
                break;
        case CHIP_RV730:
        case CHIP_RV710:
+       case CHIP_RV740:
        default:
                sq_ms_fifo_sizes |= R600_FETCH_FIFO_HIWATER(0x4);
                break;
@@ -1569,6 +1600,7 @@ static void r700_gfx_init(struct drm_device *dev,
        switch (dev_priv->flags & RADEON_FAMILY_MASK) {
        case CHIP_RV770:
        case CHIP_RV730:
+       case CHIP_RV740:
                gs_prim_buffer_depth = 384;
                break;
        case CHIP_RV710:
index aff90bb964885568567f17d43a2f1b8dd79f179c..89c4c44169f778f39d9aad86cc57a8a31b18adcf 100644 (file)
@@ -2109,7 +2109,7 @@ int radeon_master_create(struct drm_device *dev, struct drm_master *master)
 
        /* prebuild the SAREA */
        sareapage = max_t(unsigned long, SAREA_MAX, PAGE_SIZE);
-       ret = drm_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK|_DRM_DRIVER,
+       ret = drm_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK,
                         &master_priv->sarea);
        if (ret) {
                DRM_ERROR("SAREA setup failed\n");
index 0c6bfc1de153108fe2780b3d49978a26a008a9b7..127d0456f628bf87ed469e5a451347c5d981f5b4 100644 (file)
@@ -146,6 +146,7 @@ enum radeon_family {
        CHIP_RV770,
        CHIP_RV730,
        CHIP_RV710,
+       CHIP_RV740,
        CHIP_LAST,
 };
 
index 409e00afdd0754f6eb60707810a262df43a75fe9..327380888b4a5f15880bbef8f17f93658d12ab89 100644 (file)
@@ -195,10 +195,8 @@ via_free_sg_info(struct pci_dev *pdev, drm_via_sg_info_t *vsg)
        default:
                vsg->state = dr_via_sg_init;
        }
-       if (vsg->bounce_buffer) {
-               vfree(vsg->bounce_buffer);
-               vsg->bounce_buffer = NULL;
-       }
+       vfree(vsg->bounce_buffer);
+       vsg->bounce_buffer = NULL;
        vsg->free_on_sequence = 0;
 }
 
index 403d0e4265db1ef9d9163229fae6568a66d032ac..fc0949a8cfdeadb457282dddff2a49d104932404 100644 (file)
@@ -216,6 +216,7 @@ static const struct ide_port_info at91_ide_port_info __initdata = {
        .host_flags     = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA | IDE_HFLAG_SINGLE |
                          IDE_HFLAG_NO_IO_32BIT | IDE_HFLAG_UNMASK_IRQS,
        .pio_mask       = ATA_PIO6,
+       .chipset        = ide_generic,
 };
 
 /*
@@ -246,8 +247,7 @@ irqreturn_t at91_irq_handler(int irq, void *dev_id)
 static int __init at91_ide_probe(struct platform_device *pdev)
 {
        int ret;
-       hw_regs_t hw;
-       hw_regs_t *hws[] = { &hw, NULL, NULL, NULL };
+       struct ide_hw hw, *hws[] = { &hw };
        struct ide_host *host;
        struct resource *res;
        unsigned long tf_base = 0, ctl_base = 0;
@@ -304,10 +304,9 @@ static int __init at91_ide_probe(struct platform_device *pdev)
                ide_std_init_ports(&hw, tf_base, ctl_base + 6);
 
        hw.irq = board->irq_pin;
-       hw.chipset = ide_generic;
        hw.dev = &pdev->dev;
 
-       host = ide_host_alloc(&at91_ide_port_info, hws);
+       host = ide_host_alloc(&at91_ide_port_info, hws, 1);
        if (!host) {
                perr("failed to allocate ide host\n");
                return -ENOMEM;
index 46013644c965c1638faf8f519cd1578a177d114d..58121bd6c1152c62199cc3417713a8c7416c80d6 100644 (file)
@@ -449,7 +449,7 @@ static int auide_ddma_init(ide_hwif_t *hwif, const struct ide_port_info *d)
 }
 #endif
 
-static void auide_setup_ports(hw_regs_t *hw, _auide_hwif *ahwif)
+static void auide_setup_ports(struct ide_hw *hw, _auide_hwif *ahwif)
 {
        int i;
        unsigned long *ata_regs = hw->io_ports_array;
@@ -499,6 +499,7 @@ static const struct ide_port_info au1xxx_port_info = {
 #ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA
        .mwdma_mask             = ATA_MWDMA2,
 #endif
+       .chipset                = ide_au1xxx,
 };
 
 static int au_ide_probe(struct platform_device *dev)
@@ -507,7 +508,7 @@ static int au_ide_probe(struct platform_device *dev)
        struct resource *res;
        struct ide_host *host;
        int ret = 0;
-       hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+       struct ide_hw hw, *hws[] = { &hw };
 
 #if defined(CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA)
        char *mode = "MWDMA2";
@@ -548,9 +549,8 @@ static int au_ide_probe(struct platform_device *dev)
        auide_setup_ports(&hw, ahwif);
        hw.irq = ahwif->irq;
        hw.dev = &dev->dev;
-       hw.chipset = ide_au1xxx;
 
-       ret = ide_host_add(&au1xxx_port_info, hws, &host);
+       ret = ide_host_add(&au1xxx_port_info, hws, 1, &host);
        if (ret)
                goto out;
 
index d028f8864bc14f7324f24c25abc02c4258f98bb7..e3c6a59133051e65f88e7678d6ed16a058b86858 100644 (file)
@@ -121,7 +121,7 @@ static int xsurf_ack_intr(ide_hwif_t *hwif)
     return 1;
 }
 
-static void __init buddha_setup_ports(hw_regs_t *hw, unsigned long base,
+static void __init buddha_setup_ports(struct ide_hw *hw, unsigned long base,
                                      unsigned long ctl, unsigned long irq_port,
                                      ide_ack_intr_t *ack_intr)
 {
@@ -139,13 +139,12 @@ static void __init buddha_setup_ports(hw_regs_t *hw, unsigned long base,
 
        hw->irq = IRQ_AMIGA_PORTS;
        hw->ack_intr = ack_intr;
-
-       hw->chipset = ide_generic;
 }
 
 static const struct ide_port_info buddha_port_info = {
        .host_flags             = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA,
        .irq_flags              = IRQF_SHARED,
+       .chipset                = ide_generic,
 };
 
     /*
@@ -161,7 +160,7 @@ static int __init buddha_init(void)
 
        while ((z = zorro_find_device(ZORRO_WILDCARD, z))) {
                unsigned long board;
-               hw_regs_t hw[MAX_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL };
+               struct ide_hw hw[MAX_NUM_HWIFS], *hws[MAX_NUM_HWIFS];
 
                if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA) {
                        buddha_num_hwifs = BUDDHA_NUM_HWIFS;
@@ -225,7 +224,7 @@ fail_base2:
                        hws[i] = &hw[i];
                }
 
-               ide_host_add(&buddha_port_info, hws, NULL);
+               ide_host_add(&buddha_port_info, hws, i, NULL);
        }
 
        return 0;
index 8890276fef7fbfaa3d163c9c9856421e025d11ee..1683ed5c7329c0466375d4337d679bb2b18206f2 100644 (file)
@@ -708,7 +708,7 @@ static int __init cmd640x_init(void)
        int second_port_cmd640 = 0, rc;
        const char *bus_type, *port2;
        u8 b, cfr;
-       hw_regs_t hw[2], *hws[] = { NULL, NULL, NULL, NULL };
+       struct ide_hw hw[2], *hws[2];
 
        if (cmd640_vlb && probe_for_cmd640_vlb()) {
                bus_type = "VLB";
@@ -762,11 +762,9 @@ static int __init cmd640x_init(void)
 
        ide_std_init_ports(&hw[0], 0x1f0, 0x3f6);
        hw[0].irq = 14;
-       hw[0].chipset = ide_cmd640;
 
        ide_std_init_ports(&hw[1], 0x170, 0x376);
        hw[1].irq = 15;
-       hw[1].chipset = ide_cmd640;
 
        printk(KERN_INFO "cmd640: buggy cmd640%c interface on %s, config=0x%02x"
                         "\n", 'a' + cmd640_chip_version - 1, bus_type, cfr);
@@ -824,7 +822,8 @@ static int __init cmd640x_init(void)
        cmd640_dump_regs();
 #endif
 
-       return ide_host_add(&cmd640_port_info, hws, NULL);
+       return ide_host_add(&cmd640_port_info, hws, second_port_cmd640 ? 2 : 1,
+                           NULL);
 }
 
 module_param_named(probe_vlb, cmd640_vlb, bool, 0);
index 87987a7d36c9c3c6d0cf391c92cd12a9c5426ae0..bd066bb9d61105262c342329fce891f38b015393 100644 (file)
@@ -110,7 +110,7 @@ static const struct ide_port_info cyrix_chipset __devinitdata = {
 static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_device_id *id)
 {
        const struct ide_port_info *d = &cyrix_chipset;
-       hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL };
+       struct ide_hw hw[2], *hws[] = { NULL, NULL };
 
        ide_setup_pci_noise(dev, d);
 
@@ -136,7 +136,7 @@ static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_devic
        ide_pci_setup_ports(dev, d, &hw[0], &hws[0]);
        hw[0].irq = 14;
 
-       return ide_host_add(d, hws, NULL);
+       return ide_host_add(d, hws, 2, NULL);
 }
 
 static const struct pci_device_id cs5520_pci_tbl[] = {
index f153b95619bb372e958f7c51c8ca1dcd1ccf043a..1e10eba62ceb7ba97568da89b24162e2b09da2d0 100644 (file)
@@ -68,6 +68,7 @@ static const struct ide_port_info delkin_cb_port_info = {
                                  IDE_HFLAG_NO_DMA,
        .irq_flags              = IRQF_SHARED,
        .init_chipset           = delkin_cb_init_chipset,
+       .chipset                = ide_pci,
 };
 
 static int __devinit
@@ -76,7 +77,7 @@ delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id)
        struct ide_host *host;
        unsigned long base;
        int rc;
-       hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+       struct ide_hw hw, *hws[] = { &hw };
 
        rc = pci_enable_device(dev);
        if (rc) {
@@ -97,9 +98,8 @@ delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id)
        ide_std_init_ports(&hw, base + 0x10, base + 0x1e);
        hw.irq = dev->irq;
        hw.dev = &dev->dev;
-       hw.chipset = ide_pci;           /* this enables IRQ sharing */
 
-       rc = ide_host_add(&delkin_cb_port_info, hws, &host);
+       rc = ide_host_add(&delkin_cb_port_info, hws, 1, &host);
        if (rc)
                goto out_disable;
 
index 0e2df6755ec9f3b45d540fe85b2ef999efd058ac..22fa27389c3b694a7341ab8cc3574fd0beb0fff7 100644 (file)
@@ -111,9 +111,10 @@ static const struct ide_port_info falconide_port_info = {
        .host_flags             = IDE_HFLAG_MMIO | IDE_HFLAG_SERIALIZE |
                                  IDE_HFLAG_NO_DMA,
        .irq_flags              = IRQF_SHARED,
+       .chipset                = ide_generic,
 };
 
-static void __init falconide_setup_ports(hw_regs_t *hw)
+static void __init falconide_setup_ports(struct ide_hw *hw)
 {
        int i;
 
@@ -128,8 +129,6 @@ static void __init falconide_setup_ports(hw_regs_t *hw)
 
        hw->irq = IRQ_MFP_IDE;
        hw->ack_intr = NULL;
-
-       hw->chipset = ide_generic;
 }
 
     /*
@@ -139,7 +138,7 @@ static void __init falconide_setup_ports(hw_regs_t *hw)
 static int __init falconide_init(void)
 {
        struct ide_host *host;
-       hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+       struct ide_hw hw, *hws[] = { &hw };
        int rc;
 
        if (!MACH_IS_ATARI || !ATARIHW_PRESENT(IDE))
@@ -154,7 +153,7 @@ static int __init falconide_init(void)
 
        falconide_setup_ports(&hw);
 
-       host = ide_host_alloc(&falconide_port_info, hws);
+       host = ide_host_alloc(&falconide_port_info, hws, 1);
        if (host == NULL) {
                rc = -ENOMEM;
                goto err;
index c7119516c5a7ccaff6029fbacc3e3a7dfe1017ec..4451a6a5dfe0af00a5ba05b7b70a69370b1d277a 100644 (file)
@@ -88,7 +88,7 @@ static int gayle_ack_intr_a1200(ide_hwif_t *hwif)
     return 1;
 }
 
-static void __init gayle_setup_ports(hw_regs_t *hw, unsigned long base,
+static void __init gayle_setup_ports(struct ide_hw *hw, unsigned long base,
                                     unsigned long ctl, unsigned long irq_port,
                                     ide_ack_intr_t *ack_intr)
 {
@@ -106,14 +106,13 @@ static void __init gayle_setup_ports(hw_regs_t *hw, unsigned long base,
 
        hw->irq = IRQ_AMIGA_PORTS;
        hw->ack_intr = ack_intr;
-
-       hw->chipset = ide_generic;
 }
 
 static const struct ide_port_info gayle_port_info = {
        .host_flags             = IDE_HFLAG_MMIO | IDE_HFLAG_SERIALIZE |
                                  IDE_HFLAG_NO_DMA,
        .irq_flags              = IRQF_SHARED,
+       .chipset                = ide_generic,
 };
 
     /*
@@ -126,7 +125,7 @@ static int __init gayle_init(void)
     unsigned long base, ctrlport, irqport;
     ide_ack_intr_t *ack_intr;
     int a4000, i, rc;
-    hw_regs_t hw[GAYLE_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL };
+    struct ide_hw hw[GAYLE_NUM_HWIFS], *hws[GAYLE_NUM_HWIFS];
 
     if (!MACH_IS_AMIGA)
        return -ENODEV;
@@ -171,7 +170,7 @@ found:
        hws[i] = &hw[i];
     }
 
-    rc = ide_host_add(&gayle_port_info, hws, NULL);
+    rc = ide_host_add(&gayle_port_info, hws, i, NULL);
     if (rc)
        release_mem_region(res_start, res_n);
 
index 0feb66c720e1bdb945bc922a6027f213bdcb2259..7ce68ef6b904f0e29539342b3c661cfc296318c5 100644 (file)
 #undef HPT_RESET_STATE_ENGINE
 #undef HPT_DELAY_INTERRUPT
 
-static const char *quirk_drives[] = {
-       "QUANTUM FIREBALLlct08 08",
-       "QUANTUM FIREBALLP KA6.4",
-       "QUANTUM FIREBALLP LM20.4",
-       "QUANTUM FIREBALLP LM20.5",
-       NULL
-};
-
 static const char *bad_ata100_5[] = {
        "IBM-DTLA-307075",
        "IBM-DTLA-307060",
@@ -729,27 +721,13 @@ static void hpt3xx_set_pio_mode(ide_drive_t *drive, const u8 pio)
        hpt3xx_set_mode(drive, XFER_PIO_0 + pio);
 }
 
-static void hpt3xx_quirkproc(ide_drive_t *drive)
-{
-       char *m                 = (char *)&drive->id[ATA_ID_PROD];
-       const  char **list      = quirk_drives;
-
-       while (*list)
-               if (strstr(m, *list++)) {
-                       drive->quirk_list = 1;
-                       return;
-               }
-
-       drive->quirk_list = 0;
-}
-
 static void hpt3xx_maskproc(ide_drive_t *drive, int mask)
 {
        ide_hwif_t *hwif        = drive->hwif;
        struct pci_dev  *dev    = to_pci_dev(hwif->dev);
        struct hpt_info *info   = hpt3xx_get_info(hwif->dev);
 
-       if (drive->quirk_list == 0)
+       if ((drive->dev_flags & IDE_DFLAG_NIEN_QUIRK) == 0)
                return;
 
        if (info->chip_type >= HPT370) {
@@ -1404,7 +1382,6 @@ static int __devinit hpt36x_init(struct pci_dev *dev, struct pci_dev *dev2)
 static const struct ide_port_ops hpt3xx_port_ops = {
        .set_pio_mode           = hpt3xx_set_pio_mode,
        .set_dma_mode           = hpt3xx_set_mode,
-       .quirkproc              = hpt3xx_quirkproc,
        .maskproc               = hpt3xx_maskproc,
        .mdma_filter            = hpt3xx_mdma_filter,
        .udma_filter            = hpt3xx_udma_filter,
index 36da913cc5532051ebbfc4ef260986238f60cbc0..5af3d0ffaf0af972630e9ac09f2cd546969b48be 100644 (file)
@@ -65,8 +65,6 @@ static struct cardinfo icside_cardinfo_v6_2 = {
 };
 
 struct icside_state {
-       unsigned int channel;
-       unsigned int enabled;
        void __iomem *irq_port;
        void __iomem *ioc_base;
        unsigned int sel;
@@ -116,18 +114,11 @@ static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
        struct icside_state *state = ec->irq_data;
        void __iomem *base = state->irq_port;
 
-       state->enabled = 1;
+       writeb(0, base + ICS_ARCIN_V6_INTROFFSET_1);
+       readb(base + ICS_ARCIN_V6_INTROFFSET_2);
 
-       switch (state->channel) {
-       case 0:
-               writeb(0, base + ICS_ARCIN_V6_INTROFFSET_1);
-               readb(base + ICS_ARCIN_V6_INTROFFSET_2);
-               break;
-       case 1:
-               writeb(0, base + ICS_ARCIN_V6_INTROFFSET_2);
-               readb(base + ICS_ARCIN_V6_INTROFFSET_1);
-               break;
-       }
+       writeb(0, base + ICS_ARCIN_V6_INTROFFSET_2);
+       readb(base + ICS_ARCIN_V6_INTROFFSET_1);
 }
 
 /* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
@@ -137,8 +128,6 @@ static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
 {
        struct icside_state *state = ec->irq_data;
 
-       state->enabled = 0;
-
        readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
        readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
 }
@@ -160,44 +149,6 @@ static const expansioncard_ops_t icside_ops_arcin_v6 = {
        .irqpending     = icside_irqpending_arcin_v6,
 };
 
-/*
- * Handle routing of interrupts.  This is called before
- * we write the command to the drive.
- */
-static void icside_maskproc(ide_drive_t *drive, int mask)
-{
-       ide_hwif_t *hwif = drive->hwif;
-       struct expansion_card *ec = ECARD_DEV(hwif->dev);
-       struct icside_state *state = ecard_get_drvdata(ec);
-       unsigned long flags;
-
-       local_irq_save(flags);
-
-       state->channel = hwif->channel;
-
-       if (state->enabled && !mask) {
-               switch (hwif->channel) {
-               case 0:
-                       writeb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
-                       readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
-                       break;
-               case 1:
-                       writeb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
-                       readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
-                       break;
-               }
-       } else {
-               readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
-               readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
-       }
-
-       local_irq_restore(flags);
-}
-
-static const struct ide_port_ops icside_v6_no_dma_port_ops = {
-       .maskproc               = icside_maskproc,
-};
-
 #ifdef CONFIG_BLK_DEV_IDEDMA_ICS
 /*
  * SG-DMA support.
@@ -275,7 +226,6 @@ static void icside_set_dma_mode(ide_drive_t *drive, const u8 xfer_mode)
 
 static const struct ide_port_ops icside_v6_port_ops = {
        .set_dma_mode           = icside_set_dma_mode,
-       .maskproc               = icside_maskproc,
 };
 
 static void icside_dma_host_set(ide_drive_t *drive, int on)
@@ -319,11 +269,6 @@ static int icside_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd)
         */
        BUG_ON(dma_channel_active(ec->dma));
 
-       /*
-        * Ensure that we have the right interrupt routed.
-        */
-       icside_maskproc(drive, 0);
-
        /*
         * Route the DMA signals to the correct interface.
         */
@@ -381,7 +326,7 @@ static int icside_dma_off_init(ide_hwif_t *hwif, const struct ide_port_info *d)
        return -EOPNOTSUPP;
 }
 
-static void icside_setup_ports(hw_regs_t *hw, void __iomem *base,
+static void icside_setup_ports(struct ide_hw *hw, void __iomem *base,
                               struct cardinfo *info, struct expansion_card *ec)
 {
        unsigned long port = (unsigned long)base + info->dataoffset;
@@ -398,11 +343,11 @@ static void icside_setup_ports(hw_regs_t *hw, void __iomem *base,
 
        hw->irq = ec->irq;
        hw->dev = &ec->dev;
-       hw->chipset = ide_acorn;
 }
 
 static const struct ide_port_info icside_v5_port_info = {
        .host_flags             = IDE_HFLAG_NO_DMA,
+       .chipset                = ide_acorn,
 };
 
 static int __devinit
@@ -410,7 +355,7 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec)
 {
        void __iomem *base;
        struct ide_host *host;
-       hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+       struct ide_hw hw, *hws[] = { &hw };
        int ret;
 
        base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0);
@@ -431,7 +376,7 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec)
 
        icside_setup_ports(&hw, base, &icside_cardinfo_v5, ec);
 
-       host = ide_host_alloc(&icside_v5_port_info, hws);
+       host = ide_host_alloc(&icside_v5_port_info, hws, 1);
        if (host == NULL)
                return -ENODEV;
 
@@ -452,11 +397,11 @@ err_free:
 
 static const struct ide_port_info icside_v6_port_info __initdata = {
        .init_dma               = icside_dma_off_init,
-       .port_ops               = &icside_v6_no_dma_port_ops,
        .dma_ops                = &icside_v6_dma_ops,
        .host_flags             = IDE_HFLAG_SERIALIZE | IDE_HFLAG_MMIO,
        .mwdma_mask             = ATA_MWDMA2,
        .swdma_mask             = ATA_SWDMA2,
+       .chipset                = ide_acorn,
 };
 
 static int __devinit
@@ -466,7 +411,7 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec)
        struct ide_host *host;
        unsigned int sel = 0;
        int ret;
-       hw_regs_t hw[2], *hws[] = { &hw[0], &hw[1], NULL, NULL };
+       struct ide_hw hw[2], *hws[] = { &hw[0], &hw[1] };
        struct ide_port_info d = icside_v6_port_info;
 
        ioc_base = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0);
@@ -506,7 +451,7 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec)
        icside_setup_ports(&hw[0], easi_base, &icside_cardinfo_v6_1, ec);
        icside_setup_ports(&hw[1], easi_base, &icside_cardinfo_v6_2, ec);
 
-       host = ide_host_alloc(&d, hws);
+       host = ide_host_alloc(&d, hws, 2);
        if (host == NULL)
                return -ENODEV;
 
index 78aca75a2c487ece474e08bcb220c5542caa8b4b..979d342c338ae71b39b03edd7986a7ea37732f97 100644 (file)
@@ -25,12 +25,13 @@ static const struct ide_port_info ide_4drives_port_info = {
        .port_ops               = &ide_4drives_port_ops,
        .host_flags             = IDE_HFLAG_SERIALIZE | IDE_HFLAG_NO_DMA |
                                  IDE_HFLAG_4DRIVES,
+       .chipset                = ide_4drives,
 };
 
 static int __init ide_4drives_init(void)
 {
        unsigned long base = 0x1f0, ctl = 0x3f6;
-       hw_regs_t hw, *hws[] = { &hw, &hw, NULL, NULL };
+       struct ide_hw hw, *hws[] = { &hw, &hw };
 
        if (probe_4drives == 0)
                return -ENODEV;
@@ -52,9 +53,8 @@ static int __init ide_4drives_init(void)
 
        ide_std_init_ports(&hw, base, ctl);
        hw.irq = 14;
-       hw.chipset = ide_4drives;
 
-       return ide_host_add(&ide_4drives_port_info, hws, NULL);
+       return ide_host_add(&ide_4drives_port_info, hws, 2, NULL);
 }
 
 module_init(ide_4drives_init);
index 757e5956b13297f38a204740486f9907c35a0ae7..bbdd2547f12aa98779dcb9d96d7461cfdc8dc5af 100644 (file)
@@ -259,7 +259,7 @@ void ide_retry_pc(ide_drive_t *drive)
        pc->req_xfer = blk_rq_bytes(sense_rq);
 
        if (drive->media == ide_tape)
-               set_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags);
+               drive->atapi_flags |= IDE_AFLAG_IGNORE_DSC;
 
        /*
         * Push back the failed request and put request sense on top
index 9e47f3529d5555685bb829b1e92b7b621c15e624..527908ff298c408c0f86fdf24363ea9c70d6e652 100644 (file)
@@ -155,6 +155,7 @@ static const struct ide_port_info idecs_port_info = {
        .port_ops               = &idecs_port_ops,
        .host_flags             = IDE_HFLAG_NO_DMA,
        .irq_flags              = IRQF_SHARED,
+       .chipset                = ide_pci,
 };
 
 static struct ide_host *idecs_register(unsigned long io, unsigned long ctl,
@@ -163,7 +164,7 @@ static struct ide_host *idecs_register(unsigned long io, unsigned long ctl,
     struct ide_host *host;
     ide_hwif_t *hwif;
     int i, rc;
-    hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+    struct ide_hw hw, *hws[] = { &hw };
 
     if (!request_region(io, 8, DRV_NAME)) {
        printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n",
@@ -181,10 +182,9 @@ static struct ide_host *idecs_register(unsigned long io, unsigned long ctl,
     memset(&hw, 0, sizeof(hw));
     ide_std_init_ports(&hw, io, ctl);
     hw.irq = irq;
-    hw.chipset = ide_pci;
     hw.dev = &handle->dev;
 
-    rc = ide_host_add(&idecs_port_info, hws, &host);
+    rc = ide_host_add(&idecs_port_info, hws, 1, &host);
     if (rc)
        goto out_release;
 
index c6f7fcfb9d672f47fb585fc035a1ee9f74f9642d..6a1de21697096525d19b850ffbfba2f99a129c88 100644 (file)
@@ -302,14 +302,12 @@ static const struct drive_list_entry hpa_list[] = {
        { NULL,         NULL }
 };
 
-static void idedisk_check_hpa(ide_drive_t *drive)
+static u64 ide_disk_hpa_get_native_capacity(ide_drive_t *drive, int lba48)
 {
-       unsigned long long capacity, set_max;
-       int lba48 = ata_id_lba48_enabled(drive->id);
+       u64 capacity, set_max;
 
        capacity = drive->capacity64;
-
-       set_max = idedisk_read_native_max_address(drive, lba48);
+       set_max  = idedisk_read_native_max_address(drive, lba48);
 
        if (ide_in_drive_list(drive->id, hpa_list)) {
                /*
@@ -320,9 +318,31 @@ static void idedisk_check_hpa(ide_drive_t *drive)
                        set_max--;
        }
 
+       return set_max;
+}
+
+static u64 ide_disk_hpa_set_capacity(ide_drive_t *drive, u64 set_max, int lba48)
+{
+       set_max = idedisk_set_max_address(drive, set_max, lba48);
+       if (set_max)
+               drive->capacity64 = set_max;
+
+       return set_max;
+}
+
+static void idedisk_check_hpa(ide_drive_t *drive)
+{
+       u64 capacity, set_max;
+       int lba48 = ata_id_lba48_enabled(drive->id);
+
+       capacity = drive->capacity64;
+       set_max  = ide_disk_hpa_get_native_capacity(drive, lba48);
+
        if (set_max <= capacity)
                return;
 
+       drive->probed_capacity = set_max;
+
        printk(KERN_INFO "%s: Host Protected Area detected.\n"
                         "\tcurrent capacity is %llu sectors (%llu MB)\n"
                         "\tnative  capacity is %llu sectors (%llu MB)\n",
@@ -330,13 +350,13 @@ static void idedisk_check_hpa(ide_drive_t *drive)
                         capacity, sectors_to_MB(capacity),
                         set_max, sectors_to_MB(set_max));
 
-       set_max = idedisk_set_max_address(drive, set_max, lba48);
+       if ((drive->dev_flags & IDE_DFLAG_NOHPA) == 0)
+               return;
 
-       if (set_max) {
-               drive->capacity64 = set_max;
+       set_max = ide_disk_hpa_set_capacity(drive, set_max, lba48);
+       if (set_max)
                printk(KERN_INFO "%s: Host Protected Area disabled.\n",
                                 drive->name);
-       }
 }
 
 static int ide_disk_get_capacity(ide_drive_t *drive)
@@ -358,6 +378,8 @@ static int ide_disk_get_capacity(ide_drive_t *drive)
                drive->capacity64 = drive->cyl * drive->head * drive->sect;
        }
 
+       drive->probed_capacity = drive->capacity64;
+
        if (lba) {
                drive->dev_flags |= IDE_DFLAG_LBA;
 
@@ -376,7 +398,7 @@ static int ide_disk_get_capacity(ide_drive_t *drive)
                       "%llu sectors (%llu MB)\n",
                       drive->name, (unsigned long long)drive->capacity64,
                       sectors_to_MB(drive->capacity64));
-               drive->capacity64 = 1ULL << 28;
+               drive->probed_capacity = drive->capacity64 = 1ULL << 28;
        }
 
        if ((drive->hwif->host_flags & IDE_HFLAG_NO_LBA48_DMA) &&
@@ -392,6 +414,34 @@ static int ide_disk_get_capacity(ide_drive_t *drive)
        return 0;
 }
 
+static u64 ide_disk_set_capacity(ide_drive_t *drive, u64 capacity)
+{
+       u64 set = min(capacity, drive->probed_capacity);
+       u16 *id = drive->id;
+       int lba48 = ata_id_lba48_enabled(id);
+
+       if ((drive->dev_flags & IDE_DFLAG_LBA) == 0 ||
+           ata_id_hpa_enabled(id) == 0)
+               goto out;
+
+       /*
+        * according to the spec the SET MAX ADDRESS command shall be
+        * immediately preceded by a READ NATIVE MAX ADDRESS command
+        */
+       capacity = ide_disk_hpa_get_native_capacity(drive, lba48);
+       if (capacity == 0)
+               goto out;
+
+       set = ide_disk_hpa_set_capacity(drive, set, lba48);
+       if (set) {
+               /* needed for ->resume to disable HPA */
+               drive->dev_flags |= IDE_DFLAG_NOHPA;
+               return set;
+       }
+out:
+       return drive->capacity64;
+}
+
 static void idedisk_prepare_flush(struct request_queue *q, struct request *rq)
 {
        ide_drive_t *drive = q->queuedata;
@@ -428,14 +478,14 @@ static int set_multcount(ide_drive_t *drive, int arg)
        if (arg < 0 || arg > (drive->id[ATA_ID_MAX_MULTSECT] & 0xff))
                return -EINVAL;
 
-       if (drive->special.b.set_multmode)
+       if (drive->special_flags & IDE_SFLAG_SET_MULTMODE)
                return -EBUSY;
 
        rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
        rq->cmd_type = REQ_TYPE_ATA_TASKFILE;
 
        drive->mult_req = arg;
-       drive->special.b.set_multmode = 1;
+       drive->special_flags |= IDE_SFLAG_SET_MULTMODE;
        error = blk_execute_rq(drive->queue, NULL, rq, 0);
        blk_put_request(rq);
 
@@ -740,6 +790,7 @@ static int ide_disk_set_doorlock(ide_drive_t *drive, struct gendisk *disk,
 
 const struct ide_disk_ops ide_ata_disk_ops = {
        .check          = ide_disk_check,
+       .set_capacity   = ide_disk_set_capacity,
        .get_capacity   = ide_disk_get_capacity,
        .setup          = ide_disk_setup,
        .flush          = ide_disk_flush,
index 001f68f0bb285c62d0b42b6dbb1508d92182923a..219e6fb78dc6414304ff8b707c59554f9c82db75 100644 (file)
@@ -347,7 +347,6 @@ u8 ide_find_dma_mode(ide_drive_t *drive, u8 req_mode)
 
        return mode;
 }
-EXPORT_SYMBOL_GPL(ide_find_dma_mode);
 
 static int ide_tune_dma(ide_drive_t *drive)
 {
index 5d5fb961b5ce8ee83e9e6e5cfc216ca3d0314d65..2b91419796136e24b4a98a27dd85aab0c6caccc0 100644 (file)
@@ -52,7 +52,7 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq,
        }
 
        if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
-               drive->special.b.recalibrate = 1;
+               drive->special_flags |= IDE_SFLAG_RECALIBRATE;
 
        ++rq->errors;
 
@@ -268,9 +268,8 @@ static void ide_disk_pre_reset(ide_drive_t *drive)
 {
        int legacy = (drive->id[ATA_ID_CFS_ENABLE_2] & 0x0400) ? 0 : 1;
 
-       drive->special.all = 0;
-       drive->special.b.set_geometry = legacy;
-       drive->special.b.recalibrate  = legacy;
+       drive->special_flags =
+               legacy ? (IDE_SFLAG_SET_GEOMETRY | IDE_SFLAG_RECALIBRATE) : 0;
 
        drive->mult_count = 0;
        drive->dev_flags &= ~IDE_DFLAG_PARKED;
@@ -280,7 +279,7 @@ static void ide_disk_pre_reset(ide_drive_t *drive)
                drive->mult_req = 0;
 
        if (drive->mult_req != drive->mult_count)
-               drive->special.b.set_multmode = 1;
+               drive->special_flags |= IDE_SFLAG_SET_MULTMODE;
 }
 
 static void pre_reset(ide_drive_t *drive)
@@ -408,8 +407,9 @@ static ide_startstop_t do_reset1(ide_drive_t *drive, int do_not_try_atapi)
        /* more than enough time */
        udelay(10);
        /* clear SRST, leave nIEN (unless device is on the quirk list) */
-       tp_ops->write_devctl(hwif, (drive->quirk_list == 2 ? 0 : ATA_NIEN) |
-                            ATA_DEVCTL_OBS);
+       tp_ops->write_devctl(hwif,
+               ((drive->dev_flags & IDE_DFLAG_NIEN_QUIRK) ? 0 : ATA_NIEN) |
+                ATA_DEVCTL_OBS);
        /* more than enough time */
        udelay(10);
        hwif->poll_timeout = jiffies + WAIT_WORSTCASE;
index 4b6b71e2cdf5b6cc3f1fe1288e0f99408d5a3742..214119026b3f4e55eec0c0eb1f60898ecc62e047 100644 (file)
@@ -287,6 +287,19 @@ static int ide_gd_media_changed(struct gendisk *disk)
        return ret;
 }
 
+static unsigned long long ide_gd_set_capacity(struct gendisk *disk,
+                                             unsigned long long capacity)
+{
+       struct ide_disk_obj *idkp = ide_drv_g(disk, ide_disk_obj);
+       ide_drive_t *drive = idkp->drive;
+       const struct ide_disk_ops *disk_ops = drive->disk_ops;
+
+       if (disk_ops->set_capacity)
+               return disk_ops->set_capacity(drive, capacity);
+
+       return drive->capacity64;
+}
+
 static int ide_gd_revalidate_disk(struct gendisk *disk)
 {
        struct ide_disk_obj *idkp = ide_drv_g(disk, ide_disk_obj);
@@ -315,6 +328,7 @@ static struct block_device_operations ide_gd_ops = {
        .locked_ioctl           = ide_gd_ioctl,
        .getgeo                 = ide_gd_getgeo,
        .media_changed          = ide_gd_media_changed,
+       .set_capacity           = ide_gd_set_capacity,
        .revalidate_disk        = ide_gd_revalidate_disk
 };
 
index 7812ca0be13beb1c5af1ba98ea8b29b4bd2cbf4b..54d7c4685d23aa5e62ce606e7b994a57bb54b08a 100644 (file)
@@ -29,6 +29,7 @@ MODULE_PARM_DESC(probe_mask, "probe mask for legacy ISA IDE ports");
 
 static const struct ide_port_info ide_generic_port_info = {
        .host_flags             = IDE_HFLAG_NO_DMA,
+       .chipset                = ide_generic,
 };
 
 #ifdef CONFIG_ARM
@@ -85,7 +86,7 @@ static void ide_generic_check_pci_legacy_iobases(int *primary, int *secondary)
 
 static int __init ide_generic_init(void)
 {
-       hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+       struct ide_hw hw, *hws[] = { &hw };
        unsigned long io_addr;
        int i, rc = 0, primary = 0, secondary = 0;
 
@@ -132,9 +133,7 @@ static int __init ide_generic_init(void)
 #else
                        hw.irq = legacy_irqs[i];
 #endif
-                       hw.chipset = ide_generic;
-
-                       rc = ide_host_add(&ide_generic_port_info, hws, NULL);
+                       rc = ide_host_add(&ide_generic_port_info, hws, 1, NULL);
                        if (rc) {
                                release_region(io_addr + 0x206, 1);
                                release_region(io_addr, 8);
index c06ebdc4a130b9c609d2074748fdcf0e0077b651..520f42c5445a029313a1a5f31ab29a4d263ff106 100644 (file)
@@ -64,26 +64,26 @@ static const struct ide_tp_ops h8300_tp_ops = {
 
 #define H8300_IDE_GAP (2)
 
-static inline void hw_setup(hw_regs_t *hw)
+static inline void hw_setup(struct ide_hw *hw)
 {
        int i;
 
-       memset(hw, 0, sizeof(hw_regs_t));
+       memset(hw, 0, sizeof(*hw));
        for (i = 0; i <= 7; i++)
                hw->io_ports_array[i] = CONFIG_H8300_IDE_BASE + H8300_IDE_GAP*i;
        hw->io_ports.ctl_addr = CONFIG_H8300_IDE_ALT;
        hw->irq = EXT_IRQ0 + CONFIG_H8300_IDE_IRQ;
-       hw->chipset = ide_generic;
 }
 
 static const struct ide_port_info h8300_port_info = {
        .tp_ops                 = &h8300_tp_ops,
        .host_flags             = IDE_HFLAG_NO_IO_32BIT | IDE_HFLAG_NO_DMA,
+       .chipset                = ide_generic,
 };
 
 static int __init h8300_ide_init(void)
 {
-       hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+       struct ide_hw hw, *hws[] = { &hw };
 
        printk(KERN_INFO DRV_NAME ": H8/300 generic IDE interface\n");
 
@@ -96,7 +96,7 @@ static int __init h8300_ide_init(void)
 
        hw_setup(&hw);
 
-       return ide_host_add(&h8300_port_info, hws, NULL);
+       return ide_host_add(&h8300_port_info, hws, 1, NULL);
 
 out_busy:
        printk(KERN_ERR "ide-h8300: IDE I/F resource already used.\n");
index bba4297f2f03fcadf80f8bb3f3a05e780e6f3551..272cc38f6dbe55192283db6ea2902e3d576b6a26 100644 (file)
@@ -184,29 +184,42 @@ static void ide_tf_set_setmult_cmd(ide_drive_t *drive, struct ide_taskfile *tf)
        tf->command = ATA_CMD_SET_MULTI;
 }
 
-static ide_startstop_t ide_disk_special(ide_drive_t *drive)
+/**
+ *     do_special              -       issue some special commands
+ *     @drive: drive the command is for
+ *
+ *     do_special() is used to issue ATA_CMD_INIT_DEV_PARAMS,
+ *     ATA_CMD_RESTORE and ATA_CMD_SET_MULTI commands to a drive.
+ */
+
+static ide_startstop_t do_special(ide_drive_t *drive)
 {
-       special_t *s = &drive->special;
        struct ide_cmd cmd;
 
+#ifdef DEBUG
+       printk(KERN_DEBUG "%s: %s: 0x%02x\n", drive->name, __func__,
+               drive->special_flags);
+#endif
+       if (drive->media != ide_disk) {
+               drive->special_flags = 0;
+               drive->mult_req = 0;
+               return ide_stopped;
+       }
+
        memset(&cmd, 0, sizeof(cmd));
        cmd.protocol = ATA_PROT_NODATA;
 
-       if (s->b.set_geometry) {
-               s->b.set_geometry = 0;
+       if (drive->special_flags & IDE_SFLAG_SET_GEOMETRY) {
+               drive->special_flags &= ~IDE_SFLAG_SET_GEOMETRY;
                ide_tf_set_specify_cmd(drive, &cmd.tf);
-       } else if (s->b.recalibrate) {
-               s->b.recalibrate = 0;
+       } else if (drive->special_flags & IDE_SFLAG_RECALIBRATE) {
+               drive->special_flags &= ~IDE_SFLAG_RECALIBRATE;
                ide_tf_set_restore_cmd(drive, &cmd.tf);
-       } else if (s->b.set_multmode) {
-               s->b.set_multmode = 0;
+       } else if (drive->special_flags & IDE_SFLAG_SET_MULTMODE) {
+               drive->special_flags &= ~IDE_SFLAG_SET_MULTMODE;
                ide_tf_set_setmult_cmd(drive, &cmd.tf);
-       } else if (s->all) {
-               int special = s->all;
-               s->all = 0;
-               printk(KERN_ERR "%s: bad special flag: 0x%02x\n", drive->name, special);
-               return ide_stopped;
-       }
+       } else
+               BUG();
 
        cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE;
        cmd.valid.in.tf  = IDE_VALID_IN_TF  | IDE_VALID_DEVICE;
@@ -217,31 +230,6 @@ static ide_startstop_t ide_disk_special(ide_drive_t *drive)
        return ide_started;
 }
 
-/**
- *     do_special              -       issue some special commands
- *     @drive: drive the command is for
- *
- *     do_special() is used to issue ATA_CMD_INIT_DEV_PARAMS,
- *     ATA_CMD_RESTORE and ATA_CMD_SET_MULTI commands to a drive.
- *
- *     It used to do much more, but has been scaled back.
- */
-
-static ide_startstop_t do_special (ide_drive_t *drive)
-{
-       special_t *s = &drive->special;
-
-#ifdef DEBUG
-       printk("%s: do_special: 0x%02x\n", drive->name, s->all);
-#endif
-       if (drive->media == ide_disk)
-               return ide_disk_special(drive);
-
-       s->all = 0;
-       drive->mult_req = 0;
-       return ide_stopped;
-}
-
 void ide_map_sg(ide_drive_t *drive, struct ide_cmd *cmd)
 {
        ide_hwif_t *hwif = drive->hwif;
@@ -351,7 +339,8 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq)
                printk(KERN_ERR "%s: drive not ready for command\n", drive->name);
                return startstop;
        }
-       if (!drive->special.all) {
+
+       if (drive->special_flags == 0) {
                struct ide_driver *drv;
 
                /*
@@ -499,11 +488,15 @@ repeat:
 
                if ((hwif->host->host_flags & IDE_HFLAG_SERIALIZE) &&
                    hwif != prev_port) {
+                       ide_drive_t *cur_dev =
+                               prev_port ? prev_port->cur_dev : NULL;
+
                        /*
                         * set nIEN for previous port, drives in the
-                        * quirk_list may not like intr setups/cleanups
+                        * quirk list may not like intr setups/cleanups
                         */
-                       if (prev_port && prev_port->cur_dev->quirk_list == 0)
+                       if (cur_dev &&
+                           (cur_dev->dev_flags & IDE_DFLAG_NIEN_QUIRK) == 0)
                                prev_port->tp_ops->write_devctl(prev_port,
                                                                ATA_NIEN |
                                                                ATA_DEVCTL_OBS);
index 06fe002116ecc733a1015216d7676e4b24439070..fa047150a1c691f47f9c74a8404810d451332619 100644 (file)
@@ -282,6 +282,29 @@ no_80w:
        return 0;
 }
 
+static const char *nien_quirk_list[] = {
+       "QUANTUM FIREBALLlct08 08",
+       "QUANTUM FIREBALLP KA6.4",
+       "QUANTUM FIREBALLP KA9.1",
+       "QUANTUM FIREBALLP KX13.6",
+       "QUANTUM FIREBALLP KX20.5",
+       "QUANTUM FIREBALLP KX27.3",
+       "QUANTUM FIREBALLP LM20.4",
+       "QUANTUM FIREBALLP LM20.5",
+       NULL
+};
+
+void ide_check_nien_quirk_list(ide_drive_t *drive)
+{
+       const char **list, *m = (char *)&drive->id[ATA_ID_PROD];
+
+       for (list = nien_quirk_list; *list != NULL; list++)
+               if (strstr(m, *list) != NULL) {
+                       drive->dev_flags |= IDE_DFLAG_NIEN_QUIRK;
+                       return;
+               }
+}
+
 int ide_driveid_update(ide_drive_t *drive)
 {
        u16 *id;
@@ -311,7 +334,6 @@ int ide_driveid_update(ide_drive_t *drive)
 
        return 1;
 out_err:
-       SELECT_MASK(drive, 0);
        if (rc == 2)
                printk(KERN_ERR "%s: %s: bad status\n", drive->name, __func__);
        kfree(id);
@@ -365,7 +387,7 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed)
 
        tp_ops->exec_command(hwif, ATA_CMD_SET_FEATURES);
 
-       if (drive->quirk_list == 2)
+       if (drive->dev_flags & IDE_DFLAG_NIEN_QUIRK)
                tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS);
 
        error = __ide_wait_stat(drive, drive->ready_stat,
index 8c5dcbf22547a23d0eca965fd573340ca50e06d8..b9654a7bb7becb2d7f2b07aa126b075e4e611b89 100644 (file)
@@ -1,7 +1,7 @@
 #include <linux/kernel.h>
 #include <linux/ide.h>
 
-static void ide_legacy_init_one(hw_regs_t **hws, hw_regs_t *hw,
+static void ide_legacy_init_one(struct ide_hw **hws, struct ide_hw *hw,
                                u8 port_no, const struct ide_port_info *d,
                                unsigned long config)
 {
@@ -33,7 +33,6 @@ static void ide_legacy_init_one(hw_regs_t **hws, hw_regs_t *hw,
 
        ide_std_init_ports(hw, base, ctl);
        hw->irq = irq;
-       hw->chipset = d->chipset;
        hw->config = config;
 
        hws[port_no] = hw;
@@ -41,7 +40,7 @@ static void ide_legacy_init_one(hw_regs_t **hws, hw_regs_t *hw,
 
 int ide_legacy_device_add(const struct ide_port_info *d, unsigned long config)
 {
-       hw_regs_t hw[2], *hws[] = { NULL, NULL, NULL, NULL };
+       struct ide_hw hw[2], *hws[] = { NULL, NULL };
 
        memset(&hw, 0, sizeof(hw));
 
@@ -53,6 +52,6 @@ int ide_legacy_device_add(const struct ide_port_info *d, unsigned long config)
            (d->host_flags & IDE_HFLAG_SINGLE))
                return -ENOENT;
 
-       return ide_host_add(d, hws, NULL);
+       return ide_host_add(d, hws, 2, NULL);
 }
 EXPORT_SYMBOL_GPL(ide_legacy_device_add);
index 6e80b774e88a8fb538c08d98298775eb3514eee0..017b1df3b8054c476b8e7e3dbc3b4112d2d739fc 100644 (file)
@@ -29,6 +29,7 @@ static struct pnp_device_id idepnp_devices[] = {
 
 static const struct ide_port_info ide_pnp_port_info = {
        .host_flags             = IDE_HFLAG_NO_DMA,
+       .chipset                = ide_generic,
 };
 
 static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
@@ -36,7 +37,7 @@ static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
        struct ide_host *host;
        unsigned long base, ctl;
        int rc;
-       hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+       struct ide_hw hw, *hws[] = { &hw };
 
        printk(KERN_INFO DRV_NAME ": generic PnP IDE interface\n");
 
@@ -62,9 +63,8 @@ static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
        memset(&hw, 0, sizeof(hw));
        ide_std_init_ports(&hw, base, ctl);
        hw.irq = pnp_irq(dev, 0);
-       hw.chipset = ide_generic;
 
-       rc = ide_host_add(&ide_pnp_port_info, hws, &host);
+       rc = ide_host_add(&ide_pnp_port_info, hws, 1, &host);
        if (rc)
                goto out;
 
index c895ed52b2e870909cd103d1e82c7318288a16f6..f371b0de314f75374a6728d206dabb3a24ef676e 100644 (file)
@@ -97,7 +97,7 @@ static void ide_disk_init_mult_count(ide_drive_t *drive)
                drive->mult_req = id[ATA_ID_MULTSECT] & 0xff;
 
                if (drive->mult_req)
-                       drive->special.b.set_multmode = 1;
+                       drive->special_flags |= IDE_SFLAG_SET_MULTMODE;
        }
 }
 
@@ -465,23 +465,8 @@ static u8 probe_for_drive(ide_drive_t *drive)
        int rc;
        u8 cmd;
 
-       /*
-        *      In order to keep things simple we have an id
-        *      block for all drives at all times. If the device
-        *      is pre ATA or refuses ATA/ATAPI identify we
-        *      will add faked data to this.
-        *
-        *      Also note that 0 everywhere means "can't do X"
-        */
        drive->dev_flags &= ~IDE_DFLAG_ID_READ;
 
-       drive->id = kzalloc(SECTOR_SIZE, GFP_KERNEL);
-       if (drive->id == NULL) {
-               printk(KERN_ERR "ide: out of memory for id data.\n");
-               return 0;
-       }
-
        m = (char *)&drive->id[ATA_ID_PROD];
        strcpy(m, "UNKNOWN");
 
@@ -497,7 +482,7 @@ static u8 probe_for_drive(ide_drive_t *drive)
                }
 
                if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
-                       goto out_free;
+                       return 0;
 
                /* identification failed? */
                if ((drive->dev_flags & IDE_DFLAG_ID_READ) == 0) {
@@ -521,7 +506,7 @@ static u8 probe_for_drive(ide_drive_t *drive)
        }
 
        if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
-               goto out_free;
+               return 0;
 
        /* The drive wasn't being helpful. Add generic info only */
        if ((drive->dev_flags & IDE_DFLAG_ID_READ) == 0) {
@@ -535,9 +520,6 @@ static u8 probe_for_drive(ide_drive_t *drive)
        }
 
        return 1;
-out_free:
-       kfree(drive->id);
-       return 0;
 }
 
 static void hwif_release_dev(struct device *dev)
@@ -702,8 +684,14 @@ static int ide_probe_port(ide_hwif_t *hwif)
        if (irqd)
                disable_irq(hwif->irq);
 
-       if (ide_port_wait_ready(hwif) == -EBUSY)
-               printk(KERN_DEBUG "%s: Wait for ready failed before probe !\n", hwif->name);
+       rc = ide_port_wait_ready(hwif);
+       if (rc == -ENODEV) {
+               printk(KERN_INFO "%s: no devices on the port\n", hwif->name);
+               goto out;
+       } else if (rc == -EBUSY)
+               printk(KERN_ERR "%s: not ready before the probe\n", hwif->name);
+       else
+               rc = -ENODEV;
 
        /*
         * Second drive should only exist if first drive was found,
@@ -714,7 +702,7 @@ static int ide_probe_port(ide_hwif_t *hwif)
                if (drive->dev_flags & IDE_DFLAG_PRESENT)
                        rc = 0;
        }
-
+out:
        /*
         * Use cached IRQ number. It might be (and is...) changed by probe
         * code above
@@ -732,6 +720,8 @@ static void ide_port_tune_devices(ide_hwif_t *hwif)
        int i;
 
        ide_port_for_each_present_dev(i, drive, hwif) {
+               ide_check_nien_quirk_list(drive);
+
                if (port_ops && port_ops->quirkproc)
                        port_ops->quirkproc(drive);
        }
@@ -817,8 +807,6 @@ static int ide_port_setup_devices(ide_hwif_t *hwif)
                if (ide_init_queue(drive)) {
                        printk(KERN_ERR "ide: failed to init %s\n",
                                        drive->name);
-                       kfree(drive->id);
-                       drive->id = NULL;
                        drive->dev_flags &= ~IDE_DFLAG_PRESENT;
                        continue;
                }
@@ -947,9 +935,6 @@ static void drive_release_dev (struct device *dev)
        blk_cleanup_queue(drive->queue);
        drive->queue = NULL;
 
-       kfree(drive->id);
-       drive->id = NULL;
-
        drive->dev_flags &= ~IDE_DFLAG_PRESENT;
 
        complete(&drive->gendev_rel_comp);
@@ -1035,6 +1020,15 @@ static void ide_port_init_devices(ide_hwif_t *hwif)
                if (port_ops && port_ops->init_dev)
                        port_ops->init_dev(drive);
        }
+
+       ide_port_for_each_dev(i, drive, hwif) {
+               /*
+                * default to PIO Mode 0 before we figure out
+                * the most suited mode for the attached device
+                */
+               if (port_ops && port_ops->set_pio_mode)
+                       port_ops->set_pio_mode(drive, 0);
+       }
 }
 
 static void ide_init_port(ide_hwif_t *hwif, unsigned int port,
@@ -1042,8 +1036,7 @@ static void ide_init_port(ide_hwif_t *hwif, unsigned int port,
 {
        hwif->channel = port;
 
-       if (d->chipset)
-               hwif->chipset = d->chipset;
+       hwif->chipset = d->chipset ? d->chipset : ide_pci;
 
        if (d->init_iops)
                d->init_iops(hwif);
@@ -1124,16 +1117,19 @@ static void ide_port_init_devices_data(ide_hwif_t *hwif)
 
        ide_port_for_each_dev(i, drive, hwif) {
                u8 j = (hwif->index * MAX_DRIVES) + i;
+               u16 *saved_id = drive->id;
 
                memset(drive, 0, sizeof(*drive));
+               memset(saved_id, 0, SECTOR_SIZE);
+               drive->id = saved_id;
 
                drive->media                    = ide_disk;
                drive->select                   = (i << 4) | ATA_DEVICE_OBS;
                drive->hwif                     = hwif;
                drive->ready_stat               = ATA_DRDY;
                drive->bad_wstat                = BAD_W_STAT;
-               drive->special.b.recalibrate    = 1;
-               drive->special.b.set_geometry   = 1;
+               drive->special_flags            = IDE_SFLAG_RECALIBRATE |
+                                                 IDE_SFLAG_SET_GEOMETRY;
                drive->name[0]                  = 'h';
                drive->name[1]                  = 'd';
                drive->name[2]                  = 'a' + j;
@@ -1168,11 +1164,10 @@ static void ide_init_port_data(ide_hwif_t *hwif, unsigned int index)
        ide_port_init_devices_data(hwif);
 }
 
-static void ide_init_port_hw(ide_hwif_t *hwif, hw_regs_t *hw)
+static void ide_init_port_hw(ide_hwif_t *hwif, struct ide_hw *hw)
 {
        memcpy(&hwif->io_ports, &hw->io_ports, sizeof(hwif->io_ports));
        hwif->irq = hw->irq;
-       hwif->chipset = hw->chipset;
        hwif->dev = hw->dev;
        hwif->gendev.parent = hw->parent ? hw->parent : hw->dev;
        hwif->ack_intr = hw->ack_intr;
@@ -1233,8 +1228,10 @@ static void ide_port_free_devices(ide_hwif_t *hwif)
        ide_drive_t *drive;
        int i;
 
-       ide_port_for_each_dev(i, drive, hwif)
+       ide_port_for_each_dev(i, drive, hwif) {
+               kfree(drive->id);
                kfree(drive);
+       }
 }
 
 static int ide_port_alloc_devices(ide_hwif_t *hwif, int node)
@@ -1248,6 +1245,18 @@ static int ide_port_alloc_devices(ide_hwif_t *hwif, int node)
                if (drive == NULL)
                        goto out_nomem;
 
+               /*
+                * In order to keep things simple we have an id
+                * block for all drives at all times. If the device
+                * is pre ATA or refuses ATA/ATAPI identify we
+                * will add faked data to this.
+                *
+                * Also note that 0 everywhere means "can't do X"
+                */
+               drive->id = kzalloc_node(SECTOR_SIZE, GFP_KERNEL, node);
+               if (drive->id == NULL)
+                       goto out_nomem;
+
                hwif->devices[i] = drive;
        }
        return 0;
@@ -1257,7 +1266,8 @@ out_nomem:
        return -ENOMEM;
 }
 
-struct ide_host *ide_host_alloc(const struct ide_port_info *d, hw_regs_t **hws)
+struct ide_host *ide_host_alloc(const struct ide_port_info *d,
+                               struct ide_hw **hws, unsigned int n_ports)
 {
        struct ide_host *host;
        struct device *dev = hws[0] ? hws[0]->dev : NULL;
@@ -1268,7 +1278,7 @@ struct ide_host *ide_host_alloc(const struct ide_port_info *d, hw_regs_t **hws)
        if (host == NULL)
                return NULL;
 
-       for (i = 0; i < MAX_HOST_PORTS; i++) {
+       for (i = 0; i < n_ports; i++) {
                ide_hwif_t *hwif;
                int idx;
 
@@ -1288,6 +1298,7 @@ struct ide_host *ide_host_alloc(const struct ide_port_info *d, hw_regs_t **hws)
                if (idx < 0) {
                        printk(KERN_ERR "%s: no free slot for interface\n",
                                        d ? d->name : "ide");
+                       ide_port_free_devices(hwif);
                        kfree(hwif);
                        continue;
                }
@@ -1344,7 +1355,7 @@ static void ide_disable_port(ide_hwif_t *hwif)
 }
 
 int ide_host_register(struct ide_host *host, const struct ide_port_info *d,
-                     hw_regs_t **hws)
+                     struct ide_hw **hws)
 {
        ide_hwif_t *hwif, *mate = NULL;
        int i, j = 0;
@@ -1438,13 +1449,13 @@ int ide_host_register(struct ide_host *host, const struct ide_port_info *d,
 }
 EXPORT_SYMBOL_GPL(ide_host_register);
 
-int ide_host_add(const struct ide_port_info *d, hw_regs_t **hws,
-                struct ide_host **hostp)
+int ide_host_add(const struct ide_port_info *d, struct ide_hw **hws,
+                unsigned int n_ports, struct ide_host **hostp)
 {
        struct ide_host *host;
        int rc;
 
-       host = ide_host_alloc(d, hws);
+       host = ide_host_alloc(d, hws, n_ports);
        if (host == NULL)
                return -ENOMEM;
 
index d9764f0bc82f54e4b250c6e03754d108e2252383..4b447a8a49d4f7c44cb1c37a2390f902802dc519 100644 (file)
@@ -240,18 +240,27 @@ static struct class *idetape_sysfs_class;
 
 static void ide_tape_release(struct device *);
 
-static struct ide_tape_obj *ide_tape_get(struct gendisk *disk)
+static struct ide_tape_obj *idetape_devs[MAX_HWIFS * MAX_DRIVES];
+
+static struct ide_tape_obj *ide_tape_get(struct gendisk *disk, bool cdev,
+                                        unsigned int i)
 {
        struct ide_tape_obj *tape = NULL;
 
        mutex_lock(&idetape_ref_mutex);
-       tape = ide_drv_g(disk, ide_tape_obj);
+
+       if (cdev)
+               tape = idetape_devs[i];
+       else
+               tape = ide_drv_g(disk, ide_tape_obj);
+
        if (tape) {
                if (ide_device_get(tape->drive))
                        tape = NULL;
                else
                        get_device(&tape->dev);
        }
+
        mutex_unlock(&idetape_ref_mutex);
        return tape;
 }
@@ -266,24 +275,6 @@ static void ide_tape_put(struct ide_tape_obj *tape)
        mutex_unlock(&idetape_ref_mutex);
 }
 
-/*
- * The variables below are used for the character device interface. Additional
- * state variables are defined in our ide_drive_t structure.
- */
-static struct ide_tape_obj *idetape_devs[MAX_HWIFS * MAX_DRIVES];
-
-static struct ide_tape_obj *ide_tape_chrdev_get(unsigned int i)
-{
-       struct ide_tape_obj *tape = NULL;
-
-       mutex_lock(&idetape_ref_mutex);
-       tape = idetape_devs[i];
-       if (tape)
-               get_device(&tape->dev);
-       mutex_unlock(&idetape_ref_mutex);
-       return tape;
-}
-
 /*
  * called on each failed packet command retry to analyze the request sense. We
  * currently do not utilize this information.
@@ -397,7 +388,8 @@ static int ide_tape_callback(ide_drive_t *drive, int dsc)
                if (readpos[0] & 0x4) {
                        printk(KERN_INFO "ide-tape: Block location is unknown"
                                         "to the tape\n");
-                       clear_bit(IDE_AFLAG_ADDRESS_VALID, &drive->atapi_flags);
+                       clear_bit(ilog2(IDE_AFLAG_ADDRESS_VALID),
+                                 &drive->atapi_flags);
                        uptodate = 0;
                        err = IDE_DRV_ERROR_GENERAL;
                } else {
@@ -406,7 +398,8 @@ static int ide_tape_callback(ide_drive_t *drive, int dsc)
 
                        tape->partition = readpos[1];
                        tape->first_frame = be32_to_cpup((__be32 *)&readpos[4]);
-                       set_bit(IDE_AFLAG_ADDRESS_VALID, &drive->atapi_flags);
+                       set_bit(ilog2(IDE_AFLAG_ADDRESS_VALID),
+                               &drive->atapi_flags);
                }
        }
 
@@ -656,15 +649,15 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive,
 
        if ((drive->dev_flags & IDE_DFLAG_DSC_OVERLAP) == 0 &&
            (rq->cmd[13] & REQ_IDETAPE_PC2) == 0)
-               set_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags);
+               drive->atapi_flags |= IDE_AFLAG_IGNORE_DSC;
 
        if (drive->dev_flags & IDE_DFLAG_POST_RESET) {
-               set_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags);
+               drive->atapi_flags |= IDE_AFLAG_IGNORE_DSC;
                drive->dev_flags &= ~IDE_DFLAG_POST_RESET;
        }
 
-       if (!test_and_clear_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags) &&
-           (stat & ATA_DSC) == 0) {
+       if (!(drive->atapi_flags & IDE_AFLAG_IGNORE_DSC) &&
+           !(stat & ATA_DSC)) {
                if (postponed_rq == NULL) {
                        tape->dsc_polling_start = jiffies;
                        tape->dsc_poll_freq = tape->best_dsc_rw_freq;
@@ -684,7 +677,9 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive,
                        tape->dsc_poll_freq = IDETAPE_DSC_MA_SLOW;
                idetape_postpone_request(drive);
                return ide_stopped;
-       }
+       } else
+               drive->atapi_flags &= ~IDE_AFLAG_IGNORE_DSC;
+
        if (rq->cmd[13] & REQ_IDETAPE_READ) {
                pc = &tape->queued_pc;
                ide_tape_create_rw_cmd(tape, pc, rq, READ_6);
@@ -744,7 +739,7 @@ static int idetape_wait_ready(ide_drive_t *drive, unsigned long timeout)
        int load_attempted = 0;
 
        /* Wait for the tape to become ready */
-       set_bit(IDE_AFLAG_MEDIUM_PRESENT, &drive->atapi_flags);
+       set_bit(ilog2(IDE_AFLAG_MEDIUM_PRESENT), &drive->atapi_flags);
        timeout += jiffies;
        while (time_before(jiffies, timeout)) {
                if (ide_do_test_unit_ready(drive, disk) == 0)
@@ -820,7 +815,7 @@ static void __ide_tape_discard_merge_buffer(ide_drive_t *drive)
        if (tape->chrdev_dir != IDETAPE_DIR_READ)
                return;
 
-       clear_bit(IDE_AFLAG_FILEMARK, &drive->atapi_flags);
+       clear_bit(ilog2(IDE_AFLAG_FILEMARK), &drive->atapi_flags);
        tape->valid = 0;
        if (tape->buf != NULL) {
                kfree(tape->buf);
@@ -1113,7 +1108,8 @@ static int idetape_space_over_filemarks(ide_drive_t *drive, short mt_op,
 
        if (tape->chrdev_dir == IDETAPE_DIR_READ) {
                tape->valid = 0;
-               if (test_and_clear_bit(IDE_AFLAG_FILEMARK, &drive->atapi_flags))
+               if (test_and_clear_bit(ilog2(IDE_AFLAG_FILEMARK),
+                                      &drive->atapi_flags))
                        ++count;
                ide_tape_discard_merge_buffer(drive, 0);
        }
@@ -1168,7 +1164,7 @@ static ssize_t idetape_chrdev_read(struct file *file, char __user *buf,
        debug_log(DBG_CHRDEV, "Enter %s, count %Zd\n", __func__, count);
 
        if (tape->chrdev_dir != IDETAPE_DIR_READ) {
-               if (test_bit(IDE_AFLAG_DETECT_BS, &drive->atapi_flags))
+               if (test_bit(ilog2(IDE_AFLAG_DETECT_BS), &drive->atapi_flags))
                        if (count > tape->blk_size &&
                            (count % tape->blk_size) == 0)
                                tape->user_bs_factor = count / tape->blk_size;
@@ -1184,7 +1180,8 @@ static ssize_t idetape_chrdev_read(struct file *file, char __user *buf,
                /* refill if staging buffer is empty */
                if (!tape->valid) {
                        /* If we are at a filemark, nothing more to read */
-                       if (test_bit(IDE_AFLAG_FILEMARK, &drive->atapi_flags))
+                       if (test_bit(ilog2(IDE_AFLAG_FILEMARK),
+                                    &drive->atapi_flags))
                                break;
                        /* read */
                        if (idetape_queue_rw_tail(drive, REQ_IDETAPE_READ,
@@ -1202,7 +1199,7 @@ static ssize_t idetape_chrdev_read(struct file *file, char __user *buf,
                done += todo;
        }
 
-       if (!done && test_bit(IDE_AFLAG_FILEMARK, &drive->atapi_flags)) {
+       if (!done && test_bit(ilog2(IDE_AFLAG_FILEMARK), &drive->atapi_flags)) {
                debug_log(DBG_SENSE, "%s: spacing over filemark\n", tape->name);
 
                idetape_space_over_filemarks(drive, MTFSF, 1);
@@ -1336,7 +1333,8 @@ static int idetape_mtioctop(ide_drive_t *drive, short mt_op, int mt_count)
                ide_tape_discard_merge_buffer(drive, 0);
                retval = ide_do_start_stop(drive, disk, !IDETAPE_LU_LOAD_MASK);
                if (!retval)
-                       clear_bit(IDE_AFLAG_MEDIUM_PRESENT, &drive->atapi_flags);
+                       clear_bit(ilog2(IDE_AFLAG_MEDIUM_PRESENT),
+                                 &drive->atapi_flags);
                return retval;
        case MTNOP:
                ide_tape_discard_merge_buffer(drive, 0);
@@ -1358,9 +1356,11 @@ static int idetape_mtioctop(ide_drive_t *drive, short mt_op, int mt_count)
                            mt_count % tape->blk_size)
                                return -EIO;
                        tape->user_bs_factor = mt_count / tape->blk_size;
-                       clear_bit(IDE_AFLAG_DETECT_BS, &drive->atapi_flags);
+                       clear_bit(ilog2(IDE_AFLAG_DETECT_BS),
+                                 &drive->atapi_flags);
                } else
-                       set_bit(IDE_AFLAG_DETECT_BS, &drive->atapi_flags);
+                       set_bit(ilog2(IDE_AFLAG_DETECT_BS),
+                               &drive->atapi_flags);
                return 0;
        case MTSEEK:
                ide_tape_discard_merge_buffer(drive, 0);
@@ -1486,7 +1486,7 @@ static int idetape_chrdev_open(struct inode *inode, struct file *filp)
                return -ENXIO;
 
        lock_kernel();
-       tape = ide_tape_chrdev_get(i);
+       tape = ide_tape_get(NULL, true, i);
        if (!tape) {
                unlock_kernel();
                return -ENXIO;
@@ -1505,20 +1505,20 @@ static int idetape_chrdev_open(struct inode *inode, struct file *filp)
 
        filp->private_data = tape;
 
-       if (test_and_set_bit(IDE_AFLAG_BUSY, &drive->atapi_flags)) {
+       if (test_and_set_bit(ilog2(IDE_AFLAG_BUSY), &drive->atapi_flags)) {
                retval = -EBUSY;
                goto out_put_tape;
        }
 
        retval = idetape_wait_ready(drive, 60 * HZ);
        if (retval) {
-               clear_bit(IDE_AFLAG_BUSY, &drive->atapi_flags);
+               clear_bit(ilog2(IDE_AFLAG_BUSY), &drive->atapi_flags);
                printk(KERN_ERR "ide-tape: %s: drive not ready\n", tape->name);
                goto out_put_tape;
        }
 
        idetape_read_position(drive);
-       if (!test_bit(IDE_AFLAG_ADDRESS_VALID, &drive->atapi_flags))
+       if (!test_bit(ilog2(IDE_AFLAG_ADDRESS_VALID), &drive->atapi_flags))
                (void)idetape_rewind_tape(drive);
 
        /* Read block size and write protect status from drive. */
@@ -1534,7 +1534,7 @@ static int idetape_chrdev_open(struct inode *inode, struct file *filp)
        if (tape->write_prot) {
                if ((filp->f_flags & O_ACCMODE) == O_WRONLY ||
                    (filp->f_flags & O_ACCMODE) == O_RDWR) {
-                       clear_bit(IDE_AFLAG_BUSY, &drive->atapi_flags);
+                       clear_bit(ilog2(IDE_AFLAG_BUSY), &drive->atapi_flags);
                        retval = -EROFS;
                        goto out_put_tape;
                }
@@ -1591,15 +1591,17 @@ static int idetape_chrdev_release(struct inode *inode, struct file *filp)
                        ide_tape_discard_merge_buffer(drive, 1);
        }
 
-       if (minor < 128 && test_bit(IDE_AFLAG_MEDIUM_PRESENT, &drive->atapi_flags))
+       if (minor < 128 && test_bit(ilog2(IDE_AFLAG_MEDIUM_PRESENT),
+                                   &drive->atapi_flags))
                (void) idetape_rewind_tape(drive);
+
        if (tape->chrdev_dir == IDETAPE_DIR_NONE) {
                if (tape->door_locked == DOOR_LOCKED) {
                        if (!ide_set_media_lock(drive, tape->disk, 0))
                                tape->door_locked = DOOR_UNLOCKED;
                }
        }
-       clear_bit(IDE_AFLAG_BUSY, &drive->atapi_flags);
+       clear_bit(ilog2(IDE_AFLAG_BUSY), &drive->atapi_flags);
        ide_tape_put(tape);
        unlock_kernel();
        return 0;
@@ -1905,7 +1907,7 @@ static const struct file_operations idetape_fops = {
 
 static int idetape_open(struct block_device *bdev, fmode_t mode)
 {
-       struct ide_tape_obj *tape = ide_tape_get(bdev->bd_disk);
+       struct ide_tape_obj *tape = ide_tape_get(bdev->bd_disk, false, 0);
 
        if (!tape)
                return -ENXIO;
index a0c3e1b2f73c20b6b005eb9ce2b35ea61bbb42f1..75b85a8cd2d4935ba500927946fa292c890e0e8d 100644 (file)
@@ -98,7 +98,6 @@ ide_startstop_t do_rw_taskfile(ide_drive_t *drive, struct ide_cmd *orig_cmd)
        if ((cmd->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0) {
                ide_tf_dump(drive->name, cmd);
                tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS);
-               SELECT_MASK(drive, 0);
 
                if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) {
                        u8 data[2] = { cmd->tf.data, cmd->hob.data };
@@ -166,7 +165,7 @@ static ide_startstop_t task_no_data_intr(ide_drive_t *drive)
        if (!OK_STAT(stat, ATA_DRDY, BAD_STAT)) {
                if (custom && tf->command == ATA_CMD_SET_MULTI) {
                        drive->mult_req = drive->mult_count = 0;
-                       drive->special.b.recalibrate = 1;
+                       drive->special_flags |= IDE_SFLAG_RECALIBRATE;
                        (void)ide_dump_status(drive, __func__, stat);
                        return ide_stopped;
                } else if (custom && tf->command == ATA_CMD_INIT_DEV_PARAMS) {
index 92c9b90931e73393f0d47b311b3fb1839483a1df..16d056939f9f313119c5a0c51da62456d85669aa 100644 (file)
@@ -211,6 +211,11 @@ static unsigned int ide_noflush;
 module_param_call(noflush, ide_set_dev_param_mask, NULL, &ide_noflush, 0);
 MODULE_PARM_DESC(noflush, "disable flush requests for a device");
 
+static unsigned int ide_nohpa;
+
+module_param_call(nohpa, ide_set_dev_param_mask, NULL, &ide_nohpa, 0);
+MODULE_PARM_DESC(nohpa, "disable Host Protected Area for a device");
+
 static unsigned int ide_noprobe;
 
 module_param_call(noprobe, ide_set_dev_param_mask, NULL, &ide_noprobe, 0);
@@ -281,6 +286,11 @@ static void ide_dev_apply_params(ide_drive_t *drive, u8 unit)
                                 drive->name);
                drive->dev_flags |= IDE_DFLAG_NOFLUSH;
        }
+       if (ide_nohpa & (1 << i)) {
+               printk(KERN_INFO "ide: disabling Host Protected Area for %s\n",
+                                drive->name);
+               drive->dev_flags |= IDE_DFLAG_NOHPA;
+       }
        if (ide_noprobe & (1 << i)) {
                printk(KERN_INFO "ide: skipping probe for %s\n", drive->name);
                drive->dev_flags |= IDE_DFLAG_NOPROBE;
index 051b4ab0f359d033745e0a035235a656dea7be4e..ee9b55ecc62b10741d1c7693d00ca8209f66ca78 100644 (file)
@@ -21,7 +21,7 @@
 #include <linux/platform_device.h>
 #include <linux/io.h>
 
-static void __devinit plat_ide_setup_ports(hw_regs_t *hw,
+static void __devinit plat_ide_setup_ports(struct ide_hw *hw,
                                           void __iomem *base,
                                           void __iomem *ctrl,
                                           struct pata_platform_info *pdata,
@@ -40,12 +40,11 @@ static void __devinit plat_ide_setup_ports(hw_regs_t *hw,
        hw->io_ports.ctl_addr = (unsigned long)ctrl;
 
        hw->irq = irq;
-
-       hw->chipset = ide_generic;
 }
 
 static const struct ide_port_info platform_ide_port_info = {
        .host_flags             = IDE_HFLAG_NO_DMA,
+       .chipset                = ide_generic,
 };
 
 static int __devinit plat_ide_probe(struct platform_device *pdev)
@@ -55,7 +54,7 @@ static int __devinit plat_ide_probe(struct platform_device *pdev)
        struct pata_platform_info *pdata;
        struct ide_host *host;
        int ret = 0, mmio = 0;
-       hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+       struct ide_hw hw, *hws[] = { &hw };
        struct ide_port_info d = platform_ide_port_info;
 
        pdata = pdev->dev.platform_data;
@@ -99,7 +98,7 @@ static int __devinit plat_ide_probe(struct platform_device *pdev)
        if (mmio)
                d.host_flags |= IDE_HFLAG_MMIO;
 
-       ret = ide_host_add(&d, hws, &host);
+       ret = ide_host_add(&d, hws, 1, &host);
        if (ret)
                goto out;
 
index 4b1718e832839186c12673f8b589022ba115c55a..1447c8c90565dbdbb669465da8e1e2a5186d8526 100644 (file)
@@ -62,7 +62,7 @@ int macide_ack_intr(ide_hwif_t* hwif)
        return 0;
 }
 
-static void __init macide_setup_ports(hw_regs_t *hw, unsigned long base,
+static void __init macide_setup_ports(struct ide_hw *hw, unsigned long base,
                                      int irq, ide_ack_intr_t *ack_intr)
 {
        int i;
@@ -76,13 +76,12 @@ static void __init macide_setup_ports(hw_regs_t *hw, unsigned long base,
 
        hw->irq = irq;
        hw->ack_intr = ack_intr;
-
-       hw->chipset = ide_generic;
 }
 
 static const struct ide_port_info macide_port_info = {
        .host_flags             = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA,
        .irq_flags              = IRQF_SHARED,
+       .chipset                = ide_generic,
 };
 
 static const char *mac_ide_name[] =
@@ -97,7 +96,7 @@ static int __init macide_init(void)
        ide_ack_intr_t *ack_intr;
        unsigned long base;
        int irq;
-       hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+       struct ide_hw hw, *hws[] = { &hw };
 
        if (!MACH_IS_MAC)
                return -ENODEV;
@@ -127,7 +126,7 @@ static int __init macide_init(void)
 
        macide_setup_ports(&hw, base, irq, ack_intr);
 
-       return ide_host_add(&macide_port_info, hws, NULL);
+       return ide_host_add(&macide_port_info, hws, 1, NULL);
 }
 
 module_init(macide_init);
index 09d813d313f4a44986a2f358d8ddc08c619533f1..3c1dc015215351ed1cf43e00c8fb85b859ff22d0 100644 (file)
@@ -306,6 +306,7 @@ static struct ide_port_info __devinitdata palm_bk3710_port_info = {
        .host_flags             = IDE_HFLAG_MMIO,
        .pio_mask               = ATA_PIO4,
        .mwdma_mask             = ATA_MWDMA2,
+       .chipset                = ide_palm3710,
 };
 
 static int __init palm_bk3710_probe(struct platform_device *pdev)
@@ -315,7 +316,7 @@ static int __init palm_bk3710_probe(struct platform_device *pdev)
        void __iomem *base;
        unsigned long rate, mem_size;
        int i, rc;
-       hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+       struct ide_hw hw, *hws[] = { &hw };
 
        clk = clk_get(&pdev->dev, "IDECLK");
        if (IS_ERR(clk))
@@ -363,13 +364,12 @@ static int __init palm_bk3710_probe(struct platform_device *pdev)
                        (base + IDE_PALM_ATA_PRI_CTL_OFFSET);
        hw.irq = irq->start;
        hw.dev = &pdev->dev;
-       hw.chipset = ide_palm3710;
 
        palm_bk3710_port_info.udma_mask = rate < 100000000 ? ATA_UDMA4 :
                                                             ATA_UDMA5;
 
        /* Register the IDE interface with Linux */
-       rc = ide_host_add(&palm_bk3710_port_info, hws, NULL);
+       rc = ide_host_add(&palm_bk3710_port_info, hws, 1, NULL);
        if (rc)
                goto out;
 
index b68906c3c17e49f4223a1c84c2b68f1a17d6f47e..65ba8239e7b563496ca93c905841f5c731d9e2e3 100644 (file)
 #define DBG(fmt, args...)
 #endif
 
-static const char *pdc_quirk_drives[] = {
-       "QUANTUM FIREBALLlct08 08",
-       "QUANTUM FIREBALLP KA6.4",
-       "QUANTUM FIREBALLP KA9.1",
-       "QUANTUM FIREBALLP LM20.4",
-       "QUANTUM FIREBALLP KX13.6",
-       "QUANTUM FIREBALLP KX20.5",
-       "QUANTUM FIREBALLP KX27.3",
-       "QUANTUM FIREBALLP LM20.5",
-       NULL
-};
-
 static u8 max_dma_rate(struct pci_dev *pdev)
 {
        u8 mode;
@@ -200,19 +188,6 @@ static u8 pdcnew_cable_detect(ide_hwif_t *hwif)
                return ATA_CBL_PATA80;
 }
 
-static void pdcnew_quirkproc(ide_drive_t *drive)
-{
-       const char **list, *m = (char *)&drive->id[ATA_ID_PROD];
-
-       for (list = pdc_quirk_drives; *list != NULL; list++)
-               if (strstr(m, *list) != NULL) {
-                       drive->quirk_list = 2;
-                       return;
-               }
-
-       drive->quirk_list = 0;
-}
-
 static void pdcnew_reset(ide_drive_t *drive)
 {
        /*
@@ -473,7 +448,6 @@ static struct pci_dev * __devinit pdc20270_get_dev2(struct pci_dev *dev)
 static const struct ide_port_ops pdcnew_port_ops = {
        .set_pio_mode           = pdcnew_set_pio_mode,
        .set_dma_mode           = pdcnew_set_dma_mode,
-       .quirkproc              = pdcnew_quirkproc,
        .resetproc              = pdcnew_reset,
        .cable_detect           = pdcnew_cable_detect,
 };
index e24ecc87a9b1837a60bdfce7ae59fffd1b2ad7f1..b6abf7e52cacb04cfa011e7a1f2adf476c28d885 100644 (file)
 
 #define PDC202XX_DEBUG_DRIVE_INFO      0
 
-static const char *pdc_quirk_drives[] = {
-       "QUANTUM FIREBALLlct08 08",
-       "QUANTUM FIREBALLP KA6.4",
-       "QUANTUM FIREBALLP KA9.1",
-       "QUANTUM FIREBALLP LM20.4",
-       "QUANTUM FIREBALLP KX13.6",
-       "QUANTUM FIREBALLP KX20.5",
-       "QUANTUM FIREBALLP KX27.3",
-       "QUANTUM FIREBALLP LM20.5",
-       NULL
-};
-
 static void pdc_old_disable_66MHz_clock(ide_hwif_t *);
 
 static void pdc202xx_set_mode(ide_drive_t *drive, const u8 speed)
@@ -151,19 +139,6 @@ static void pdc_old_disable_66MHz_clock(ide_hwif_t *hwif)
        outb(clock & ~(hwif->channel ? 0x08 : 0x02), clock_reg);
 }
 
-static void pdc202xx_quirkproc(ide_drive_t *drive)
-{
-       const char **list, *m = (char *)&drive->id[ATA_ID_PROD];
-
-       for (list = pdc_quirk_drives; *list != NULL; list++)
-               if (strstr(m, *list) != NULL) {
-                       drive->quirk_list = 2;
-                       return;
-               }
-
-       drive->quirk_list = 0;
-}
-
 static void pdc202xx_dma_start(ide_drive_t *drive)
 {
        if (drive->current_speed > XFER_UDMA_2)
@@ -203,52 +178,6 @@ static int pdc202xx_dma_end(ide_drive_t *drive)
        return ide_dma_end(drive);
 }
 
-static int pdc202xx_dma_test_irq(ide_drive_t *drive)
-{
-       ide_hwif_t *hwif        = drive->hwif;
-       unsigned long high_16   = hwif->extra_base - 16;
-       u8 dma_stat             = inb(hwif->dma_base + ATA_DMA_STATUS);
-       u8 sc1d                 = inb(high_16 + 0x001d);
-
-       if (hwif->channel) {
-               /* bit7: Error, bit6: Interrupting, bit5: FIFO Full, bit4: FIFO Empty */
-               if ((sc1d & 0x50) == 0x50)
-                       goto somebody_else;
-               else if ((sc1d & 0x40) == 0x40)
-                       return (dma_stat & 4) == 4;
-       } else {
-               /* bit3: Error, bit2: Interrupting, bit1: FIFO Full, bit0: FIFO Empty */
-               if ((sc1d & 0x05) == 0x05)
-                       goto somebody_else;
-               else if ((sc1d & 0x04) == 0x04)
-                       return (dma_stat & 4) == 4;
-       }
-somebody_else:
-       return (dma_stat & 4) == 4;     /* return 1 if INTR asserted */
-}
-
-static void pdc202xx_reset(ide_drive_t *drive)
-{
-       ide_hwif_t *hwif        = drive->hwif;
-       unsigned long high_16   = hwif->extra_base - 16;
-       u8 udma_speed_flag      = inb(high_16 | 0x001f);
-
-       printk(KERN_WARNING "PDC202xx: software reset...\n");
-
-       outb(udma_speed_flag | 0x10, high_16 | 0x001f);
-       mdelay(100);
-       outb(udma_speed_flag & ~0x10, high_16 | 0x001f);
-       mdelay(2000);   /* 2 seconds ?! */
-
-       ide_set_max_pio(drive);
-}
-
-static void pdc202xx_dma_lost_irq(ide_drive_t *drive)
-{
-       pdc202xx_reset(drive);
-       ide_dma_lost_irq(drive);
-}
-
 static int init_chipset_pdc202xx(struct pci_dev *dev)
 {
        unsigned long dmabase = pci_resource_start(dev, 4);
@@ -302,37 +231,22 @@ static void __devinit pdc202ata4_fixup_irq(struct pci_dev *dev,
 static const struct ide_port_ops pdc20246_port_ops = {
        .set_pio_mode           = pdc202xx_set_pio_mode,
        .set_dma_mode           = pdc202xx_set_mode,
-       .quirkproc              = pdc202xx_quirkproc,
 };
 
 static const struct ide_port_ops pdc2026x_port_ops = {
        .set_pio_mode           = pdc202xx_set_pio_mode,
        .set_dma_mode           = pdc202xx_set_mode,
-       .quirkproc              = pdc202xx_quirkproc,
-       .resetproc              = pdc202xx_reset,
        .cable_detect           = pdc2026x_cable_detect,
 };
 
-static const struct ide_dma_ops pdc20246_dma_ops = {
-       .dma_host_set           = ide_dma_host_set,
-       .dma_setup              = ide_dma_setup,
-       .dma_start              = ide_dma_start,
-       .dma_end                = ide_dma_end,
-       .dma_test_irq           = pdc202xx_dma_test_irq,
-       .dma_lost_irq           = ide_dma_lost_irq,
-       .dma_timer_expiry       = ide_dma_sff_timer_expiry,
-       .dma_sff_read_status    = ide_dma_sff_read_status,
-};
-
 static const struct ide_dma_ops pdc2026x_dma_ops = {
        .dma_host_set           = ide_dma_host_set,
        .dma_setup              = ide_dma_setup,
        .dma_start              = pdc202xx_dma_start,
        .dma_end                = pdc202xx_dma_end,
-       .dma_test_irq           = pdc202xx_dma_test_irq,
-       .dma_lost_irq           = pdc202xx_dma_lost_irq,
+       .dma_test_irq           = ide_dma_test_irq,
+       .dma_lost_irq           = ide_dma_lost_irq,
        .dma_timer_expiry       = ide_dma_sff_timer_expiry,
-       .dma_clear              = pdc202xx_reset,
        .dma_sff_read_status    = ide_dma_sff_read_status,
 };
 
@@ -354,7 +268,7 @@ static const struct ide_port_info pdc202xx_chipsets[] __devinitdata = {
                .name           = DRV_NAME,
                .init_chipset   = init_chipset_pdc202xx,
                .port_ops       = &pdc20246_port_ops,
-               .dma_ops        = &pdc20246_dma_ops,
+               .dma_ops        = &sff_dma_ops,
                .host_flags     = IDE_HFLAGS_PDC202XX,
                .pio_mask       = ATA_PIO4,
                .mwdma_mask     = ATA_MWDMA2,
index f76e4e6b408f018483c0761c0efe8cdadda46975..97642a7a79c4143afc17e04db691cf555ddc8b40 100644 (file)
@@ -1023,13 +1023,14 @@ static const struct ide_port_info pmac_port_info = {
  * Setup, register & probe an IDE channel driven by this driver, this is
  * called by one of the 2 probe functions (macio or PCI).
  */
-static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw)
+static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif,
+                                          struct ide_hw *hw)
 {
        struct device_node *np = pmif->node;
        const int *bidp;
        struct ide_host *host;
        ide_hwif_t *hwif;
-       hw_regs_t *hws[] = { hw, NULL, NULL, NULL };
+       struct ide_hw *hws[] = { hw };
        struct ide_port_info d = pmac_port_info;
        int rc;
 
@@ -1077,7 +1078,7 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw)
        /* Make sure we have sane timings */
        sanitize_timings(pmif);
 
-       host = ide_host_alloc(&d, hws);
+       host = ide_host_alloc(&d, hws, 1);
        if (host == NULL)
                return -ENOMEM;
        hwif = host->ports[0];
@@ -1124,7 +1125,7 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw)
        return 0;
 }
 
-static void __devinit pmac_ide_init_ports(hw_regs_t *hw, unsigned long base)
+static void __devinit pmac_ide_init_ports(struct ide_hw *hw, unsigned long base)
 {
        int i;
 
@@ -1144,7 +1145,7 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match)
        unsigned long regbase;
        pmac_ide_hwif_t *pmif;
        int irq, rc;
-       hw_regs_t hw;
+       struct ide_hw hw;
 
        pmif = kzalloc(sizeof(*pmif), GFP_KERNEL);
        if (pmif == NULL)
@@ -1268,7 +1269,7 @@ pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id)
        void __iomem *base;
        unsigned long rbase, rlen;
        int rc;
-       hw_regs_t hw;
+       struct ide_hw hw;
 
        np = pci_device_to_OF_node(pdev);
        if (np == NULL) {
index c79346679244228e5671570bea1d335b02cf78c9..ab49a97023d9294c0120635cbb57034ec7907149 100644 (file)
@@ -51,11 +51,11 @@ static int q40ide_default_irq(unsigned long base)
 /*
  * Addresses are pretranslated for Q40 ISA access.
  */
-static void q40_ide_setup_ports(hw_regs_t *hw, unsigned long base,
+static void q40_ide_setup_ports(struct ide_hw *hw, unsigned long base,
                        ide_ack_intr_t *ack_intr,
                        int irq)
 {
-       memset(hw, 0, sizeof(hw_regs_t));
+       memset(hw, 0, sizeof(*hw));
        /* BIG FAT WARNING: 
           assumption: only DATA port is ever used in 16 bit mode */
        hw->io_ports.data_addr = Q40_ISA_IO_W(base);
@@ -70,8 +70,6 @@ static void q40_ide_setup_ports(hw_regs_t *hw, unsigned long base,
 
        hw->irq = irq;
        hw->ack_intr = ack_intr;
-
-       hw->chipset = ide_generic;
 }
 
 static void q40ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd,
@@ -119,6 +117,7 @@ static const struct ide_port_info q40ide_port_info = {
        .tp_ops                 = &q40ide_tp_ops,
        .host_flags             = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA,
        .irq_flags              = IRQF_SHARED,
+       .chipset                = ide_generic,
 };
 
 /* 
@@ -136,7 +135,7 @@ static const char *q40_ide_names[Q40IDE_NUM_HWIFS]={
 static int __init q40ide_init(void)
 {
     int i;
-    hw_regs_t hw[Q40IDE_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL };
+    struct ide_hw hw[Q40IDE_NUM_HWIFS], *hws[] = { NULL, NULL };
 
     if (!MACH_IS_Q40)
       return -ENODEV;
@@ -163,7 +162,7 @@ static int __init q40ide_init(void)
        hws[i] = &hw[i];
     }
 
-    return ide_host_add(&q40ide_port_info, hws, NULL);
+    return ide_host_add(&q40ide_port_info, hws, Q40IDE_NUM_HWIFS, NULL);
 }
 
 module_init(q40ide_init);
index d5003ca69801684bdc71e65db78767a41445c42e..00f54248f41f0d76fbf62e2c791123a0bf17dfe9 100644 (file)
 
 static const struct ide_port_info rapide_port_info = {
        .host_flags             = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA,
+       .chipset                = ide_generic,
 };
 
-static void rapide_setup_ports(hw_regs_t *hw, void __iomem *base,
+static void rapide_setup_ports(struct ide_hw *hw, void __iomem *base,
                               void __iomem *ctrl, unsigned int sz, int irq)
 {
        unsigned long port = (unsigned long)base;
@@ -35,7 +36,7 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id)
        void __iomem *base;
        struct ide_host *host;
        int ret;
-       hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+       struct ide_hw hw, *hws[] = { &hw };
 
        ret = ecard_request_resources(ec);
        if (ret)
@@ -49,10 +50,9 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id)
 
        memset(&hw, 0, sizeof(hw));
        rapide_setup_ports(&hw, base, base + 0x818, 1 << 6, ec->irq);
-       hw.chipset = ide_generic;
        hw.dev = &ec->dev;
 
-       ret = ide_host_add(&rapide_port_info, hws, &host);
+       ret = ide_host_add(&rapide_port_info, hws, 1, &host);
        if (ret)
                goto release;
 
index 5be41f25204f627e775aa4a962febf98a1cdb560..1104bb301eb98eb4f2d22b78a05ba33d1d99761c 100644 (file)
@@ -559,7 +559,7 @@ static int scc_ide_setup_pci_device(struct pci_dev *dev,
 {
        struct scc_ports *ports = pci_get_drvdata(dev);
        struct ide_host *host;
-       hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+       struct ide_hw hw, *hws[] = { &hw };
        int i, rc;
 
        memset(&hw, 0, sizeof(hw));
@@ -567,9 +567,8 @@ static int scc_ide_setup_pci_device(struct pci_dev *dev,
                hw.io_ports_array[i] = ports->dma + 0x20 + i * 4;
        hw.irq = dev->irq;
        hw.dev = &dev->dev;
-       hw.chipset = ide_pci;
 
-       rc = ide_host_add(d, hws, &host);
+       rc = ide_host_add(d, hws, 1, &host);
        if (rc)
                return rc;
 
@@ -823,6 +822,7 @@ static const struct ide_port_info scc_chipset __devinitdata = {
        .host_flags     = IDE_HFLAG_SINGLE,
        .irq_flags      = IRQF_SHARED,
        .pio_mask       = ATA_PIO4,
+       .chipset        = ide_pci,
 };
 
 /**
index 7a3a12d6e638eae21a9bbce4f3f269d55b5a8aa6..ab3db61d2ba038a84128b08870aa7bd091fc9624 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Copyright (C) 1998-2000  Andre Hedrick <andre@linux-ide.org>
  *  Copyright (C) 1995-1998  Mark Lord
- *  Copyright (C)      2007  Bartlomiej Zolnierkiewicz
+ *  Copyright (C) 2007-2009  Bartlomiej Zolnierkiewicz
  *
  *  May be copied or modified under the terms of the GNU General Public License
  */
@@ -301,11 +301,11 @@ static int ide_pci_check_iomem(struct pci_dev *dev, const struct ide_port_info *
 }
 
 /**
- *     ide_hw_configure        -       configure a hw_regs_t instance
+ *     ide_hw_configure        -       configure a struct ide_hw instance
  *     @dev: PCI device holding interface
  *     @d: IDE port info
  *     @port: port number
- *     @hw: hw_regs_t instance corresponding to this port
+ *     @hw: struct ide_hw instance corresponding to this port
  *
  *     Perform the initial set up for the hardware interface structure. This
  *     is done per interface port rather than per PCI device. There may be
@@ -315,7 +315,7 @@ static int ide_pci_check_iomem(struct pci_dev *dev, const struct ide_port_info *
  */
 
 static int ide_hw_configure(struct pci_dev *dev, const struct ide_port_info *d,
-                           unsigned int port, hw_regs_t *hw)
+                           unsigned int port, struct ide_hw *hw)
 {
        unsigned long ctl = 0, base = 0;
 
@@ -344,7 +344,6 @@ static int ide_hw_configure(struct pci_dev *dev, const struct ide_port_info *d,
 
        memset(hw, 0, sizeof(*hw));
        hw->dev = &dev->dev;
-       hw->chipset = d->chipset ? d->chipset : ide_pci;
        ide_std_init_ports(hw, base, ctl | 2);
 
        return 0;
@@ -446,8 +445,8 @@ out:
  *     ide_pci_setup_ports     -       configure ports/devices on PCI IDE
  *     @dev: PCI device
  *     @d: IDE port info
- *     @hw: hw_regs_t instances corresponding to this PCI IDE device
- *     @hws: hw_regs_t pointers table to update
+ *     @hw: struct ide_hw instances corresponding to this PCI IDE device
+ *     @hws: struct ide_hw pointers table to update
  *
  *     Scan the interfaces attached to this device and do any
  *     necessary per port setup. Attach the devices and ask the
@@ -459,7 +458,7 @@ out:
  */
 
 void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d,
-                        hw_regs_t *hw, hw_regs_t **hws)
+                        struct ide_hw *hw, struct ide_hw **hws)
 {
        int channels = (d->host_flags & IDE_HFLAG_SINGLE) ? 1 : 2, port;
        u8 tmp;
@@ -535,61 +534,15 @@ out:
        return ret;
 }
 
-int ide_pci_init_one(struct pci_dev *dev, const struct ide_port_info *d,
-                    void *priv)
-{
-       struct ide_host *host;
-       hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL };
-       int ret;
-
-       ret = ide_setup_pci_controller(dev, d, 1);
-       if (ret < 0)
-               goto out;
-
-       ide_pci_setup_ports(dev, d, &hw[0], &hws[0]);
-
-       host = ide_host_alloc(d, hws);
-       if (host == NULL) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       host->dev[0] = &dev->dev;
-
-       host->host_priv = priv;
-
-       host->irq_flags = IRQF_SHARED;
-
-       pci_set_drvdata(dev, host);
-
-       ret = do_ide_setup_pci_device(dev, d, 1);
-       if (ret < 0)
-               goto out;
-
-       /* fixup IRQ */
-       if (ide_pci_is_in_compatibility_mode(dev)) {
-               hw[0].irq = pci_get_legacy_ide_irq(dev, 0);
-               hw[1].irq = pci_get_legacy_ide_irq(dev, 1);
-       } else
-               hw[1].irq = hw[0].irq = ret;
-
-       ret = ide_host_register(host, d, hws);
-       if (ret)
-               ide_host_free(host);
-out:
-       return ret;
-}
-EXPORT_SYMBOL_GPL(ide_pci_init_one);
-
 int ide_pci_init_two(struct pci_dev *dev1, struct pci_dev *dev2,
                     const struct ide_port_info *d, void *priv)
 {
        struct pci_dev *pdev[] = { dev1, dev2 };
        struct ide_host *host;
-       int ret, i;
-       hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL };
+       int ret, i, n_ports = dev2 ? 4 : 2;
+       struct ide_hw hw[4], *hws[] = { NULL, NULL, NULL, NULL };
 
-       for (i = 0; i < 2; i++) {
+       for (i = 0; i < n_ports / 2; i++) {
                ret = ide_setup_pci_controller(pdev[i], d, !i);
                if (ret < 0)
                        goto out;
@@ -597,23 +550,24 @@ int ide_pci_init_two(struct pci_dev *dev1, struct pci_dev *dev2,
                ide_pci_setup_ports(pdev[i], d, &hw[i*2], &hws[i*2]);
        }
 
-       host = ide_host_alloc(d, hws);
+       host = ide_host_alloc(d, hws, n_ports);
        if (host == NULL) {
                ret = -ENOMEM;
                goto out;
        }
 
        host->dev[0] = &dev1->dev;
-       host->dev[1] = &dev2->dev;
+       if (dev2)
+               host->dev[1] = &dev2->dev;
 
        host->host_priv = priv;
-
        host->irq_flags = IRQF_SHARED;
 
        pci_set_drvdata(pdev[0], host);
-       pci_set_drvdata(pdev[1], host);
+       if (dev2)
+               pci_set_drvdata(pdev[1], host);
 
-       for (i = 0; i < 2; i++) {
+       for (i = 0; i < n_ports / 2; i++) {
                ret = do_ide_setup_pci_device(pdev[i], d, !i);
 
                /*
@@ -639,6 +593,13 @@ out:
 }
 EXPORT_SYMBOL_GPL(ide_pci_init_two);
 
+int ide_pci_init_one(struct pci_dev *dev, const struct ide_port_info *d,
+                    void *priv)
+{
+       return ide_pci_init_two(dev, NULL, d, priv);
+}
+EXPORT_SYMBOL_GPL(ide_pci_init_one);
+
 void ide_pci_remove(struct pci_dev *dev)
 {
        struct ide_host *host = pci_get_drvdata(dev);
index e5d2a48a84de9314133d2b54c49f63aa08f536e4..5f37f168f94446c0403a676c0ed0ba7e91b0514b 100644 (file)
@@ -91,7 +91,7 @@ typedef struct {
 
 
 static void
-sgiioc4_init_hwif_ports(hw_regs_t * hw, unsigned long data_port,
+sgiioc4_init_hwif_ports(struct ide_hw *hw, unsigned long data_port,
                        unsigned long ctrl_port, unsigned long irq_port)
 {
        unsigned long reg = data_port;
@@ -546,7 +546,7 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev)
        unsigned long cmd_base, irqport;
        unsigned long bar0, cmd_phys_base, ctl;
        void __iomem *virt_base;
-       hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
+       struct ide_hw hw, *hws[] = { &hw };
        int rc;
 
        /*  Get the CmdBlk and CtrlBlk Base Registers */
@@ -575,13 +575,12 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev)
        memset(&hw, 0, sizeof(hw));
        sgiioc4_init_hwif_ports(&hw, cmd_base, ctl, irqport);
        hw.irq = dev->irq;
-       hw.chipset = ide_pci;
        hw.dev = &dev->dev;
 
        /* Initializing chipset IRQ Registers */
        writel(0x03, (void __iomem *)(irqport + IOC4_INTR_SET * 4));
 
-       rc = ide_host_add(&sgiioc4_port_info, hws, NULL);
+       rc = ide_host_add(&sgiioc4_port_info, hws, 1, NULL);
        if (!rc)
                return 0;
 
index e4973cd1fba9e8735191f73e32e3eb9575f1d97d..bd82d228608c053dd0ded5ad1faa24749e443858 100644 (file)
@@ -451,8 +451,8 @@ static int sil_sata_reset_poll(ide_drive_t *drive)
 static void sil_sata_pre_reset(ide_drive_t *drive)
 {
        if (drive->media == ide_disk) {
-               drive->special.b.set_geometry = 0;
-               drive->special.b.recalibrate = 0;
+               drive->special_flags &=
+                       ~(IDE_SFLAG_SET_GEOMETRY | IDE_SFLAG_RECALIBRATE);
        }
 }
 
index b0a460625335c9c469b94ea94a7d4210c305e2ab..0924abff52ff6f1db1d2878c8a816327769d86ec 100644 (file)
@@ -10,7 +10,7 @@
  * with the timing registers setup.
  *  -- Benjamin Herrenschmidt (01/11/03) benh@kernel.crashing.org
  *
- * Copyright (C) 2006-2007 MontaVista Software, Inc. <source@mvista.com>
+ * Copyright (C) 2006-2007,2009 MontaVista Software, Inc. <source@mvista.com>
  * Copyright (C)      2007 Bartlomiej Zolnierkiewicz
  */
 
@@ -146,14 +146,15 @@ static void sl82c105_dma_lost_irq(ide_drive_t *drive)
        u32 val, mask           = hwif->channel ? CTRL_IDE_IRQB : CTRL_IDE_IRQA;
        u8 dma_cmd;
 
-       printk("sl82c105: lost IRQ, resetting host\n");
+       printk(KERN_WARNING "sl82c105: lost IRQ, resetting host\n");
 
        /*
         * Check the raw interrupt from the drive.
         */
        pci_read_config_dword(dev, 0x40, &val);
        if (val & mask)
-               printk("sl82c105: drive was requesting IRQ, but host lost it\n");
+               printk(KERN_INFO "sl82c105: drive was requesting IRQ, "
+                      "but host lost it\n");
 
        /*
         * Was DMA enabled?  If so, disable it - we're resetting the
@@ -162,7 +163,7 @@ static void sl82c105_dma_lost_irq(ide_drive_t *drive)
        dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD);
        if (dma_cmd & 1) {
                outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD);
-               printk("sl82c105: DMA was enabled\n");
+               printk(KERN_INFO "sl82c105: DMA was enabled\n");
        }
 
        sl82c105_reset_host(dev);
index e33d764e2945339a89544ec045f2f6769fb73474..ea89fddeed9122fa002f1149ffb0364a7f579100 100644 (file)
@@ -130,8 +130,7 @@ static const struct ide_port_info tx4938ide_port_info __initdata = {
 
 static int __init tx4938ide_probe(struct platform_device *pdev)
 {
-       hw_regs_t hw;
-       hw_regs_t *hws[] = { &hw, NULL, NULL, NULL };
+       struct ide_hw hw, *hws[] = { &hw };
        struct ide_host *host;
        struct resource *res;
        struct tx4938ide_platform_info *pdata = pdev->dev.platform_data;
@@ -183,7 +182,7 @@ static int __init tx4938ide_probe(struct platform_device *pdev)
                tx4938ide_tune_ebusc(pdata->ebus_ch, pdata->gbus_clock, 0);
        else
                d.port_ops = NULL;
-       ret = ide_host_add(&d, hws, &host);
+       ret = ide_host_add(&d, hws, 1, &host);
        if (!ret)
                platform_set_drvdata(pdev, host);
        return ret;
index 5ca76224f6d11a731b585f98a35ea8e2661c4e5e..64b58ecc3f0ea7131d697634e4c848554f5b7e17 100644 (file)
@@ -537,8 +537,7 @@ static const struct ide_port_info tx4939ide_port_info __initdata = {
 
 static int __init tx4939ide_probe(struct platform_device *pdev)
 {
-       hw_regs_t hw;
-       hw_regs_t *hws[] = { &hw, NULL, NULL, NULL };
+       struct ide_hw hw, *hws[] = { &hw };
        struct ide_host *host;
        struct resource *res;
        int irq, ret;
@@ -581,7 +580,7 @@ static int __init tx4939ide_probe(struct platform_device *pdev)
        hw.dev = &pdev->dev;
 
        pr_info("TX4939 IDE interface (base %#lx, irq %d)\n", mapbase, irq);
-       host = ide_host_alloc(&tx4939ide_port_info, hws);
+       host = ide_host_alloc(&tx4939ide_port_info, hws, 1);
        if (!host)
                return -ENOMEM;
        /* use extra_base for base address of the all registers */
index 75223f50de586fa9dc08c77f4f0ccec0a73a6020..0ba6ec87629616e946a64aedad68d86a3b6d4010 100644 (file)
@@ -257,11 +257,8 @@ static void iscsi_iser_cleanup_task(struct iscsi_task *task)
 {
        struct iscsi_iser_task *iser_task = task->dd_data;
 
-       /*
-        * mgmt tasks do not need special cleanup and we do not
-        * allocate anything in the init task callout
-        */
-       if (!task->sc || task->state == ISCSI_TASK_PENDING)
+       /* mgmt tasks do not need special cleanup */
+       if (!task->sc)
                return;
 
        if (iser_task->status == ISER_TASK_STATUS_STARTED) {
@@ -517,7 +514,8 @@ iscsi_iser_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *s
 }
 
 static struct iscsi_endpoint *
-iscsi_iser_ep_connect(struct sockaddr *dst_addr, int non_blocking)
+iscsi_iser_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr,
+                     int non_blocking)
 {
        int err;
        struct iser_conn *ib_conn;
index da3c3a5d26892a5f763e754697ef36a12416bb27..c4b3fbd1a80f667f20f56eb90f6b736ccce00ad2 100644 (file)
@@ -192,7 +192,7 @@ config SERIO_RAW
 
 config SERIO_XILINX_XPS_PS2
        tristate "Xilinx XPS PS/2 Controller Support"
-       depends on PPC
+       depends on PPC || MICROBLAZE
        help
          This driver supports XPS PS/2 IP from the Xilinx EDK on
          PowerPC platform.
index a3d3cbab359a8b1ae9bea16ecf59b2caef0f3212..0aaa0597a6223779e758d45875af4fb17cc3956c 100644 (file)
@@ -1,6 +1,6 @@
 config LGUEST
        tristate "Linux hypervisor example code"
-       depends on X86_32 && EXPERIMENTAL && !X86_PAE && FUTEX
+       depends on X86_32 && EXPERIMENTAL && EVENTFD
        select HVC_DRIVER
        ---help---
          This is a very simple module which allows you to run
index 4845fb3cf74bd8911dc3a48b8370c722be688512..a6974e9b8ebff8c8676f297da0e32a1e3e6d0105 100644 (file)
@@ -95,7 +95,7 @@ static __init int map_switcher(void)
         * array of struct pages.  It increments that pointer, but we don't
         * care. */
        pagep = switcher_page;
-       err = map_vm_area(switcher_vma, PAGE_KERNEL, &pagep);
+       err = map_vm_area(switcher_vma, PAGE_KERNEL_EXEC, &pagep);
        if (err) {
                printk("lguest: map_vm_area failed: %i\n", err);
                goto free_vma;
@@ -188,6 +188,9 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user)
 {
        /* We stop running once the Guest is dead. */
        while (!cpu->lg->dead) {
+               unsigned int irq;
+               bool more;
+
                /* First we run any hypercalls the Guest wants done. */
                if (cpu->hcall)
                        do_hypercalls(cpu);
@@ -195,23 +198,23 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user)
                /* It's possible the Guest did a NOTIFY hypercall to the
                 * Launcher, in which case we return from the read() now. */
                if (cpu->pending_notify) {
-                       if (put_user(cpu->pending_notify, user))
-                               return -EFAULT;
-                       return sizeof(cpu->pending_notify);
+                       if (!send_notify_to_eventfd(cpu)) {
+                               if (put_user(cpu->pending_notify, user))
+                                       return -EFAULT;
+                               return sizeof(cpu->pending_notify);
+                       }
                }
 
                /* Check for signals */
                if (signal_pending(current))
                        return -ERESTARTSYS;
 
-               /* If Waker set break_out, return to Launcher. */
-               if (cpu->break_out)
-                       return -EAGAIN;
-
                /* Check if there are any interrupts which can be delivered now:
                 * if so, this sets up the hander to be executed when we next
                 * run the Guest. */
-               maybe_do_interrupt(cpu);
+               irq = interrupt_pending(cpu, &more);
+               if (irq < LGUEST_IRQS)
+                       try_deliver_interrupt(cpu, irq, more);
 
                /* All long-lived kernel loops need to check with this horrible
                 * thing called the freezer.  If the Host is trying to suspend,
@@ -224,10 +227,15 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user)
                        break;
 
                /* If the Guest asked to be stopped, we sleep.  The Guest's
-                * clock timer or LHREQ_BREAK from the Waker will wake us. */
+                * clock timer will wake us. */
                if (cpu->halted) {
                        set_current_state(TASK_INTERRUPTIBLE);
-                       schedule();
+                       /* Just before we sleep, make sure no interrupt snuck in
+                        * which we should be doing. */
+                       if (interrupt_pending(cpu, &more) < LGUEST_IRQS)
+                               set_current_state(TASK_RUNNING);
+                       else
+                               schedule();
                        continue;
                }
 
index 54d66f05fefa0a82af7c96a78432cf2a763d6530..c29ffa19cb74410e5b37ddc74bf19d4a45c5d9e7 100644 (file)
@@ -37,6 +37,10 @@ static void do_hcall(struct lg_cpu *cpu, struct hcall_args *args)
                /* This call does nothing, except by breaking out of the Guest
                 * it makes us process all the asynchronous hypercalls. */
                break;
+       case LHCALL_SEND_INTERRUPTS:
+               /* This call does nothing too, but by breaking out of the Guest
+                * it makes us process any pending interrupts. */
+               break;
        case LHCALL_LGUEST_INIT:
                /* You can't get here unless you're already initialized.  Don't
                 * do that. */
@@ -73,11 +77,21 @@ static void do_hcall(struct lg_cpu *cpu, struct hcall_args *args)
                guest_set_stack(cpu, args->arg1, args->arg2, args->arg3);
                break;
        case LHCALL_SET_PTE:
+#ifdef CONFIG_X86_PAE
+               guest_set_pte(cpu, args->arg1, args->arg2,
+                               __pte(args->arg3 | (u64)args->arg4 << 32));
+#else
                guest_set_pte(cpu, args->arg1, args->arg2, __pte(args->arg3));
+#endif
+               break;
+       case LHCALL_SET_PGD:
+               guest_set_pgd(cpu->lg, args->arg1, args->arg2);
                break;
+#ifdef CONFIG_X86_PAE
        case LHCALL_SET_PMD:
                guest_set_pmd(cpu->lg, args->arg1, args->arg2);
                break;
+#endif
        case LHCALL_SET_CLOCKEVENT:
                guest_set_clockevent(cpu, args->arg1);
                break;
index 6e99adbe1946e8810951460872fdd59d79985ebf..0e9067b0d5072194d3ee1d496335cd8222b4109d 100644 (file)
@@ -128,30 +128,39 @@ static void set_guest_interrupt(struct lg_cpu *cpu, u32 lo, u32 hi,
 /*H:205
  * Virtual Interrupts.
  *
- * maybe_do_interrupt() gets called before every entry to the Guest, to see if
- * we should divert the Guest to running an interrupt handler. */
-void maybe_do_interrupt(struct lg_cpu *cpu)
+ * interrupt_pending() returns the first pending interrupt which isn't blocked
+ * by the Guest.  It is called before every entry to the Guest, and just before
+ * we go to sleep when the Guest has halted itself. */
+unsigned int interrupt_pending(struct lg_cpu *cpu, bool *more)
 {
        unsigned int irq;
        DECLARE_BITMAP(blk, LGUEST_IRQS);
-       struct desc_struct *idt;
 
        /* If the Guest hasn't even initialized yet, we can do nothing. */
        if (!cpu->lg->lguest_data)
-               return;
+               return LGUEST_IRQS;
 
        /* Take our "irqs_pending" array and remove any interrupts the Guest
         * wants blocked: the result ends up in "blk". */
        if (copy_from_user(&blk, cpu->lg->lguest_data->blocked_interrupts,
                           sizeof(blk)))
-               return;
+               return LGUEST_IRQS;
        bitmap_andnot(blk, cpu->irqs_pending, blk, LGUEST_IRQS);
 
        /* Find the first interrupt. */
        irq = find_first_bit(blk, LGUEST_IRQS);
-       /* None?  Nothing to do */
-       if (irq >= LGUEST_IRQS)
-               return;
+       *more = find_next_bit(blk, LGUEST_IRQS, irq+1);
+
+       return irq;
+}
+
+/* This actually diverts the Guest to running an interrupt handler, once an
+ * interrupt has been identified by interrupt_pending(). */
+void try_deliver_interrupt(struct lg_cpu *cpu, unsigned int irq, bool more)
+{
+       struct desc_struct *idt;
+
+       BUG_ON(irq >= LGUEST_IRQS);
 
        /* They may be in the middle of an iret, where they asked us never to
         * deliver interrupts. */
@@ -170,8 +179,12 @@ void maybe_do_interrupt(struct lg_cpu *cpu)
                u32 irq_enabled;
                if (get_user(irq_enabled, &cpu->lg->lguest_data->irq_enabled))
                        irq_enabled = 0;
-               if (!irq_enabled)
+               if (!irq_enabled) {
+                       /* Make sure they know an IRQ is pending. */
+                       put_user(X86_EFLAGS_IF,
+                                &cpu->lg->lguest_data->irq_pending);
                        return;
+               }
        }
 
        /* Look at the IDT entry the Guest gave us for this interrupt.  The
@@ -194,6 +207,25 @@ void maybe_do_interrupt(struct lg_cpu *cpu)
         * here is a compromise which means at least it gets updated every
         * timer interrupt. */
        write_timestamp(cpu);
+
+       /* If there are no other interrupts we want to deliver, clear
+        * the pending flag. */
+       if (!more)
+               put_user(0, &cpu->lg->lguest_data->irq_pending);
+}
+
+/* And this is the routine when we want to set an interrupt for the Guest. */
+void set_interrupt(struct lg_cpu *cpu, unsigned int irq)
+{
+       /* Next time the Guest runs, the core code will see if it can deliver
+        * this interrupt. */
+       set_bit(irq, cpu->irqs_pending);
+
+       /* Make sure it sees it; it might be asleep (eg. halted), or
+        * running the Guest right now, in which case kick_process()
+        * will knock it out. */
+       if (!wake_up_process(cpu->tsk))
+               kick_process(cpu->tsk);
 }
 /*:*/
 
@@ -510,10 +542,7 @@ static enum hrtimer_restart clockdev_fn(struct hrtimer *timer)
        struct lg_cpu *cpu = container_of(timer, struct lg_cpu, hrt);
 
        /* Remember the first interrupt is the timer interrupt. */
-       set_bit(0, cpu->irqs_pending);
-       /* If the Guest is actually stopped, we need to wake it up. */
-       if (cpu->halted)
-               wake_up_process(cpu->tsk);
+       set_interrupt(cpu, 0);
        return HRTIMER_NORESTART;
 }
 
index af92a176697f379f1f8c345ceb1db061e3ac73b2..d4e8979735cb002f9f97cf929a1b45a900f2ddc6 100644 (file)
@@ -49,7 +49,7 @@ struct lg_cpu {
        u32 cr2;
        int ts;
        u32 esp1;
-       u8 ss1;
+       u16 ss1;
 
        /* Bitmap of what has changed: see CHANGED_* above. */
        int changed;
@@ -71,9 +71,7 @@ struct lg_cpu {
        /* Virtual clock device */
        struct hrtimer hrt;
 
-       /* Do we need to stop what we're doing and return to userspace? */
-       int break_out;
-       wait_queue_head_t break_wq;
+       /* Did the Guest tell us to halt? */
        int halted;
 
        /* Pending virtual interrupts */
@@ -82,6 +80,16 @@ struct lg_cpu {
        struct lg_cpu_arch arch;
 };
 
+struct lg_eventfd {
+       unsigned long addr;
+       struct file *event;
+};
+
+struct lg_eventfd_map {
+       unsigned int num;
+       struct lg_eventfd map[];
+};
+
 /* The private info the thread maintains about the guest. */
 struct lguest
 {
@@ -102,6 +110,8 @@ struct lguest
        unsigned int stack_pages;
        u32 tsc_khz;
 
+       struct lg_eventfd_map *eventfds;
+
        /* Dead? */
        const char *dead;
 };
@@ -137,9 +147,13 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user);
  * in the kernel. */
 #define pgd_flags(x)   (pgd_val(x) & ~PAGE_MASK)
 #define pgd_pfn(x)     (pgd_val(x) >> PAGE_SHIFT)
+#define pmd_flags(x)    (pmd_val(x) & ~PAGE_MASK)
+#define pmd_pfn(x)     (pmd_val(x) >> PAGE_SHIFT)
 
 /* interrupts_and_traps.c: */
-void maybe_do_interrupt(struct lg_cpu *cpu);
+unsigned int interrupt_pending(struct lg_cpu *cpu, bool *more);
+void try_deliver_interrupt(struct lg_cpu *cpu, unsigned int irq, bool more);
+void set_interrupt(struct lg_cpu *cpu, unsigned int irq);
 bool deliver_trap(struct lg_cpu *cpu, unsigned int num);
 void load_guest_idt_entry(struct lg_cpu *cpu, unsigned int i,
                          u32 low, u32 hi);
@@ -150,6 +164,7 @@ void setup_default_idt_entries(struct lguest_ro_state *state,
 void copy_traps(const struct lg_cpu *cpu, struct desc_struct *idt,
                const unsigned long *def);
 void guest_set_clockevent(struct lg_cpu *cpu, unsigned long delta);
+bool send_notify_to_eventfd(struct lg_cpu *cpu);
 void init_clockdev(struct lg_cpu *cpu);
 bool check_syscall_vector(struct lguest *lg);
 int init_interrupts(void);
@@ -168,7 +183,10 @@ void copy_gdt_tls(const struct lg_cpu *cpu, struct desc_struct *gdt);
 int init_guest_pagetable(struct lguest *lg);
 void free_guest_pagetable(struct lguest *lg);
 void guest_new_pagetable(struct lg_cpu *cpu, unsigned long pgtable);
+void guest_set_pgd(struct lguest *lg, unsigned long gpgdir, u32 i);
+#ifdef CONFIG_X86_PAE
 void guest_set_pmd(struct lguest *lg, unsigned long gpgdir, u32 i);
+#endif
 void guest_pagetable_clear_all(struct lg_cpu *cpu);
 void guest_pagetable_flush_user(struct lg_cpu *cpu);
 void guest_set_pte(struct lg_cpu *cpu, unsigned long gpgdir,
index df44d962626d327ba26a3b1a2aa83a2bba383390..e082cdac88b4bb283f41ebb611671d583ca78ecb 100644 (file)
@@ -228,7 +228,8 @@ extern void lguest_setup_irq(unsigned int irq);
  * function. */
 static struct virtqueue *lg_find_vq(struct virtio_device *vdev,
                                    unsigned index,
-                                   void (*callback)(struct virtqueue *vq))
+                                   void (*callback)(struct virtqueue *vq),
+                                   const char *name)
 {
        struct lguest_device *ldev = to_lgdev(vdev);
        struct lguest_vq_info *lvq;
@@ -263,7 +264,7 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev,
        /* OK, tell virtio_ring.c to set up a virtqueue now we know its size
         * and we've got a pointer to its pages. */
        vq = vring_new_virtqueue(lvq->config.num, LGUEST_VRING_ALIGN,
-                                vdev, lvq->pages, lg_notify, callback);
+                                vdev, lvq->pages, lg_notify, callback, name);
        if (!vq) {
                err = -ENOMEM;
                goto unmap;
@@ -312,6 +313,38 @@ static void lg_del_vq(struct virtqueue *vq)
        kfree(lvq);
 }
 
+static void lg_del_vqs(struct virtio_device *vdev)
+{
+       struct virtqueue *vq, *n;
+
+       list_for_each_entry_safe(vq, n, &vdev->vqs, list)
+               lg_del_vq(vq);
+}
+
+static int lg_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+                      struct virtqueue *vqs[],
+                      vq_callback_t *callbacks[],
+                      const char *names[])
+{
+       struct lguest_device *ldev = to_lgdev(vdev);
+       int i;
+
+       /* We must have this many virtqueues. */
+       if (nvqs > ldev->desc->num_vq)
+               return -ENOENT;
+
+       for (i = 0; i < nvqs; ++i) {
+               vqs[i] = lg_find_vq(vdev, i, callbacks[i], names[i]);
+               if (IS_ERR(vqs[i]))
+                       goto error;
+       }
+       return 0;
+
+error:
+       lg_del_vqs(vdev);
+       return PTR_ERR(vqs[i]);
+}
+
 /* The ops structure which hooks everything together. */
 static struct virtio_config_ops lguest_config_ops = {
        .get_features = lg_get_features,
@@ -321,8 +354,8 @@ static struct virtio_config_ops lguest_config_ops = {
        .get_status = lg_get_status,
        .set_status = lg_set_status,
        .reset = lg_reset,
-       .find_vq = lg_find_vq,
-       .del_vq = lg_del_vq,
+       .find_vqs = lg_find_vqs,
+       .del_vqs = lg_del_vqs,
 };
 
 /* The root device for the lguest virtio devices.  This makes them appear as
index b8ee103eed5f503e44ad5ebb27aab75e81ba5bec..32e297121058a3d7dc52fbd6d347eab496c08252 100644 (file)
@@ -7,32 +7,83 @@
 #include <linux/miscdevice.h>
 #include <linux/fs.h>
 #include <linux/sched.h>
+#include <linux/eventfd.h>
+#include <linux/file.h>
 #include "lg.h"
 
-/*L:055 When something happens, the Waker process needs a way to stop the
- * kernel running the Guest and return to the Launcher.  So the Waker writes
- * LHREQ_BREAK and the value "1" to /dev/lguest to do this.  Once the Launcher
- * has done whatever needs attention, it writes LHREQ_BREAK and "0" to release
- * the Waker. */
-static int break_guest_out(struct lg_cpu *cpu, const unsigned long __user*input)
+bool send_notify_to_eventfd(struct lg_cpu *cpu)
 {
-       unsigned long on;
+       unsigned int i;
+       struct lg_eventfd_map *map;
+
+       /* lg->eventfds is RCU-protected */
+       rcu_read_lock();
+       map = rcu_dereference(cpu->lg->eventfds);
+       for (i = 0; i < map->num; i++) {
+               if (map->map[i].addr == cpu->pending_notify) {
+                       eventfd_signal(map->map[i].event, 1);
+                       cpu->pending_notify = 0;
+                       break;
+               }
+       }
+       rcu_read_unlock();
+       return cpu->pending_notify == 0;
+}
 
-       /* Fetch whether they're turning break on or off. */
-       if (get_user(on, input) != 0)
-               return -EFAULT;
+static int add_eventfd(struct lguest *lg, unsigned long addr, int fd)
+{
+       struct lg_eventfd_map *new, *old = lg->eventfds;
 
-       if (on) {
-               cpu->break_out = 1;
-               /* Pop it out of the Guest (may be running on different CPU) */
-               wake_up_process(cpu->tsk);
-               /* Wait for them to reset it */
-               return wait_event_interruptible(cpu->break_wq, !cpu->break_out);
-       } else {
-               cpu->break_out = 0;
-               wake_up(&cpu->break_wq);
-               return 0;
+       if (!addr)
+               return -EINVAL;
+
+       /* Replace the old array with the new one, carefully: others can
+        * be accessing it at the same time */
+       new = kmalloc(sizeof(*new) + sizeof(new->map[0]) * (old->num + 1),
+                     GFP_KERNEL);
+       if (!new)
+               return -ENOMEM;
+
+       /* First make identical copy. */
+       memcpy(new->map, old->map, sizeof(old->map[0]) * old->num);
+       new->num = old->num;
+
+       /* Now append new entry. */
+       new->map[new->num].addr = addr;
+       new->map[new->num].event = eventfd_fget(fd);
+       if (IS_ERR(new->map[new->num].event)) {
+               kfree(new);
+               return PTR_ERR(new->map[new->num].event);
        }
+       new->num++;
+
+       /* Now put new one in place. */
+       rcu_assign_pointer(lg->eventfds, new);
+
+       /* We're not in a big hurry.  Wait until noone's looking at old
+        * version, then delete it. */
+       synchronize_rcu();
+       kfree(old);
+
+       return 0;
+}
+
+static int attach_eventfd(struct lguest *lg, const unsigned long __user *input)
+{
+       unsigned long addr, fd;
+       int err;
+
+       if (get_user(addr, input) != 0)
+               return -EFAULT;
+       input++;
+       if (get_user(fd, input) != 0)
+               return -EFAULT;
+
+       mutex_lock(&lguest_lock);
+       err = add_eventfd(lg, addr, fd);
+       mutex_unlock(&lguest_lock);
+
+       return 0;
 }
 
 /*L:050 Sending an interrupt is done by writing LHREQ_IRQ and an interrupt
@@ -45,9 +96,8 @@ static int user_send_irq(struct lg_cpu *cpu, const unsigned long __user *input)
                return -EFAULT;
        if (irq >= LGUEST_IRQS)
                return -EINVAL;
-       /* Next time the Guest runs, the core code will see if it can deliver
-        * this interrupt. */
-       set_bit(irq, cpu->irqs_pending);
+
+       set_interrupt(cpu, irq);
        return 0;
 }
 
@@ -126,9 +176,6 @@ static int lg_cpu_start(struct lg_cpu *cpu, unsigned id, unsigned long start_ip)
         * address. */
        lguest_arch_setup_regs(cpu, start_ip);
 
-       /* Initialize the queue for the Waker to wait on */
-       init_waitqueue_head(&cpu->break_wq);
-
        /* We keep a pointer to the Launcher task (ie. current task) for when
         * other Guests want to wake this one (eg. console input). */
        cpu->tsk = current;
@@ -185,6 +232,13 @@ static int initialize(struct file *file, const unsigned long __user *input)
                goto unlock;
        }
 
+       lg->eventfds = kmalloc(sizeof(*lg->eventfds), GFP_KERNEL);
+       if (!lg->eventfds) {
+               err = -ENOMEM;
+               goto free_lg;
+       }
+       lg->eventfds->num = 0;
+
        /* Populate the easy fields of our "struct lguest" */
        lg->mem_base = (void __user *)args[0];
        lg->pfn_limit = args[1];
@@ -192,7 +246,7 @@ static int initialize(struct file *file, const unsigned long __user *input)
        /* This is the first cpu (cpu 0) and it will start booting at args[2] */
        err = lg_cpu_start(&lg->cpus[0], 0, args[2]);
        if (err)
-               goto release_guest;
+               goto free_eventfds;
 
        /* Initialize the Guest's shadow page tables, using the toplevel
         * address the Launcher gave us.  This allocates memory, so can fail. */
@@ -211,7 +265,9 @@ static int initialize(struct file *file, const unsigned long __user *input)
 free_regs:
        /* FIXME: This should be in free_vcpu */
        free_page(lg->cpus[0].regs_page);
-release_guest:
+free_eventfds:
+       kfree(lg->eventfds);
+free_lg:
        kfree(lg);
 unlock:
        mutex_unlock(&lguest_lock);
@@ -252,11 +308,6 @@ static ssize_t write(struct file *file, const char __user *in,
                /* Once the Guest is dead, you can only read() why it died. */
                if (lg->dead)
                        return -ENOENT;
-
-               /* If you're not the task which owns the Guest, all you can do
-                * is break the Launcher out of running the Guest. */
-               if (current != cpu->tsk && req != LHREQ_BREAK)
-                       return -EPERM;
        }
 
        switch (req) {
@@ -264,8 +315,8 @@ static ssize_t write(struct file *file, const char __user *in,
                return initialize(file, input);
        case LHREQ_IRQ:
                return user_send_irq(cpu, input);
-       case LHREQ_BREAK:
-               return break_guest_out(cpu, input);
+       case LHREQ_EVENTFD:
+               return attach_eventfd(lg, input);
        default:
                return -EINVAL;
        }
@@ -303,6 +354,12 @@ static int close(struct inode *inode, struct file *file)
                 * the Launcher's memory management structure. */
                mmput(lg->cpus[i].mm);
        }
+
+       /* Release any eventfds they registered. */
+       for (i = 0; i < lg->eventfds->num; i++)
+               fput(lg->eventfds->map[i].event);
+       kfree(lg->eventfds);
+
        /* If lg->dead doesn't contain an error code it will be NULL or a
         * kmalloc()ed string, either of which is ok to hand to kfree(). */
        if (!IS_ERR(lg->dead))
index a059cf9980f711b97d2009545b574e63cf79fddc..a6fe1abda24002d623d74a7914f23b55ad708b4e 100644 (file)
  * page.  */
 #define SWITCHER_PGD_INDEX (PTRS_PER_PGD - 1)
 
+/* For PAE we need the PMD index as well. We use the last 2MB, so we
+ * will need the last pmd entry of the last pmd page.  */
+#ifdef CONFIG_X86_PAE
+#define SWITCHER_PMD_INDEX     (PTRS_PER_PMD - 1)
+#define RESERVE_MEM            2U
+#define CHECK_GPGD_MASK                _PAGE_PRESENT
+#else
+#define RESERVE_MEM            4U
+#define CHECK_GPGD_MASK                _PAGE_TABLE
+#endif
+
 /* We actually need a separate PTE page for each CPU.  Remember that after the
  * Switcher code itself comes two pages for each CPU, and we don't want this
  * CPU's guest to see the pages of any other CPU. */
@@ -73,24 +84,59 @@ static pgd_t *spgd_addr(struct lg_cpu *cpu, u32 i, unsigned long vaddr)
 {
        unsigned int index = pgd_index(vaddr);
 
+#ifndef CONFIG_X86_PAE
        /* We kill any Guest trying to touch the Switcher addresses. */
        if (index >= SWITCHER_PGD_INDEX) {
                kill_guest(cpu, "attempt to access switcher pages");
                index = 0;
        }
+#endif
        /* Return a pointer index'th pgd entry for the i'th page table. */
        return &cpu->lg->pgdirs[i].pgdir[index];
 }
 
+#ifdef CONFIG_X86_PAE
+/* This routine then takes the PGD entry given above, which contains the
+ * address of the PMD page.  It then returns a pointer to the PMD entry for the
+ * given address. */
+static pmd_t *spmd_addr(struct lg_cpu *cpu, pgd_t spgd, unsigned long vaddr)
+{
+       unsigned int index = pmd_index(vaddr);
+       pmd_t *page;
+
+       /* We kill any Guest trying to touch the Switcher addresses. */
+       if (pgd_index(vaddr) == SWITCHER_PGD_INDEX &&
+                                       index >= SWITCHER_PMD_INDEX) {
+               kill_guest(cpu, "attempt to access switcher pages");
+               index = 0;
+       }
+
+       /* You should never call this if the PGD entry wasn't valid */
+       BUG_ON(!(pgd_flags(spgd) & _PAGE_PRESENT));
+       page = __va(pgd_pfn(spgd) << PAGE_SHIFT);
+
+       return &page[index];
+}
+#endif
+
 /* This routine then takes the page directory entry returned above, which
  * contains the address of the page table entry (PTE) page.  It then returns a
  * pointer to the PTE entry for the given address. */
-static pte_t *spte_addr(pgd_t spgd, unsigned long vaddr)
+static pte_t *spte_addr(struct lg_cpu *cpu, pgd_t spgd, unsigned long vaddr)
 {
+#ifdef CONFIG_X86_PAE
+       pmd_t *pmd = spmd_addr(cpu, spgd, vaddr);
+       pte_t *page = __va(pmd_pfn(*pmd) << PAGE_SHIFT);
+
+       /* You should never call this if the PMD entry wasn't valid */
+       BUG_ON(!(pmd_flags(*pmd) & _PAGE_PRESENT));
+#else
        pte_t *page = __va(pgd_pfn(spgd) << PAGE_SHIFT);
        /* You should never call this if the PGD entry wasn't valid */
        BUG_ON(!(pgd_flags(spgd) & _PAGE_PRESENT));
-       return &page[(vaddr >> PAGE_SHIFT) % PTRS_PER_PTE];
+#endif
+
+       return &page[pte_index(vaddr)];
 }
 
 /* These two functions just like the above two, except they access the Guest
@@ -101,12 +147,32 @@ static unsigned long gpgd_addr(struct lg_cpu *cpu, unsigned long vaddr)
        return cpu->lg->pgdirs[cpu->cpu_pgd].gpgdir + index * sizeof(pgd_t);
 }
 
-static unsigned long gpte_addr(pgd_t gpgd, unsigned long vaddr)
+#ifdef CONFIG_X86_PAE
+static unsigned long gpmd_addr(pgd_t gpgd, unsigned long vaddr)
+{
+       unsigned long gpage = pgd_pfn(gpgd) << PAGE_SHIFT;
+       BUG_ON(!(pgd_flags(gpgd) & _PAGE_PRESENT));
+       return gpage + pmd_index(vaddr) * sizeof(pmd_t);
+}
+
+static unsigned long gpte_addr(struct lg_cpu *cpu,
+                              pmd_t gpmd, unsigned long vaddr)
+{
+       unsigned long gpage = pmd_pfn(gpmd) << PAGE_SHIFT;
+
+       BUG_ON(!(pmd_flags(gpmd) & _PAGE_PRESENT));
+       return gpage + pte_index(vaddr) * sizeof(pte_t);
+}
+#else
+static unsigned long gpte_addr(struct lg_cpu *cpu,
+                               pgd_t gpgd, unsigned long vaddr)
 {
        unsigned long gpage = pgd_pfn(gpgd) << PAGE_SHIFT;
+
        BUG_ON(!(pgd_flags(gpgd) & _PAGE_PRESENT));
-       return gpage + ((vaddr>>PAGE_SHIFT) % PTRS_PER_PTE) * sizeof(pte_t);
+       return gpage + pte_index(vaddr) * sizeof(pte_t);
 }
+#endif
 /*:*/
 
 /*M:014 get_pfn is slow: we could probably try to grab batches of pages here as
@@ -171,7 +237,7 @@ static void release_pte(pte_t pte)
        /* Remember that get_user_pages_fast() took a reference to the page, in
         * get_pfn()?  We have to put it back now. */
        if (pte_flags(pte) & _PAGE_PRESENT)
-               put_page(pfn_to_page(pte_pfn(pte)));
+               put_page(pte_page(pte));
 }
 /*:*/
 
@@ -184,11 +250,20 @@ static void check_gpte(struct lg_cpu *cpu, pte_t gpte)
 
 static void check_gpgd(struct lg_cpu *cpu, pgd_t gpgd)
 {
-       if ((pgd_flags(gpgd) & ~_PAGE_TABLE) ||
+       if ((pgd_flags(gpgd) & ~CHECK_GPGD_MASK) ||
           (pgd_pfn(gpgd) >= cpu->lg->pfn_limit))
                kill_guest(cpu, "bad page directory entry");
 }
 
+#ifdef CONFIG_X86_PAE
+static void check_gpmd(struct lg_cpu *cpu, pmd_t gpmd)
+{
+       if ((pmd_flags(gpmd) & ~_PAGE_TABLE) ||
+          (pmd_pfn(gpmd) >= cpu->lg->pfn_limit))
+               kill_guest(cpu, "bad page middle directory entry");
+}
+#endif
+
 /*H:330
  * (i) Looking up a page table entry when the Guest faults.
  *
@@ -207,6 +282,11 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode)
        pte_t gpte;
        pte_t *spte;
 
+#ifdef CONFIG_X86_PAE
+       pmd_t *spmd;
+       pmd_t gpmd;
+#endif
+
        /* First step: get the top-level Guest page table entry. */
        gpgd = lgread(cpu, gpgd_addr(cpu, vaddr), pgd_t);
        /* Toplevel not present?  We can't map it in. */
@@ -228,12 +308,45 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode)
                check_gpgd(cpu, gpgd);
                /* And we copy the flags to the shadow PGD entry.  The page
                 * number in the shadow PGD is the page we just allocated. */
-               *spgd = __pgd(__pa(ptepage) | pgd_flags(gpgd));
+               set_pgd(spgd, __pgd(__pa(ptepage) | pgd_flags(gpgd)));
        }
 
+#ifdef CONFIG_X86_PAE
+       gpmd = lgread(cpu, gpmd_addr(gpgd, vaddr), pmd_t);
+       /* middle level not present?  We can't map it in. */
+       if (!(pmd_flags(gpmd) & _PAGE_PRESENT))
+               return false;
+
+       /* Now look at the matching shadow entry. */
+       spmd = spmd_addr(cpu, *spgd, vaddr);
+
+       if (!(pmd_flags(*spmd) & _PAGE_PRESENT)) {
+               /* No shadow entry: allocate a new shadow PTE page. */
+               unsigned long ptepage = get_zeroed_page(GFP_KERNEL);
+
+               /* This is not really the Guest's fault, but killing it is
+               * simple for this corner case. */
+               if (!ptepage) {
+                       kill_guest(cpu, "out of memory allocating pte page");
+                       return false;
+               }
+
+               /* We check that the Guest pmd is OK. */
+               check_gpmd(cpu, gpmd);
+
+               /* And we copy the flags to the shadow PMD entry.  The page
+                * number in the shadow PMD is the page we just allocated. */
+               native_set_pmd(spmd, __pmd(__pa(ptepage) | pmd_flags(gpmd)));
+       }
+
+       /* OK, now we look at the lower level in the Guest page table: keep its
+        * address, because we might update it later. */
+       gpte_ptr = gpte_addr(cpu, gpmd, vaddr);
+#else
        /* OK, now we look at the lower level in the Guest page table: keep its
         * address, because we might update it later. */
-       gpte_ptr = gpte_addr(gpgd, vaddr);
+       gpte_ptr = gpte_addr(cpu, gpgd, vaddr);
+#endif
        gpte = lgread(cpu, gpte_ptr, pte_t);
 
        /* If this page isn't in the Guest page tables, we can't page it in. */
@@ -259,7 +372,7 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode)
                gpte = pte_mkdirty(gpte);
 
        /* Get the pointer to the shadow PTE entry we're going to set. */
-       spte = spte_addr(*spgd, vaddr);
+       spte = spte_addr(cpu, *spgd, vaddr);
        /* If there was a valid shadow PTE entry here before, we release it.
         * This can happen with a write to a previously read-only entry. */
        release_pte(*spte);
@@ -273,7 +386,7 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode)
                 * table entry, even if the Guest says it's writable.  That way
                 * we will come back here when a write does actually occur, so
                 * we can update the Guest's _PAGE_DIRTY flag. */
-               *spte = gpte_to_spte(cpu, pte_wrprotect(gpte), 0);
+               native_set_pte(spte, gpte_to_spte(cpu, pte_wrprotect(gpte), 0));
 
        /* Finally, we write the Guest PTE entry back: we've set the
         * _PAGE_ACCESSED and maybe the _PAGE_DIRTY flags. */
@@ -301,14 +414,23 @@ static bool page_writable(struct lg_cpu *cpu, unsigned long vaddr)
        pgd_t *spgd;
        unsigned long flags;
 
+#ifdef CONFIG_X86_PAE
+       pmd_t *spmd;
+#endif
        /* Look at the current top level entry: is it present? */
        spgd = spgd_addr(cpu, cpu->cpu_pgd, vaddr);
        if (!(pgd_flags(*spgd) & _PAGE_PRESENT))
                return false;
 
+#ifdef CONFIG_X86_PAE
+       spmd = spmd_addr(cpu, *spgd, vaddr);
+       if (!(pmd_flags(*spmd) & _PAGE_PRESENT))
+               return false;
+#endif
+
        /* Check the flags on the pte entry itself: it must be present and
         * writable. */
-       flags = pte_flags(*(spte_addr(*spgd, vaddr)));
+       flags = pte_flags(*(spte_addr(cpu, *spgd, vaddr)));
 
        return (flags & (_PAGE_PRESENT|_PAGE_RW)) == (_PAGE_PRESENT|_PAGE_RW);
 }
@@ -322,8 +444,43 @@ void pin_page(struct lg_cpu *cpu, unsigned long vaddr)
                kill_guest(cpu, "bad stack page %#lx", vaddr);
 }
 
+#ifdef CONFIG_X86_PAE
+static void release_pmd(pmd_t *spmd)
+{
+       /* If the entry's not present, there's nothing to release. */
+       if (pmd_flags(*spmd) & _PAGE_PRESENT) {
+               unsigned int i;
+               pte_t *ptepage = __va(pmd_pfn(*spmd) << PAGE_SHIFT);
+               /* For each entry in the page, we might need to release it. */
+               for (i = 0; i < PTRS_PER_PTE; i++)
+                       release_pte(ptepage[i]);
+               /* Now we can free the page of PTEs */
+               free_page((long)ptepage);
+               /* And zero out the PMD entry so we never release it twice. */
+               native_set_pmd(spmd, __pmd(0));
+       }
+}
+
+static void release_pgd(pgd_t *spgd)
+{
+       /* If the entry's not present, there's nothing to release. */
+       if (pgd_flags(*spgd) & _PAGE_PRESENT) {
+               unsigned int i;
+               pmd_t *pmdpage = __va(pgd_pfn(*spgd) << PAGE_SHIFT);
+
+               for (i = 0; i < PTRS_PER_PMD; i++)
+                       release_pmd(&pmdpage[i]);
+
+               /* Now we can free the page of PMDs */
+               free_page((long)pmdpage);
+               /* And zero out the PGD entry so we never release it twice. */
+               set_pgd(spgd, __pgd(0));
+       }
+}
+
+#else /* !CONFIG_X86_PAE */
 /*H:450 If we chase down the release_pgd() code, it looks like this: */
-static void release_pgd(struct lguest *lg, pgd_t *spgd)
+static void release_pgd(pgd_t *spgd)
 {
        /* If the entry's not present, there's nothing to release. */
        if (pgd_flags(*spgd) & _PAGE_PRESENT) {
@@ -341,7 +498,7 @@ static void release_pgd(struct lguest *lg, pgd_t *spgd)
                *spgd = __pgd(0);
        }
 }
-
+#endif
 /*H:445 We saw flush_user_mappings() twice: once from the flush_user_mappings()
  * hypercall and once in new_pgdir() when we re-used a top-level pgdir page.
  * It simply releases every PTE page from 0 up to the Guest's kernel address. */
@@ -350,7 +507,7 @@ static void flush_user_mappings(struct lguest *lg, int idx)
        unsigned int i;
        /* Release every pgd entry up to the kernel's address. */
        for (i = 0; i < pgd_index(lg->kernel_address); i++)
-               release_pgd(lg, lg->pgdirs[idx].pgdir + i);
+               release_pgd(lg->pgdirs[idx].pgdir + i);
 }
 
 /*H:440 (v) Flushing (throwing away) page tables,
@@ -369,7 +526,9 @@ unsigned long guest_pa(struct lg_cpu *cpu, unsigned long vaddr)
 {
        pgd_t gpgd;
        pte_t gpte;
-
+#ifdef CONFIG_X86_PAE
+       pmd_t gpmd;
+#endif
        /* First step: get the top-level Guest page table entry. */
        gpgd = lgread(cpu, gpgd_addr(cpu, vaddr), pgd_t);
        /* Toplevel not present?  We can't map it in. */
@@ -378,7 +537,14 @@ unsigned long guest_pa(struct lg_cpu *cpu, unsigned long vaddr)
                return -1UL;
        }
 
-       gpte = lgread(cpu, gpte_addr(gpgd, vaddr), pte_t);
+#ifdef CONFIG_X86_PAE
+       gpmd = lgread(cpu, gpmd_addr(gpgd, vaddr), pmd_t);
+       if (!(pmd_flags(gpmd) & _PAGE_PRESENT))
+               kill_guest(cpu, "Bad address %#lx", vaddr);
+       gpte = lgread(cpu, gpte_addr(cpu, gpmd, vaddr), pte_t);
+#else
+       gpte = lgread(cpu, gpte_addr(cpu, gpgd, vaddr), pte_t);
+#endif
        if (!(pte_flags(gpte) & _PAGE_PRESENT))
                kill_guest(cpu, "Bad address %#lx", vaddr);
 
@@ -405,6 +571,9 @@ static unsigned int new_pgdir(struct lg_cpu *cpu,
                              int *blank_pgdir)
 {
        unsigned int next;
+#ifdef CONFIG_X86_PAE
+       pmd_t *pmd_table;
+#endif
 
        /* We pick one entry at random to throw out.  Choosing the Least
         * Recently Used might be better, but this is easy. */
@@ -416,10 +585,27 @@ static unsigned int new_pgdir(struct lg_cpu *cpu,
                /* If the allocation fails, just keep using the one we have */
                if (!cpu->lg->pgdirs[next].pgdir)
                        next = cpu->cpu_pgd;
-               else
-                       /* This is a blank page, so there are no kernel
-                        * mappings: caller must map the stack! */
+               else {
+#ifdef CONFIG_X86_PAE
+                       /* In PAE mode, allocate a pmd page and populate the
+                        * last pgd entry. */
+                       pmd_table = (pmd_t *)get_zeroed_page(GFP_KERNEL);
+                       if (!pmd_table) {
+                               free_page((long)cpu->lg->pgdirs[next].pgdir);
+                               set_pgd(cpu->lg->pgdirs[next].pgdir, __pgd(0));
+                               next = cpu->cpu_pgd;
+                       } else {
+                               set_pgd(cpu->lg->pgdirs[next].pgdir +
+                                       SWITCHER_PGD_INDEX,
+                                       __pgd(__pa(pmd_table) | _PAGE_PRESENT));
+                               /* This is a blank page, so there are no kernel
+                                * mappings: caller must map the stack! */
+                               *blank_pgdir = 1;
+                       }
+#else
                        *blank_pgdir = 1;
+#endif
+               }
        }
        /* Record which Guest toplevel this shadows. */
        cpu->lg->pgdirs[next].gpgdir = gpgdir;
@@ -431,7 +617,7 @@ static unsigned int new_pgdir(struct lg_cpu *cpu,
 
 /*H:430 (iv) Switching page tables
  *
- * Now we've seen all the page table setting and manipulation, let's see what
+ * Now we've seen all the page table setting and manipulation, let's see
  * what happens when the Guest changes page tables (ie. changes the top-level
  * pgdir).  This occurs on almost every context switch. */
 void guest_new_pagetable(struct lg_cpu *cpu, unsigned long pgtable)
@@ -460,10 +646,25 @@ static void release_all_pagetables(struct lguest *lg)
 
        /* Every shadow pagetable this Guest has */
        for (i = 0; i < ARRAY_SIZE(lg->pgdirs); i++)
-               if (lg->pgdirs[i].pgdir)
+               if (lg->pgdirs[i].pgdir) {
+#ifdef CONFIG_X86_PAE
+                       pgd_t *spgd;
+                       pmd_t *pmdpage;
+                       unsigned int k;
+
+                       /* Get the last pmd page. */
+                       spgd = lg->pgdirs[i].pgdir + SWITCHER_PGD_INDEX;
+                       pmdpage = __va(pgd_pfn(*spgd) << PAGE_SHIFT);
+
+                       /* And release the pmd entries of that pmd page,
+                        * except for the switcher pmd. */
+                       for (k = 0; k < SWITCHER_PMD_INDEX; k++)
+                               release_pmd(&pmdpage[k]);
+#endif
                        /* Every PGD entry except the Switcher at the top */
                        for (j = 0; j < SWITCHER_PGD_INDEX; j++)
-                               release_pgd(lg, lg->pgdirs[i].pgdir + j);
+                               release_pgd(lg->pgdirs[i].pgdir + j);
+               }
 }
 
 /* We also throw away everything when a Guest tells us it's changed a kernel
@@ -504,24 +705,37 @@ static void do_set_pte(struct lg_cpu *cpu, int idx,
 {
        /* Look up the matching shadow page directory entry. */
        pgd_t *spgd = spgd_addr(cpu, idx, vaddr);
+#ifdef CONFIG_X86_PAE
+       pmd_t *spmd;
+#endif
 
        /* If the top level isn't present, there's no entry to update. */
        if (pgd_flags(*spgd) & _PAGE_PRESENT) {
-               /* Otherwise, we start by releasing the existing entry. */
-               pte_t *spte = spte_addr(*spgd, vaddr);
-               release_pte(*spte);
-
-               /* If they're setting this entry as dirty or accessed, we might
-                * as well put that entry they've given us in now.  This shaves
-                * 10% off a copy-on-write micro-benchmark. */
-               if (pte_flags(gpte) & (_PAGE_DIRTY | _PAGE_ACCESSED)) {
-                       check_gpte(cpu, gpte);
-                       *spte = gpte_to_spte(cpu, gpte,
-                                            pte_flags(gpte) & _PAGE_DIRTY);
-               } else
-                       /* Otherwise kill it and we can demand_page() it in
-                        * later. */
-                       *spte = __pte(0);
+#ifdef CONFIG_X86_PAE
+               spmd = spmd_addr(cpu, *spgd, vaddr);
+               if (pmd_flags(*spmd) & _PAGE_PRESENT) {
+#endif
+                       /* Otherwise, we start by releasing
+                        * the existing entry. */
+                       pte_t *spte = spte_addr(cpu, *spgd, vaddr);
+                       release_pte(*spte);
+
+                       /* If they're setting this entry as dirty or accessed,
+                        * we might as well put that entry they've given us
+                        * in now.  This shaves 10% off a
+                        * copy-on-write micro-benchmark. */
+                       if (pte_flags(gpte) & (_PAGE_DIRTY | _PAGE_ACCESSED)) {
+                               check_gpte(cpu, gpte);
+                               native_set_pte(spte,
+                                               gpte_to_spte(cpu, gpte,
+                                               pte_flags(gpte) & _PAGE_DIRTY));
+                       } else
+                               /* Otherwise kill it and we can demand_page()
+                                * it in later. */
+                               native_set_pte(spte, __pte(0));
+#ifdef CONFIG_X86_PAE
+               }
+#endif
        }
 }
 
@@ -568,12 +782,10 @@ void guest_set_pte(struct lg_cpu *cpu,
  *
  * So with that in mind here's our code to to update a (top-level) PGD entry:
  */
-void guest_set_pmd(struct lguest *lg, unsigned long gpgdir, u32 idx)
+void guest_set_pgd(struct lguest *lg, unsigned long gpgdir, u32 idx)
 {
        int pgdir;
 
-       /* The kernel seems to try to initialize this early on: we ignore its
-        * attempts to map over the Switcher. */
        if (idx >= SWITCHER_PGD_INDEX)
                return;
 
@@ -581,8 +793,14 @@ void guest_set_pmd(struct lguest *lg, unsigned long gpgdir, u32 idx)
        pgdir = find_pgdir(lg, gpgdir);
        if (pgdir < ARRAY_SIZE(lg->pgdirs))
                /* ... throw it away. */
-               release_pgd(lg, lg->pgdirs[pgdir].pgdir + idx);
+               release_pgd(lg->pgdirs[pgdir].pgdir + idx);
 }
+#ifdef CONFIG_X86_PAE
+void guest_set_pmd(struct lguest *lg, unsigned long pmdp, u32 idx)
+{
+       guest_pagetable_clear_all(&lg->cpus[0]);
+}
+#endif
 
 /* Once we know how much memory we have we can construct simple identity
  * (which set virtual == physical) and linear mappings
@@ -596,8 +814,16 @@ static unsigned long setup_pagetables(struct lguest *lg,
 {
        pgd_t __user *pgdir;
        pte_t __user *linear;
-       unsigned int mapped_pages, i, linear_pages, phys_linear;
        unsigned long mem_base = (unsigned long)lg->mem_base;
+       unsigned int mapped_pages, i, linear_pages;
+#ifdef CONFIG_X86_PAE
+       pmd_t __user *pmds;
+       unsigned int j;
+       pgd_t pgd;
+       pmd_t pmd;
+#else
+       unsigned int phys_linear;
+#endif
 
        /* We have mapped_pages frames to map, so we need
         * linear_pages page tables to map them. */
@@ -610,6 +836,9 @@ static unsigned long setup_pagetables(struct lguest *lg,
        /* Now we use the next linear_pages pages as pte pages */
        linear = (void *)pgdir - linear_pages * PAGE_SIZE;
 
+#ifdef CONFIG_X86_PAE
+       pmds = (void *)linear - PAGE_SIZE;
+#endif
        /* Linear mapping is easy: put every page's address into the
         * mapping in order. */
        for (i = 0; i < mapped_pages; i++) {
@@ -621,6 +850,22 @@ static unsigned long setup_pagetables(struct lguest *lg,
 
        /* The top level points to the linear page table pages above.
         * We setup the identity and linear mappings here. */
+#ifdef CONFIG_X86_PAE
+       for (i = j = 0; i < mapped_pages && j < PTRS_PER_PMD;
+            i += PTRS_PER_PTE, j++) {
+               native_set_pmd(&pmd, __pmd(((unsigned long)(linear + i)
+               - mem_base) | _PAGE_PRESENT | _PAGE_RW | _PAGE_USER));
+
+               if (copy_to_user(&pmds[j], &pmd, sizeof(pmd)) != 0)
+                       return -EFAULT;
+       }
+
+       set_pgd(&pgd, __pgd(((u32)pmds - mem_base) | _PAGE_PRESENT));
+       if (copy_to_user(&pgdir[0], &pgd, sizeof(pgd)) != 0)
+               return -EFAULT;
+       if (copy_to_user(&pgdir[3], &pgd, sizeof(pgd)) != 0)
+               return -EFAULT;
+#else
        phys_linear = (unsigned long)linear - mem_base;
        for (i = 0; i < mapped_pages; i += PTRS_PER_PTE) {
                pgd_t pgd;
@@ -633,6 +878,7 @@ static unsigned long setup_pagetables(struct lguest *lg,
                                    &pgd, sizeof(pgd)))
                        return -EFAULT;
        }
+#endif
 
        /* We return the top level (guest-physical) address: remember where
         * this is. */
@@ -648,7 +894,10 @@ int init_guest_pagetable(struct lguest *lg)
        u64 mem;
        u32 initrd_size;
        struct boot_params __user *boot = (struct boot_params *)lg->mem_base;
-
+#ifdef CONFIG_X86_PAE
+       pgd_t *pgd;
+       pmd_t *pmd_table;
+#endif
        /* Get the Guest memory size and the ramdisk size from the boot header
         * located at lg->mem_base (Guest address 0). */
        if (copy_from_user(&mem, &boot->e820_map[0].size, sizeof(mem))
@@ -663,6 +912,15 @@ int init_guest_pagetable(struct lguest *lg)
        lg->pgdirs[0].pgdir = (pgd_t *)get_zeroed_page(GFP_KERNEL);
        if (!lg->pgdirs[0].pgdir)
                return -ENOMEM;
+#ifdef CONFIG_X86_PAE
+       pgd = lg->pgdirs[0].pgdir;
+       pmd_table = (pmd_t *) get_zeroed_page(GFP_KERNEL);
+       if (!pmd_table)
+               return -ENOMEM;
+
+       set_pgd(pgd + SWITCHER_PGD_INDEX,
+               __pgd(__pa(pmd_table) | _PAGE_PRESENT));
+#endif
        lg->cpus[0].cpu_pgd = 0;
        return 0;
 }
@@ -672,17 +930,24 @@ void page_table_guest_data_init(struct lg_cpu *cpu)
 {
        /* We get the kernel address: above this is all kernel memory. */
        if (get_user(cpu->lg->kernel_address,
-                    &cpu->lg->lguest_data->kernel_address)
-           /* We tell the Guest that it can't use the top 4MB of virtual
-            * addresses used by the Switcher. */
-           || put_user(4U*1024*1024, &cpu->lg->lguest_data->reserve_mem)
-           || put_user(cpu->lg->pgdirs[0].gpgdir, &cpu->lg->lguest_data->pgdir))
+               &cpu->lg->lguest_data->kernel_address)
+               /* We tell the Guest that it can't use the top 2 or 4 MB
+                * of virtual addresses used by the Switcher. */
+               || put_user(RESERVE_MEM * 1024 * 1024,
+                       &cpu->lg->lguest_data->reserve_mem)
+               || put_user(cpu->lg->pgdirs[0].gpgdir,
+                       &cpu->lg->lguest_data->pgdir))
                kill_guest(cpu, "bad guest page %p", cpu->lg->lguest_data);
 
        /* In flush_user_mappings() we loop from 0 to
         * "pgd_index(lg->kernel_address)".  This assumes it won't hit the
         * Switcher mappings, so check that now. */
+#ifdef CONFIG_X86_PAE
+       if (pgd_index(cpu->lg->kernel_address) == SWITCHER_PGD_INDEX &&
+               pmd_index(cpu->lg->kernel_address) == SWITCHER_PMD_INDEX)
+#else
        if (pgd_index(cpu->lg->kernel_address) >= SWITCHER_PGD_INDEX)
+#endif
                kill_guest(cpu, "bad kernel address %#lx",
                                 cpu->lg->kernel_address);
 }
@@ -708,16 +973,30 @@ void free_guest_pagetable(struct lguest *lg)
 void map_switcher_in_guest(struct lg_cpu *cpu, struct lguest_pages *pages)
 {
        pte_t *switcher_pte_page = __get_cpu_var(switcher_pte_pages);
-       pgd_t switcher_pgd;
        pte_t regs_pte;
        unsigned long pfn;
 
+#ifdef CONFIG_X86_PAE
+       pmd_t switcher_pmd;
+       pmd_t *pmd_table;
+
+       native_set_pmd(&switcher_pmd, pfn_pmd(__pa(switcher_pte_page) >>
+                      PAGE_SHIFT, PAGE_KERNEL_EXEC));
+
+       pmd_table = __va(pgd_pfn(cpu->lg->
+                       pgdirs[cpu->cpu_pgd].pgdir[SWITCHER_PGD_INDEX])
+                                                               << PAGE_SHIFT);
+       native_set_pmd(&pmd_table[SWITCHER_PMD_INDEX], switcher_pmd);
+#else
+       pgd_t switcher_pgd;
+
        /* Make the last PGD entry for this Guest point to the Switcher's PTE
         * page for this CPU (with appropriate flags). */
-       switcher_pgd = __pgd(__pa(switcher_pte_page) | __PAGE_KERNEL);
+       switcher_pgd = __pgd(__pa(switcher_pte_page) | __PAGE_KERNEL_EXEC);
 
        cpu->lg->pgdirs[cpu->cpu_pgd].pgdir[SWITCHER_PGD_INDEX] = switcher_pgd;
 
+#endif
        /* We also change the Switcher PTE page.  When we're running the Guest,
         * we want the Guest's "regs" page to appear where the first Switcher
         * page for this CPU is.  This is an optimization: when the Switcher
@@ -726,8 +1005,9 @@ void map_switcher_in_guest(struct lg_cpu *cpu, struct lguest_pages *pages)
         * page is already mapped there, we don't have to copy them out
         * again. */
        pfn = __pa(cpu->regs_page) >> PAGE_SHIFT;
-       regs_pte = pfn_pte(pfn, __pgprot(__PAGE_KERNEL));
-       switcher_pte_page[(unsigned long)pages/PAGE_SIZE%PTRS_PER_PTE] = regs_pte;
+       native_set_pte(&regs_pte, pfn_pte(pfn, PAGE_KERNEL));
+       native_set_pte(&switcher_pte_page[pte_index((unsigned long)pages)],
+                       regs_pte);
 }
 /*:*/
 
@@ -752,21 +1032,21 @@ static __init void populate_switcher_pte_page(unsigned int cpu,
 
        /* The first entries are easy: they map the Switcher code. */
        for (i = 0; i < pages; i++) {
-               pte[i] = mk_pte(switcher_page[i],
-                               __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED));
+               native_set_pte(&pte[i], mk_pte(switcher_page[i],
+                               __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED)));
        }
 
        /* The only other thing we map is this CPU's pair of pages. */
        i = pages + cpu*2;
 
        /* First page (Guest registers) is writable from the Guest */
-       pte[i] = pfn_pte(page_to_pfn(switcher_page[i]),
-                        __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED|_PAGE_RW));
+       native_set_pte(&pte[i], pfn_pte(page_to_pfn(switcher_page[i]),
+                        __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED|_PAGE_RW)));
 
        /* The second page contains the "struct lguest_ro_state", and is
         * read-only. */
-       pte[i+1] = pfn_pte(page_to_pfn(switcher_page[i+1]),
-                          __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED));
+       native_set_pte(&pte[i+1], pfn_pte(page_to_pfn(switcher_page[i+1]),
+                          __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED)));
 }
 
 /* We've made it through the page table code.  Perhaps our tired brains are
index 7ede64ffeef9e02b54ced2c2d024ccf17d9d59cd..482ed5a18750659e07f7bda54a90429822079b6f 100644 (file)
@@ -150,7 +150,7 @@ void load_guest_gdt_entry(struct lg_cpu *cpu, u32 num, u32 lo, u32 hi)
 {
        /* We assume the Guest has the same number of GDT entries as the
         * Host, otherwise we'd have to dynamically allocate the Guest GDT. */
-       if (num > ARRAY_SIZE(cpu->arch.gdt))
+       if (num >= ARRAY_SIZE(cpu->arch.gdt))
                kill_guest(cpu, "too many gdt entries %i", num);
 
        /* Set it up, then fix it. */
index 5d496a99e034c9498c583957f7a6b028370e8c3c..44b9315044579639e9e1e10463887f866c38c9ef 100644 (file)
@@ -146,7 +146,6 @@ static MPT_EVHANDLER                 MptEvHandlers[MPT_MAX_PROTOCOL_DRIVERS];
 static MPT_RESETHANDLER                 MptResetHandlers[MPT_MAX_PROTOCOL_DRIVERS];
 static struct mpt_pci_driver   *MptDeviceDriverHandlers[MPT_MAX_PROTOCOL_DRIVERS];
 
-static DECLARE_WAIT_QUEUE_HEAD(mpt_waitq);
 
 /*
  *  Driver Callback Index's
@@ -159,7 +158,8 @@ static u8 last_drv_idx;
  *  Forward protos...
  */
 static irqreturn_t mpt_interrupt(int irq, void *bus_id);
-static int     mpt_base_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply);
+static int     mptbase_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req,
+               MPT_FRAME_HDR *reply);
 static int     mpt_handshake_req_reply_wait(MPT_ADAPTER *ioc, int reqBytes,
                        u32 *req, int replyBytes, u16 *u16reply, int maxwait,
                        int sleepFlag);
@@ -190,9 +190,9 @@ static int  mpt_GetScsiPortSettings(MPT_ADAPTER *ioc, int portnum);
 static int     mpt_readScsiDevicePageHeaders(MPT_ADAPTER *ioc, int portnum);
 static void    mpt_read_ioc_pg_1(MPT_ADAPTER *ioc);
 static void    mpt_read_ioc_pg_4(MPT_ADAPTER *ioc);
-static void    mpt_timer_expired(unsigned long data);
 static void    mpt_get_manufacturing_pg_0(MPT_ADAPTER *ioc);
-static int     SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch);
+static int     SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch,
+       int sleepFlag);
 static int     SendEventAck(MPT_ADAPTER *ioc, EventNotificationReply_t *evnp);
 static int     mpt_host_page_access_control(MPT_ADAPTER *ioc, u8 access_control_value, int sleepFlag);
 static int     mpt_host_page_alloc(MPT_ADAPTER *ioc, pIOCInit_t ioc_init);
@@ -207,8 +207,8 @@ static int  procmpt_iocinfo_read(char *buf, char **start, off_t offset,
 #endif
 static void    mpt_get_fw_exp_ver(char *buf, MPT_ADAPTER *ioc);
 
-//int          mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag);
-static int     ProcessEventNotification(MPT_ADAPTER *ioc, EventNotificationReply_t *evReply, int *evHandlers);
+static int     ProcessEventNotification(MPT_ADAPTER *ioc,
+               EventNotificationReply_t *evReply, int *evHandlers);
 static void    mpt_iocstatus_info(MPT_ADAPTER *ioc, u32 ioc_status, MPT_FRAME_HDR *mf);
 static void    mpt_fc_log_info(MPT_ADAPTER *ioc, u32 log_info);
 static void    mpt_spi_log_info(MPT_ADAPTER *ioc, u32 log_info);
@@ -276,6 +276,56 @@ mpt_get_cb_idx(MPT_DRIVER_CLASS dclass)
        return 0;
 }
 
+/**
+ * mpt_is_discovery_complete - determine if discovery has completed
+ * @ioc: per adatper instance
+ *
+ * Returns 1 when discovery completed, else zero.
+ */
+static int
+mpt_is_discovery_complete(MPT_ADAPTER *ioc)
+{
+       ConfigExtendedPageHeader_t hdr;
+       CONFIGPARMS cfg;
+       SasIOUnitPage0_t *buffer;
+       dma_addr_t dma_handle;
+       int rc = 0;
+
+       memset(&hdr, 0, sizeof(ConfigExtendedPageHeader_t));
+       memset(&cfg, 0, sizeof(CONFIGPARMS));
+       hdr.PageVersion = MPI_SASIOUNITPAGE0_PAGEVERSION;
+       hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
+       hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
+       cfg.cfghdr.ehdr = &hdr;
+       cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+
+       if ((mpt_config(ioc, &cfg)))
+               goto out;
+       if (!hdr.ExtPageLength)
+               goto out;
+
+       buffer = pci_alloc_consistent(ioc->pcidev, hdr.ExtPageLength * 4,
+           &dma_handle);
+       if (!buffer)
+               goto out;
+
+       cfg.physAddr = dma_handle;
+       cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+
+       if ((mpt_config(ioc, &cfg)))
+               goto out_free_consistent;
+
+       if (!(buffer->PhyData[0].PortFlags &
+           MPI_SAS_IOUNIT0_PORT_FLAGS_DISCOVERY_IN_PROGRESS))
+               rc = 1;
+
+ out_free_consistent:
+       pci_free_consistent(ioc->pcidev, hdr.ExtPageLength * 4,
+           buffer, dma_handle);
+ out:
+       return rc;
+}
+
 /**
  *     mpt_fault_reset_work - work performed on workq after ioc fault
  *     @work: input argument, used to derive ioc
@@ -290,7 +340,7 @@ mpt_fault_reset_work(struct work_struct *work)
        int              rc;
        unsigned long    flags;
 
-       if (ioc->diagPending || !ioc->active)
+       if (ioc->ioc_reset_in_progress || !ioc->active)
                goto out;
 
        ioc_raw_state = mpt_GetIocState(ioc, 0);
@@ -307,6 +357,12 @@ mpt_fault_reset_work(struct work_struct *work)
                        printk(MYIOC_s_WARN_FMT "IOC is in FAULT state after "
                            "reset (%04xh)\n", ioc->name, ioc_raw_state &
                            MPI_DOORBELL_DATA_MASK);
+       } else if (ioc->bus_type == SAS && ioc->sas_discovery_quiesce_io) {
+               if ((mpt_is_discovery_complete(ioc))) {
+                       devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "clearing "
+                           "discovery_quiesce_io flag\n", ioc->name));
+                       ioc->sas_discovery_quiesce_io = 0;
+               }
        }
 
  out:
@@ -317,11 +373,11 @@ mpt_fault_reset_work(struct work_struct *work)
                ioc = ioc->alt_ioc;
 
        /* rearm the timer */
-       spin_lock_irqsave(&ioc->fault_reset_work_lock, flags);
+       spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
        if (ioc->reset_work_q)
                queue_delayed_work(ioc->reset_work_q, &ioc->fault_reset_work,
                        msecs_to_jiffies(MPT_POLLING_INTERVAL));
-       spin_unlock_irqrestore(&ioc->fault_reset_work_lock, flags);
+       spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
 }
 
 
@@ -501,9 +557,9 @@ mpt_interrupt(int irq, void *bus_id)
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /**
- *     mpt_base_reply - MPT base driver's callback routine
+ *     mptbase_reply - MPT base driver's callback routine
  *     @ioc: Pointer to MPT_ADAPTER structure
- *     @mf: Pointer to original MPT request frame
+ *     @req: Pointer to original MPT request frame
  *     @reply: Pointer to MPT reply frame (NULL if TurboReply)
  *
  *     MPT base driver's callback routine; all base driver
@@ -514,122 +570,49 @@ mpt_interrupt(int irq, void *bus_id)
  *     should be freed, or 0 if it shouldn't.
  */
 static int
-mpt_base_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *reply)
+mptbase_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply)
 {
+       EventNotificationReply_t *pEventReply;
+       u8 event;
+       int evHandlers;
        int freereq = 1;
-       u8 func;
-
-       dmfprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mpt_base_reply() called\n", ioc->name));
-#ifdef CONFIG_FUSION_LOGGING
-       if ((ioc->debug_level & MPT_DEBUG_MSG_FRAME) &&
-                       !(reply->u.hdr.MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY)) {
-               dmfprintk(ioc, printk(MYIOC_s_INFO_FMT ": Original request frame (@%p) header\n",
-                   ioc->name, mf));
-               DBG_DUMP_REQUEST_FRAME_HDR(ioc, (u32 *)mf);
-       }
-#endif
-
-       func = reply->u.hdr.Function;
-       dmfprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mpt_base_reply, Function=%02Xh\n",
-                       ioc->name, func));
-
-       if (func == MPI_FUNCTION_EVENT_NOTIFICATION) {
-               EventNotificationReply_t *pEvReply = (EventNotificationReply_t *) reply;
-               int evHandlers = 0;
-               int results;
-
-               results = ProcessEventNotification(ioc, pEvReply, &evHandlers);
-               if (results != evHandlers) {
-                       /* CHECKME! Any special handling needed here? */
-                       devtverboseprintk(ioc, printk(MYIOC_s_WARN_FMT "Called %d event handlers, sum results = %d\n",
-                                       ioc->name, evHandlers, results));
-               }
 
-               /*
-                *      Hmmm...  It seems that EventNotificationReply is an exception
-                *      to the rule of one reply per request.
-                */
-               if (pEvReply->MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY) {
+       switch (reply->u.hdr.Function) {
+       case MPI_FUNCTION_EVENT_NOTIFICATION:
+               pEventReply = (EventNotificationReply_t *)reply;
+               evHandlers = 0;
+               ProcessEventNotification(ioc, pEventReply, &evHandlers);
+               event = le32_to_cpu(pEventReply->Event) & 0xFF;
+               if (pEventReply->MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY)
                        freereq = 0;
-               } else {
-                       devtverboseprintk(ioc, printk(MYIOC_s_WARN_FMT "EVENT_NOTIFICATION reply %p returns Request frame\n",
-                               ioc->name, pEvReply));
-               }
-
-#ifdef CONFIG_PROC_FS
-//             LogEvent(ioc, pEvReply);
-#endif
-
-       } else if (func == MPI_FUNCTION_EVENT_ACK) {
-               dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mpt_base_reply, EventAck reply received\n",
-                               ioc->name));
-       } else if (func == MPI_FUNCTION_CONFIG) {
-               CONFIGPARMS *pCfg;
-               unsigned long flags;
-
-               dcprintk(ioc, printk(MYIOC_s_DEBUG_FMT "config_complete (mf=%p,mr=%p)\n",
-                               ioc->name, mf, reply));
-
-               pCfg = * ((CONFIGPARMS **)((u8 *) mf + ioc->req_sz - sizeof(void *)));
-
-               if (pCfg) {
-                       /* disable timer and remove from linked list */
-                       del_timer(&pCfg->timer);
-
-                       spin_lock_irqsave(&ioc->FreeQlock, flags);
-                       list_del(&pCfg->linkage);
-                       spin_unlock_irqrestore(&ioc->FreeQlock, flags);
-
-                       /*
-                        *      If IOC Status is SUCCESS, save the header
-                        *      and set the status code to GOOD.
-                        */
-                       pCfg->status = MPT_CONFIG_ERROR;
-                       if (reply) {
-                               ConfigReply_t   *pReply = (ConfigReply_t *)reply;
-                               u16              status;
-
-                               status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK;
-                               dcprintk(ioc, printk(MYIOC_s_NOTE_FMT "  IOCStatus=%04xh, IOCLogInfo=%08xh\n",
-                                    ioc->name, status, le32_to_cpu(pReply->IOCLogInfo)));
-
-                               pCfg->status = status;
-                               if (status == MPI_IOCSTATUS_SUCCESS) {
-                                       if ((pReply->Header.PageType &
-                                           MPI_CONFIG_PAGETYPE_MASK) ==
-                                           MPI_CONFIG_PAGETYPE_EXTENDED) {
-                                               pCfg->cfghdr.ehdr->ExtPageLength =
-                                                   le16_to_cpu(pReply->ExtPageLength);
-                                               pCfg->cfghdr.ehdr->ExtPageType =
-                                                   pReply->ExtPageType;
-                                       }
-                                       pCfg->cfghdr.hdr->PageVersion = pReply->Header.PageVersion;
-
-                                       /* If this is a regular header, save PageLength. */
-                                       /* LMP Do this better so not using a reserved field! */
-                                       pCfg->cfghdr.hdr->PageLength = pReply->Header.PageLength;
-                                       pCfg->cfghdr.hdr->PageNumber = pReply->Header.PageNumber;
-                                       pCfg->cfghdr.hdr->PageType = pReply->Header.PageType;
-                               }
-                       }
-
-                       /*
-                        *      Wake up the original calling thread
-                        */
-                       pCfg->wait_done = 1;
-                       wake_up(&mpt_waitq);
+               if (event != MPI_EVENT_EVENT_CHANGE)
+                       break;
+       case MPI_FUNCTION_CONFIG:
+       case MPI_FUNCTION_SAS_IO_UNIT_CONTROL:
+               ioc->mptbase_cmds.status |= MPT_MGMT_STATUS_COMMAND_GOOD;
+               if (reply) {
+                       ioc->mptbase_cmds.status |= MPT_MGMT_STATUS_RF_VALID;
+                       memcpy(ioc->mptbase_cmds.reply, reply,
+                           min(MPT_DEFAULT_FRAME_SIZE,
+                               4 * reply->u.reply.MsgLength));
                }
-       } else if (func == MPI_FUNCTION_SAS_IO_UNIT_CONTROL) {
-               /* we should be always getting a reply frame */
-               memcpy(ioc->persist_reply_frame, reply,
-                   min(MPT_DEFAULT_FRAME_SIZE,
-                   4*reply->u.reply.MsgLength));
-               del_timer(&ioc->persist_timer);
-               ioc->persist_wait_done = 1;
-               wake_up(&mpt_waitq);
-       } else {
-               printk(MYIOC_s_ERR_FMT "Unexpected msg function (=%02Xh) reply received!\n",
-                               ioc->name, func);
+               if (ioc->mptbase_cmds.status & MPT_MGMT_STATUS_PENDING) {
+                       ioc->mptbase_cmds.status &= ~MPT_MGMT_STATUS_PENDING;
+                       complete(&ioc->mptbase_cmds.done);
+               } else
+                       freereq = 0;
+               if (ioc->mptbase_cmds.status & MPT_MGMT_STATUS_FREE_MF)
+                       freereq = 1;
+               break;
+       case MPI_FUNCTION_EVENT_ACK:
+               devtverboseprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "EventAck reply received\n", ioc->name));
+               break;
+       default:
+               printk(MYIOC_s_ERR_FMT
+                   "Unexpected msg function (=%02Xh) reply received!\n",
+                   ioc->name, reply->u.hdr.Function);
+               break;
        }
 
        /*
@@ -988,17 +971,21 @@ mpt_free_msg_frame(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf)
 
        /*  Put Request back on FreeQ!  */
        spin_lock_irqsave(&ioc->FreeQlock, flags);
-       mf->u.frame.linkage.arg1 = 0xdeadbeaf; /* signature to know if this mf is freed */
+       if (cpu_to_le32(mf->u.frame.linkage.arg1) == 0xdeadbeaf)
+               goto out;
+       /* signature to know if this mf is freed */
+       mf->u.frame.linkage.arg1 = cpu_to_le32(0xdeadbeaf);
        list_add_tail(&mf->u.frame.linkage.list, &ioc->FreeQ);
 #ifdef MFCNT
        ioc->mfcnt--;
 #endif
+ out:
        spin_unlock_irqrestore(&ioc->FreeQlock, flags);
 }
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /**
- *     mpt_add_sge - Place a simple SGE at address pAddr.
+ *     mpt_add_sge - Place a simple 32 bit SGE at address pAddr.
  *     @pAddr: virtual address for SGE
  *     @flagslength: SGE flags and data transfer length
  *     @dma_addr: Physical address
@@ -1006,23 +993,117 @@ mpt_free_msg_frame(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf)
  *     This routine places a MPT request frame back on the MPT adapter's
  *     FreeQ.
  */
-void
-mpt_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr)
+static void
+mpt_add_sge(void *pAddr, u32 flagslength, dma_addr_t dma_addr)
 {
-       if (sizeof(dma_addr_t) == sizeof(u64)) {
-               SGESimple64_t *pSge = (SGESimple64_t *) pAddr;
+       SGESimple32_t *pSge = (SGESimple32_t *) pAddr;
+       pSge->FlagsLength = cpu_to_le32(flagslength);
+       pSge->Address = cpu_to_le32(dma_addr);
+}
+
+/**
+ *     mpt_add_sge_64bit - Place a simple 64 bit SGE at address pAddr.
+ *     @pAddr: virtual address for SGE
+ *     @flagslength: SGE flags and data transfer length
+ *     @dma_addr: Physical address
+ *
+ *     This routine places a MPT request frame back on the MPT adapter's
+ *     FreeQ.
+ **/
+static void
+mpt_add_sge_64bit(void *pAddr, u32 flagslength, dma_addr_t dma_addr)
+{
+       SGESimple64_t *pSge = (SGESimple64_t *) pAddr;
+       pSge->Address.Low = cpu_to_le32
+                       (lower_32_bits((unsigned long)(dma_addr)));
+       pSge->Address.High = cpu_to_le32
+                       (upper_32_bits((unsigned long)dma_addr));
+       pSge->FlagsLength = cpu_to_le32
+                       ((flagslength | MPT_SGE_FLAGS_64_BIT_ADDRESSING));
+}
+
+/**
+ *     mpt_add_sge_64bit_1078 - Place a simple 64 bit SGE at address pAddr
+ *     (1078 workaround).
+ *     @pAddr: virtual address for SGE
+ *     @flagslength: SGE flags and data transfer length
+ *     @dma_addr: Physical address
+ *
+ *     This routine places a MPT request frame back on the MPT adapter's
+ *     FreeQ.
+ **/
+static void
+mpt_add_sge_64bit_1078(void *pAddr, u32 flagslength, dma_addr_t dma_addr)
+{
+       SGESimple64_t *pSge = (SGESimple64_t *) pAddr;
+       u32 tmp;
+
+       pSge->Address.Low = cpu_to_le32
+                       (lower_32_bits((unsigned long)(dma_addr)));
+       tmp = (u32)(upper_32_bits((unsigned long)dma_addr));
+
+       /*
+        * 1078 errata workaround for the 36GB limitation
+        */
+       if ((((u64)dma_addr + MPI_SGE_LENGTH(flagslength)) >> 32)  == 9) {
+               flagslength |=
+                   MPI_SGE_SET_FLAGS(MPI_SGE_FLAGS_LOCAL_ADDRESS);
+               tmp |= (1<<31);
+               if (mpt_debug_level & MPT_DEBUG_36GB_MEM)
+                       printk(KERN_DEBUG "1078 P0M2 addressing for "
+                           "addr = 0x%llx len = %d\n",
+                           (unsigned long long)dma_addr,
+                           MPI_SGE_LENGTH(flagslength));
+       }
+
+       pSge->Address.High = cpu_to_le32(tmp);
+       pSge->FlagsLength = cpu_to_le32(
+               (flagslength | MPT_SGE_FLAGS_64_BIT_ADDRESSING));
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ *     mpt_add_chain - Place a 32 bit chain SGE at address pAddr.
+ *     @pAddr: virtual address for SGE
+ *     @next: nextChainOffset value (u32's)
+ *     @length: length of next SGL segment
+ *     @dma_addr: Physical address
+ *
+ */
+static void
+mpt_add_chain(void *pAddr, u8 next, u16 length, dma_addr_t dma_addr)
+{
+               SGEChain32_t *pChain = (SGEChain32_t *) pAddr;
+               pChain->Length = cpu_to_le16(length);
+               pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT;
+               pChain->NextChainOffset = next;
+               pChain->Address = cpu_to_le32(dma_addr);
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ *     mpt_add_chain_64bit - Place a 64 bit chain SGE at address pAddr.
+ *     @pAddr: virtual address for SGE
+ *     @next: nextChainOffset value (u32's)
+ *     @length: length of next SGL segment
+ *     @dma_addr: Physical address
+ *
+ */
+static void
+mpt_add_chain_64bit(void *pAddr, u8 next, u16 length, dma_addr_t dma_addr)
+{
+               SGEChain64_t *pChain = (SGEChain64_t *) pAddr;
                u32 tmp = dma_addr & 0xFFFFFFFF;
 
-               pSge->FlagsLength = cpu_to_le32(flagslength);
-               pSge->Address.Low = cpu_to_le32(tmp);
-               tmp = (u32) ((u64)dma_addr >> 32);
-               pSge->Address.High = cpu_to_le32(tmp);
+               pChain->Length = cpu_to_le16(length);
+               pChain->Flags = (MPI_SGE_FLAGS_CHAIN_ELEMENT |
+                                MPI_SGE_FLAGS_64_BIT_ADDRESSING);
 
-       } else {
-               SGESimple32_t *pSge = (SGESimple32_t *) pAddr;
-               pSge->FlagsLength = cpu_to_le32(flagslength);
-               pSge->Address = cpu_to_le32(dma_addr);
-       }
+               pChain->NextChainOffset = next;
+
+               pChain->Address.Low = cpu_to_le32(tmp);
+               tmp = (u32)(upper_32_bits((unsigned long)dma_addr));
+               pChain->Address.High = cpu_to_le32(tmp);
 }
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -1225,7 +1306,7 @@ mpt_host_page_alloc(MPT_ADAPTER *ioc, pIOCInit_t ioc_init)
        }
        flags_length = flags_length << MPI_SGE_FLAGS_SHIFT;
        flags_length |= ioc->HostPageBuffer_sz;
-       mpt_add_sge(psge, flags_length, ioc->HostPageBuffer_dma);
+       ioc->add_sge(psge, flags_length, ioc->HostPageBuffer_dma);
        ioc->facts.HostPageBufferSGE = ioc_init->HostPageBufferSGE;
 
 return 0;
@@ -1534,21 +1615,42 @@ mpt_mapresources(MPT_ADAPTER *ioc)
 
        pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);
 
-       if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))
-           && !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) {
-               dinitprintk(ioc, printk(MYIOC_s_INFO_FMT
-                   ": 64 BIT PCI BUS DMA ADDRESSING SUPPORTED\n",
-                   ioc->name));
-       } else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))
-           && !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) {
-               dinitprintk(ioc, printk(MYIOC_s_INFO_FMT
-                   ": 32 BIT PCI BUS DMA ADDRESSING SUPPORTED\n",
-                   ioc->name));
+       if (sizeof(dma_addr_t) > 4) {
+               const uint64_t required_mask = dma_get_required_mask
+                   (&pdev->dev);
+               if (required_mask > DMA_BIT_MASK(32)
+                       && !pci_set_dma_mask(pdev, DMA_BIT_MASK(64))
+                       && !pci_set_consistent_dma_mask(pdev,
+                                                DMA_BIT_MASK(64))) {
+                       ioc->dma_mask = DMA_BIT_MASK(64);
+                       dinitprintk(ioc, printk(MYIOC_s_INFO_FMT
+                               ": 64 BIT PCI BUS DMA ADDRESSING SUPPORTED\n",
+                               ioc->name));
+               } else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))
+                       && !pci_set_consistent_dma_mask(pdev,
+                                               DMA_BIT_MASK(32))) {
+                       ioc->dma_mask = DMA_BIT_MASK(32);
+                       dinitprintk(ioc, printk(MYIOC_s_INFO_FMT
+                               ": 32 BIT PCI BUS DMA ADDRESSING SUPPORTED\n",
+                               ioc->name));
+               } else {
+                       printk(MYIOC_s_WARN_FMT "no suitable DMA mask for %s\n",
+                           ioc->name, pci_name(pdev));
+                       return r;
+               }
        } else {
-               printk(MYIOC_s_WARN_FMT "no suitable DMA mask for %s\n",
-                   ioc->name, pci_name(pdev));
-               pci_release_selected_regions(pdev, ioc->bars);
-               return r;
+               if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))
+                       && !pci_set_consistent_dma_mask(pdev,
+                                               DMA_BIT_MASK(32))) {
+                       ioc->dma_mask = DMA_BIT_MASK(32);
+                       dinitprintk(ioc, printk(MYIOC_s_INFO_FMT
+                               ": 32 BIT PCI BUS DMA ADDRESSING SUPPORTED\n",
+                               ioc->name));
+               } else {
+                       printk(MYIOC_s_WARN_FMT "no suitable DMA mask for %s\n",
+                           ioc->name, pci_name(pdev));
+                       return r;
+               }
        }
 
        mem_phys = msize = 0;
@@ -1632,6 +1734,7 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
 
        ioc->id = mpt_ids++;
        sprintf(ioc->name, "ioc%d", ioc->id);
+       dinitprintk(ioc, printk(KERN_WARNING MYNAM ": mpt_adapter_install\n"));
 
        /*
         * set initial debug level
@@ -1650,14 +1753,36 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
                return r;
        }
 
+       /*
+        * Setting up proper handlers for scatter gather handling
+        */
+       if (ioc->dma_mask == DMA_BIT_MASK(64)) {
+               if (pdev->device == MPI_MANUFACTPAGE_DEVID_SAS1078)
+                       ioc->add_sge = &mpt_add_sge_64bit_1078;
+               else
+                       ioc->add_sge = &mpt_add_sge_64bit;
+               ioc->add_chain = &mpt_add_chain_64bit;
+               ioc->sg_addr_size = 8;
+       } else {
+               ioc->add_sge = &mpt_add_sge;
+               ioc->add_chain = &mpt_add_chain;
+               ioc->sg_addr_size = 4;
+       }
+       ioc->SGE_size = sizeof(u32) + ioc->sg_addr_size;
+
        ioc->alloc_total = sizeof(MPT_ADAPTER);
        ioc->req_sz = MPT_DEFAULT_FRAME_SIZE;           /* avoid div by zero! */
        ioc->reply_sz = MPT_REPLY_FRAME_SIZE;
 
        ioc->pcidev = pdev;
-       ioc->diagPending = 0;
-       spin_lock_init(&ioc->diagLock);
-       spin_lock_init(&ioc->initializing_hba_lock);
+
+       spin_lock_init(&ioc->taskmgmt_lock);
+       mutex_init(&ioc->internal_cmds.mutex);
+       init_completion(&ioc->internal_cmds.done);
+       mutex_init(&ioc->mptbase_cmds.mutex);
+       init_completion(&ioc->mptbase_cmds.done);
+       mutex_init(&ioc->taskmgmt_cmds.mutex);
+       init_completion(&ioc->taskmgmt_cmds.done);
 
        /* Initialize the event logging.
         */
@@ -1670,16 +1795,13 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
        ioc->mfcnt = 0;
 #endif
 
+       ioc->sh = NULL;
        ioc->cached_fw = NULL;
 
        /* Initilize SCSI Config Data structure
         */
        memset(&ioc->spi_data, 0, sizeof(SpiCfgData));
 
-       /* Initialize the running configQ head.
-        */
-       INIT_LIST_HEAD(&ioc->configQ);
-
        /* Initialize the fc rport list head.
         */
        INIT_LIST_HEAD(&ioc->fc_rports);
@@ -1690,9 +1812,8 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
 
        /* Initialize workqueue */
        INIT_DELAYED_WORK(&ioc->fault_reset_work, mpt_fault_reset_work);
-       spin_lock_init(&ioc->fault_reset_work_lock);
 
-       snprintf(ioc->reset_work_q_name, sizeof(ioc->reset_work_q_name),
+       snprintf(ioc->reset_work_q_name, MPT_KOBJ_NAME_LEN,
                 "mpt_poll_%d", ioc->id);
        ioc->reset_work_q =
                create_singlethread_workqueue(ioc->reset_work_q_name);
@@ -1767,11 +1888,14 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
        case MPI_MANUFACTPAGE_DEVID_SAS1064:
        case MPI_MANUFACTPAGE_DEVID_SAS1068:
                ioc->errata_flag_1064 = 1;
+               ioc->bus_type = SAS;
+               break;
 
        case MPI_MANUFACTPAGE_DEVID_SAS1064E:
        case MPI_MANUFACTPAGE_DEVID_SAS1068E:
        case MPI_MANUFACTPAGE_DEVID_SAS1078:
                ioc->bus_type = SAS;
+               break;
        }
 
 
@@ -1813,6 +1937,11 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
         */
        mpt_detect_bound_ports(ioc, pdev);
 
+       INIT_LIST_HEAD(&ioc->fw_event_list);
+       spin_lock_init(&ioc->fw_event_lock);
+       snprintf(ioc->fw_event_q_name, MPT_KOBJ_NAME_LEN, "mpt/%d", ioc->id);
+       ioc->fw_event_q = create_singlethread_workqueue(ioc->fw_event_q_name);
+
        if ((r = mpt_do_ioc_recovery(ioc, MPT_HOSTEVENT_IOC_BRINGUP,
            CAN_SLEEP)) != 0){
                printk(MYIOC_s_ERR_FMT "didn't initialize properly! (%d)\n",
@@ -1885,13 +2014,18 @@ mpt_detach(struct pci_dev *pdev)
        /*
         * Stop polling ioc for fault condition
         */
-       spin_lock_irqsave(&ioc->fault_reset_work_lock, flags);
+       spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
        wq = ioc->reset_work_q;
        ioc->reset_work_q = NULL;
-       spin_unlock_irqrestore(&ioc->fault_reset_work_lock, flags);
+       spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
        cancel_delayed_work(&ioc->fault_reset_work);
        destroy_workqueue(wq);
 
+       spin_lock_irqsave(&ioc->fw_event_lock, flags);
+       wq = ioc->fw_event_q;
+       ioc->fw_event_q = NULL;
+       spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+       destroy_workqueue(wq);
 
        sprintf(pname, MPT_PROCFS_MPTBASEDIR "/%s/summary", ioc->name);
        remove_proc_entry(pname, NULL);
@@ -1994,6 +2128,21 @@ mpt_resume(struct pci_dev *pdev)
        if (err)
                return err;
 
+       if (ioc->dma_mask == DMA_BIT_MASK(64)) {
+               if (pdev->device == MPI_MANUFACTPAGE_DEVID_SAS1078)
+                       ioc->add_sge = &mpt_add_sge_64bit_1078;
+               else
+                       ioc->add_sge = &mpt_add_sge_64bit;
+               ioc->add_chain = &mpt_add_chain_64bit;
+               ioc->sg_addr_size = 8;
+       } else {
+
+               ioc->add_sge = &mpt_add_sge;
+               ioc->add_chain = &mpt_add_chain;
+               ioc->sg_addr_size = 4;
+       }
+       ioc->SGE_size = sizeof(u32) + ioc->sg_addr_size;
+
        printk(MYIOC_s_INFO_FMT "pci-resume: ioc-state=0x%x,doorbell=0x%x\n",
            ioc->name, (mpt_GetIocState(ioc, 1) >> MPI_IOC_STATE_SHIFT),
            CHIPREG_READ32(&ioc->chip->Doorbell));
@@ -2091,12 +2240,16 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
        ioc->active = 0;
 
        if (ioc->alt_ioc) {
-               if (ioc->alt_ioc->active)
+               if (ioc->alt_ioc->active ||
+                   reason == MPT_HOSTEVENT_IOC_RECOVER) {
                        reset_alt_ioc_active = 1;
-
-               /* Disable alt-IOC's reply interrupts (and FreeQ) for a bit ... */
-               CHIPREG_WRITE32(&ioc->alt_ioc->chip->IntMask, 0xFFFFFFFF);
-               ioc->alt_ioc->active = 0;
+                       /* Disable alt-IOC's reply interrupts
+                        *  (and FreeQ) for a bit
+                        **/
+                       CHIPREG_WRITE32(&ioc->alt_ioc->chip->IntMask,
+                               0xFFFFFFFF);
+                       ioc->alt_ioc->active = 0;
+               }
        }
 
        hard = 1;
@@ -2117,9 +2270,11 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
                        }
 
                } else {
-                       printk(MYIOC_s_WARN_FMT "NOT READY!\n", ioc->name);
+                       printk(MYIOC_s_WARN_FMT
+                           "NOT READY WARNING!\n", ioc->name);
                }
-               return -1;
+               ret = -1;
+               goto out;
        }
 
        /* hard_reset_done = 0 if a soft reset was performed
@@ -2129,7 +2284,9 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
                if ((rc = MakeIocReady(ioc->alt_ioc, 0, sleepFlag)) == 0)
                        alt_ioc_ready = 1;
                else
-                       printk(MYIOC_s_WARN_FMT "alt_ioc not ready!\n", ioc->alt_ioc->name);
+                       printk(MYIOC_s_WARN_FMT
+                           ": alt-ioc Not ready WARNING!\n",
+                           ioc->alt_ioc->name);
        }
 
        for (ii=0; ii<5; ii++) {
@@ -2150,7 +2307,8 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
        if (alt_ioc_ready) {
                if ((rc = GetIocFacts(ioc->alt_ioc, sleepFlag, reason)) != 0) {
                        dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT
-                           "Initial Alt IocFacts failed rc=%x\n", ioc->name, rc));
+                           "Initial Alt IocFacts failed rc=%x\n",
+                           ioc->name, rc));
                        /* Retry - alt IOC was initialized once
                         */
                        rc = GetIocFacts(ioc->alt_ioc, sleepFlag, reason);
@@ -2194,16 +2352,20 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
                            IRQF_SHARED, ioc->name, ioc);
                        if (rc < 0) {
                                printk(MYIOC_s_ERR_FMT "Unable to allocate "
-                                   "interrupt %d!\n", ioc->name, ioc->pcidev->irq);
+                                   "interrupt %d!\n",
+                                   ioc->name, ioc->pcidev->irq);
                                if (ioc->msi_enable)
                                        pci_disable_msi(ioc->pcidev);
-                               return -EBUSY;
+                               ret = -EBUSY;
+                               goto out;
                        }
                        irq_allocated = 1;
                        ioc->pci_irq = ioc->pcidev->irq;
                        pci_set_master(ioc->pcidev);            /* ?? */
-                       dprintk(ioc, printk(MYIOC_s_INFO_FMT "installed at interrupt "
-                           "%d\n", ioc->name, ioc->pcidev->irq));
+                       pci_set_drvdata(ioc->pcidev, ioc);
+                       dinitprintk(ioc, printk(MYIOC_s_INFO_FMT
+                           "installed at interrupt %d\n", ioc->name,
+                           ioc->pcidev->irq));
                }
        }
 
@@ -2212,17 +2374,22 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
         * init as upper addresses are needed for init.
         * If fails, continue with alt-ioc processing
         */
+       dinitprintk(ioc, printk(MYIOC_s_INFO_FMT "PrimeIocFifos\n",
+           ioc->name));
        if ((ret == 0) && ((rc = PrimeIocFifos(ioc)) != 0))
                ret = -3;
 
        /* May need to check/upload firmware & data here!
         * If fails, continue with alt-ioc processing
         */
+       dinitprintk(ioc, printk(MYIOC_s_INFO_FMT "SendIocInit\n",
+           ioc->name));
        if ((ret == 0) && ((rc = SendIocInit(ioc, sleepFlag)) != 0))
                ret = -4;
 // NEW!
        if (alt_ioc_ready && ((rc = PrimeIocFifos(ioc->alt_ioc)) != 0)) {
-               printk(MYIOC_s_WARN_FMT ": alt_ioc (%d) FIFO mgmt alloc!\n",
+               printk(MYIOC_s_WARN_FMT
+                   ": alt-ioc (%d) FIFO mgmt alloc WARNING!\n",
                    ioc->alt_ioc->name, rc);
                alt_ioc_ready = 0;
                reset_alt_ioc_active = 0;
@@ -2232,8 +2399,9 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
                if ((rc = SendIocInit(ioc->alt_ioc, sleepFlag)) != 0) {
                        alt_ioc_ready = 0;
                        reset_alt_ioc_active = 0;
-                       printk(MYIOC_s_WARN_FMT "alt_ioc (%d) init failure!\n",
-                           ioc->alt_ioc->name, rc);
+                       printk(MYIOC_s_WARN_FMT
+                               ": alt-ioc: (%d) init failure WARNING!\n",
+                                       ioc->alt_ioc->name, rc);
                }
        }
 
@@ -2269,28 +2437,36 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
                }
        }
 
+       /*  Enable MPT base driver management of EventNotification
+        *  and EventAck handling.
+        */
+       if ((ret == 0) && (!ioc->facts.EventState)) {
+               dinitprintk(ioc, printk(MYIOC_s_INFO_FMT
+                       "SendEventNotification\n",
+                   ioc->name));
+               ret = SendEventNotification(ioc, 1, sleepFlag); /* 1=Enable */
+       }
+
+       if (ioc->alt_ioc && alt_ioc_ready && !ioc->alt_ioc->facts.EventState)
+               rc = SendEventNotification(ioc->alt_ioc, 1, sleepFlag);
+
        if (ret == 0) {
                /* Enable! (reply interrupt) */
                CHIPREG_WRITE32(&ioc->chip->IntMask, MPI_HIM_DIM);
                ioc->active = 1;
        }
-
-       if (reset_alt_ioc_active && ioc->alt_ioc) {
-               /* (re)Enable alt-IOC! (reply interrupt) */
-               dinitprintk(ioc, printk(MYIOC_s_INFO_FMT "alt_ioc reply irq re-enabled\n",
-                   ioc->alt_ioc->name));
-               CHIPREG_WRITE32(&ioc->alt_ioc->chip->IntMask, MPI_HIM_DIM);
-               ioc->alt_ioc->active = 1;
+       if (rc == 0) {  /* alt ioc */
+               if (reset_alt_ioc_active && ioc->alt_ioc) {
+                       /* (re)Enable alt-IOC! (reply interrupt) */
+                       dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "alt-ioc"
+                               "reply irq re-enabled\n",
+                               ioc->alt_ioc->name));
+                       CHIPREG_WRITE32(&ioc->alt_ioc->chip->IntMask,
+                               MPI_HIM_DIM);
+                       ioc->alt_ioc->active = 1;
+               }
        }
 
-       /*  Enable MPT base driver management of EventNotification
-        *  and EventAck handling.
-        */
-       if ((ret == 0) && (!ioc->facts.EventState))
-               (void) SendEventNotification(ioc, 1);   /* 1=Enable EventNotification */
-
-       if (ioc->alt_ioc && alt_ioc_ready && !ioc->alt_ioc->facts.EventState)
-               (void) SendEventNotification(ioc->alt_ioc, 1);  /* 1=Enable EventNotification */
 
        /*      Add additional "reason" check before call to GetLanConfigPages
         *      (combined with GetIoUnitPage2 call).  This prevents a somewhat
@@ -2306,8 +2482,9 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
                mutex_init(&ioc->raid_data.inactive_list_mutex);
                INIT_LIST_HEAD(&ioc->raid_data.inactive_list);
 
-               if (ioc->bus_type == SAS) {
+               switch (ioc->bus_type) {
 
+               case SAS:
                        /* clear persistency table */
                        if(ioc->facts.IOCExceptions &
                            MPI_IOCFACTS_EXCEPT_PERSISTENT_TABLE_FULL) {
@@ -2321,8 +2498,15 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
                         */
                        mpt_findImVolumes(ioc);
 
-               } else if (ioc->bus_type == FC) {
-                       if ((ioc->pfacts[0].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN) &&
+                       /* Check, and possibly reset, the coalescing value
+                        */
+                       mpt_read_ioc_pg_1(ioc);
+
+                       break;
+
+               case FC:
+                       if ((ioc->pfacts[0].ProtocolFlags &
+                               MPI_PORTFACTS_PROTOCOL_LAN) &&
                            (ioc->lan_cnfg_page0.Header.PageLength == 0)) {
                                /*
                                 *  Pre-fetch the ports LAN MAC address!
@@ -2331,11 +2515,14 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
                                (void) GetLanConfigPages(ioc);
                                a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow;
                                dprintk(ioc, printk(MYIOC_s_DEBUG_FMT
-                                   "LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
-                                   ioc->name, a[5], a[4], a[3], a[2], a[1], a[0]));
-
+                                       "LanAddr = %02X:%02X:%02X"
+                                       ":%02X:%02X:%02X\n",
+                                       ioc->name, a[5], a[4],
+                                       a[3], a[2], a[1], a[0]));
                        }
-               } else {
+                       break;
+
+               case SPI:
                        /* Get NVRAM and adapter maximums from SPP 0 and 2
                         */
                        mpt_GetScsiPortSettings(ioc, 0);
@@ -2354,6 +2541,8 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
                        mpt_read_ioc_pg_1(ioc);
 
                        mpt_read_ioc_pg_4(ioc);
+
+                       break;
                }
 
                GetIoUnitPage2(ioc);
@@ -2435,16 +2624,20 @@ mpt_detect_bound_ports(MPT_ADAPTER *ioc, struct pci_dev *pdev)
                if (_pcidev == peer) {
                        /* Paranoia checks */
                        if (ioc->alt_ioc != NULL) {
-                               printk(MYIOC_s_WARN_FMT "Oops, already bound to %s!\n",
-                                       ioc->name, ioc->alt_ioc->name);
+                               printk(MYIOC_s_WARN_FMT
+                                   "Oops, already bound (%s <==> %s)!\n",
+                                   ioc->name, ioc->name, ioc->alt_ioc->name);
                                break;
                        } else if (ioc_srch->alt_ioc != NULL) {
-                               printk(MYIOC_s_WARN_FMT "Oops, already bound to %s!\n",
-                                       ioc_srch->name, ioc_srch->alt_ioc->name);
+                               printk(MYIOC_s_WARN_FMT
+                                   "Oops, already bound (%s <==> %s)!\n",
+                                   ioc_srch->name, ioc_srch->name,
+                                   ioc_srch->alt_ioc->name);
                                break;
                        }
-                       dprintk(ioc, printk(MYIOC_s_INFO_FMT "FOUND! binding to %s\n",
-                               ioc->name, ioc_srch->name));
+                       dprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                               "FOUND! binding %s <==> %s\n",
+                               ioc->name, ioc->name, ioc_srch->name));
                        ioc_srch->alt_ioc = ioc;
                        ioc->alt_ioc = ioc_srch;
                }
@@ -2464,8 +2657,8 @@ mpt_adapter_disable(MPT_ADAPTER *ioc)
        int ret;
 
        if (ioc->cached_fw != NULL) {
-               ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: Pushing FW onto "
-                   "adapter\n", __func__, ioc->name));
+               ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                       "%s: Pushing FW onto adapter\n", __func__, ioc->name));
                if ((ret = mpt_downloadboot(ioc, (MpiFwHeader_t *)
                    ioc->cached_fw, CAN_SLEEP)) < 0) {
                        printk(MYIOC_s_WARN_FMT
@@ -2474,11 +2667,30 @@ mpt_adapter_disable(MPT_ADAPTER *ioc)
                }
        }
 
+       /*
+        * Put the controller into ready state (if its not already)
+        */
+       if (mpt_GetIocState(ioc, 1) != MPI_IOC_STATE_READY) {
+               if (!SendIocReset(ioc, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET,
+                   CAN_SLEEP)) {
+                       if (mpt_GetIocState(ioc, 1) != MPI_IOC_STATE_READY)
+                               printk(MYIOC_s_ERR_FMT "%s:  IOC msg unit "
+                                   "reset failed to put ioc in ready state!\n",
+                                   ioc->name, __func__);
+               } else
+                       printk(MYIOC_s_ERR_FMT "%s:  IOC msg unit reset "
+                           "failed!\n", ioc->name, __func__);
+       }
+
+
        /* Disable adapter interrupts! */
+       synchronize_irq(ioc->pcidev->irq);
        CHIPREG_WRITE32(&ioc->chip->IntMask, 0xFFFFFFFF);
        ioc->active = 0;
+
        /* Clear any lingering interrupt */
        CHIPREG_WRITE32(&ioc->chip->IntStatus, 0);
+       CHIPREG_READ32(&ioc->chip->IntStatus);
 
        if (ioc->alloc != NULL) {
                sz = ioc->alloc_sz;
@@ -2538,19 +2750,22 @@ mpt_adapter_disable(MPT_ADAPTER *ioc)
                if((ret = mpt_host_page_access_control(ioc,
                    MPI_DB_HPBAC_FREE_BUFFER, NO_SLEEP)) != 0) {
                        printk(MYIOC_s_ERR_FMT
-                          "host page buffers free failed (%d)!\n",
-                           ioc->name, ret);
+                          ": %s: host page buffers free failed (%d)!\n",
+                           ioc->name, __func__, ret);
                }
-               dexitprintk(ioc, printk(MYIOC_s_INFO_FMT "HostPageBuffer free  @ %p, sz=%d bytes\n",
-                       ioc->name, ioc->HostPageBuffer, ioc->HostPageBuffer_sz));
+               dexitprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                       "HostPageBuffer free  @ %p, sz=%d bytes\n",
+                       ioc->name, ioc->HostPageBuffer,
+                       ioc->HostPageBuffer_sz));
                pci_free_consistent(ioc->pcidev, ioc->HostPageBuffer_sz,
                    ioc->HostPageBuffer, ioc->HostPageBuffer_dma);
                ioc->HostPageBuffer = NULL;
                ioc->HostPageBuffer_sz = 0;
                ioc->alloc_total -= ioc->HostPageBuffer_sz;
        }
-}
 
+       pci_set_drvdata(ioc->pcidev, NULL);
+}
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /**
  *     mpt_adapter_dispose - Free all resources associated with an MPT adapter
@@ -2690,8 +2905,12 @@ MakeIocReady(MPT_ADAPTER *ioc, int force, int sleepFlag)
        }
 
        /* Is it already READY? */
-       if (!statefault && (ioc_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_READY)
+       if (!statefault &&
+           ((ioc_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_READY)) {
+               dinitprintk(ioc, printk(MYIOC_s_INFO_FMT
+                   "IOC is in READY state\n", ioc->name));
                return 0;
+       }
 
        /*
         *      Check to see if IOC is in FAULT state.
@@ -2764,8 +2983,9 @@ MakeIocReady(MPT_ADAPTER *ioc, int force, int sleepFlag)
 
                ii++; cntdn--;
                if (!cntdn) {
-                       printk(MYIOC_s_ERR_FMT "Wait IOC_READY state timeout(%d)!\n",
-                                       ioc->name, (int)((ii+5)/HZ));
+                       printk(MYIOC_s_ERR_FMT
+                               "Wait IOC_READY state (0x%x) timeout(%d)!\n",
+                               ioc->name, ioc_state, (int)((ii+5)/HZ));
                        return -ETIME;
                }
 
@@ -2778,9 +2998,8 @@ MakeIocReady(MPT_ADAPTER *ioc, int force, int sleepFlag)
        }
 
        if (statefault < 3) {
-               printk(MYIOC_s_INFO_FMT "Recovered from %s\n",
-                               ioc->name,
-                               statefault==1 ? "stuck handshake" : "IOC FAULT");
+               printk(MYIOC_s_INFO_FMT "Recovered from %s\n", ioc->name,
+                       statefault == 1 ? "stuck handshake" : "IOC FAULT");
        }
 
        return hard_reset_done;
@@ -2833,8 +3052,9 @@ GetIocFacts(MPT_ADAPTER *ioc, int sleepFlag, int reason)
 
        /* IOC *must* NOT be in RESET state! */
        if (ioc->last_state == MPI_IOC_STATE_RESET) {
-               printk(MYIOC_s_ERR_FMT "Can't get IOCFacts NOT READY! (%08x)\n",
-                   ioc->name, ioc->last_state );
+               printk(KERN_ERR MYNAM
+                   ": ERROR - Can't get IOCFacts, %s NOT READY! (%08x)\n",
+                   ioc->name, ioc->last_state);
                return -44;
        }
 
@@ -2896,7 +3116,7 @@ GetIocFacts(MPT_ADAPTER *ioc, int sleepFlag, int reason)
                 *      Old: u16{Major(4),Minor(4),SubMinor(8)}
                 *      New: u32{Major(8),Minor(8),Unit(8),Dev(8)}
                 */
-               if (facts->MsgVersion < 0x0102) {
+               if (facts->MsgVersion < MPI_VERSION_01_02) {
                        /*
                         *      Handle old FC f/w style, convert to new...
                         */
@@ -2908,9 +3128,11 @@ GetIocFacts(MPT_ADAPTER *ioc, int sleepFlag, int reason)
                        facts->FWVersion.Word = le32_to_cpu(facts->FWVersion.Word);
 
                facts->ProductID = le16_to_cpu(facts->ProductID);
+
                if ((ioc->facts.ProductID & MPI_FW_HEADER_PID_PROD_MASK)
                    > MPI_FW_HEADER_PID_PROD_TARGET_SCSI)
                        ioc->ir_firmware = 1;
+
                facts->CurrentHostMfaHighAddr =
                                le32_to_cpu(facts->CurrentHostMfaHighAddr);
                facts->GlobalCredits = le16_to_cpu(facts->GlobalCredits);
@@ -2926,7 +3148,7 @@ GetIocFacts(MPT_ADAPTER *ioc, int sleepFlag, int reason)
                 * to 14 in MPI-1.01.0x.
                 */
                if (facts->MsgLength >= (offsetof(IOCFactsReply_t,FWImageSize) + 7)/4 &&
-                   facts->MsgVersion > 0x0100) {
+                   facts->MsgVersion > MPI_VERSION_01_00) {
                        facts->FWImageSize = le32_to_cpu(facts->FWImageSize);
                }
 
@@ -3108,6 +3330,7 @@ SendIocInit(MPT_ADAPTER *ioc, int sleepFlag)
 
        ioc_init.MaxDevices = (U8)ioc->devices_per_bus;
        ioc_init.MaxBuses = (U8)ioc->number_of_buses;
+
        dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "facts.MsgVersion=%x\n",
                   ioc->name, ioc->facts.MsgVersion));
        if (ioc->facts.MsgVersion >= MPI_VERSION_01_05) {
@@ -3122,7 +3345,7 @@ SendIocInit(MPT_ADAPTER *ioc, int sleepFlag)
        }
        ioc_init.ReplyFrameSize = cpu_to_le16(ioc->reply_sz);   /* in BYTES */
 
-       if (sizeof(dma_addr_t) == sizeof(u64)) {
+       if (ioc->sg_addr_size == sizeof(u64)) {
                /* Save the upper 32-bits of the request
                 * (reply) and sense buffers.
                 */
@@ -3325,11 +3548,10 @@ mpt_do_upload(MPT_ADAPTER *ioc, int sleepFlag)
        FWUpload_t              *prequest;
        FWUploadReply_t         *preply;
        FWUploadTCSGE_t         *ptcsge;
-       int                      sgeoffset;
        u32                      flagsLength;
        int                      ii, sz, reply_sz;
        int                      cmdStatus;
-
+       int                     request_size;
        /* If the image size is 0, we are done.
         */
        if ((sz = ioc->facts.FWImageSize) == 0)
@@ -3364,42 +3586,41 @@ mpt_do_upload(MPT_ADAPTER *ioc, int sleepFlag)
        ptcsge->ImageSize = cpu_to_le32(sz);
        ptcsge++;
 
-       sgeoffset = sizeof(FWUpload_t) - sizeof(SGE_MPI_UNION) + sizeof(FWUploadTCSGE_t);
-
        flagsLength = MPT_SGE_FLAGS_SSIMPLE_READ | sz;
-       mpt_add_sge((char *)ptcsge, flagsLength, ioc->cached_fw_dma);
-
-       sgeoffset += sizeof(u32) + sizeof(dma_addr_t);
-       dinitprintk(ioc, printk(MYIOC_s_INFO_FMT ": Sending FW Upload (req @ %p) sgeoffset=%d \n",
-           ioc->name, prequest, sgeoffset));
+       ioc->add_sge((char *)ptcsge, flagsLength, ioc->cached_fw_dma);
+       request_size = offsetof(FWUpload_t, SGL) + sizeof(FWUploadTCSGE_t) +
+           ioc->SGE_size;
+       dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Sending FW Upload "
+           " (req @ %p) fw_size=%d mf_request_size=%d\n", ioc->name, prequest,
+           ioc->facts.FWImageSize, request_size));
        DBG_DUMP_FW_REQUEST_FRAME(ioc, (u32 *)prequest);
 
-       ii = mpt_handshake_req_reply_wait(ioc, sgeoffset, (u32*)prequest,
-                               reply_sz, (u16*)preply, 65 /*seconds*/, sleepFlag);
+       ii = mpt_handshake_req_reply_wait(ioc, request_size, (u32 *)prequest,
+           reply_sz, (u16 *)preply, 65 /*seconds*/, sleepFlag);
 
-       dinitprintk(ioc, printk(MYIOC_s_INFO_FMT ": FW Upload completed rc=%x \n", ioc->name, ii));
+       dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "FW Upload completed "
+           "rc=%x \n", ioc->name, ii));
 
        cmdStatus = -EFAULT;
        if (ii == 0) {
                /* Handshake transfer was complete and successful.
                 * Check the Reply Frame.
                 */
-               int status, transfer_sz;
-               status = le16_to_cpu(preply->IOCStatus);
-               if (status == MPI_IOCSTATUS_SUCCESS) {
-                       transfer_sz = le32_to_cpu(preply->ActualImageSize);
-                       if (transfer_sz == sz)
+               int status;
+               status = le16_to_cpu(preply->IOCStatus) &
+                               MPI_IOCSTATUS_MASK;
+               if (status == MPI_IOCSTATUS_SUCCESS &&
+                   ioc->facts.FWImageSize ==
+                   le32_to_cpu(preply->ActualImageSize))
                                cmdStatus = 0;
-               }
        }
        dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": do_upload cmdStatus=%d \n",
                        ioc->name, cmdStatus));
 
 
        if (cmdStatus) {
-
-               ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": fw upload failed, freeing image \n",
-                       ioc->name));
+               ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "fw upload failed, "
+                   "freeing image \n", ioc->name));
                mpt_free_fw_memory(ioc);
        }
        kfree(prequest);
@@ -3723,6 +3944,10 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag)
        CHIPREG_WRITE32(&ioc->chip->IntStatus, 0);
 
        if (ioc->pcidev->device == MPI_MANUFACTPAGE_DEVID_SAS1078) {
+
+               if (!ignore)
+                       return 0;
+
                drsprintk(ioc, printk(MYIOC_s_WARN_FMT "%s: Doorbell=%p; 1078 reset "
                        "address=%p\n",  ioc->name, __func__,
                        &ioc->chip->Doorbell, &ioc->chip->Reset_1078));
@@ -3740,6 +3965,7 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag)
                                "looking for READY STATE: doorbell=%x"
                                " count=%d\n",
                                ioc->name, doorbell, count));
+
                        if (doorbell == MPI_IOC_STATE_READY) {
                                return 1;
                        }
@@ -3890,6 +4116,10 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag)
                                doorbell = CHIPREG_READ32(&ioc->chip->Doorbell);
                                doorbell &= MPI_IOC_STATE_MASK;
 
+                               drsprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                                   "looking for READY STATE: doorbell=%x"
+                                   " count=%d\n", ioc->name, doorbell, count));
+
                                if (doorbell == MPI_IOC_STATE_READY) {
                                        break;
                                }
@@ -3901,6 +4131,11 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag)
                                        mdelay (1000);
                                }
                        }
+
+                       if (doorbell != MPI_IOC_STATE_READY)
+                               printk(MYIOC_s_ERR_FMT "Failed to come READY "
+                                   "after reset! IocState=%x", ioc->name,
+                                   doorbell);
                }
        }
 
@@ -4019,8 +4254,9 @@ SendIocReset(MPT_ADAPTER *ioc, u8 reset_type, int sleepFlag)
                        if (sleepFlag != CAN_SLEEP)
                                count *= 10;
 
-                       printk(MYIOC_s_ERR_FMT "Wait IOC_READY state timeout(%d)!\n",
-                           ioc->name, (int)((count+5)/HZ));
+                       printk(MYIOC_s_ERR_FMT
+                           "Wait IOC_READY state (0x%x) timeout(%d)!\n",
+                           ioc->name, state, (int)((count+5)/HZ));
                        return -ETIME;
                }
 
@@ -4090,24 +4326,29 @@ initChainBuffers(MPT_ADAPTER *ioc)
         * num_sge = num sge in request frame + last chain buffer
         * scale = num sge per chain buffer if no chain element
         */
-       scale = ioc->req_sz/(sizeof(dma_addr_t) + sizeof(u32));
-       if (sizeof(dma_addr_t) == sizeof(u64))
-               num_sge =  scale + (ioc->req_sz - 60) / (sizeof(dma_addr_t) + sizeof(u32));
+       scale = ioc->req_sz / ioc->SGE_size;
+       if (ioc->sg_addr_size == sizeof(u64))
+               num_sge =  scale + (ioc->req_sz - 60) / ioc->SGE_size;
        else
-               num_sge =  1+ scale + (ioc->req_sz - 64) / (sizeof(dma_addr_t) + sizeof(u32));
+               num_sge =  1 + scale + (ioc->req_sz - 64) / ioc->SGE_size;
 
-       if (sizeof(dma_addr_t) == sizeof(u64)) {
+       if (ioc->sg_addr_size == sizeof(u64)) {
                numSGE = (scale - 1) * (ioc->facts.MaxChainDepth-1) + scale +
-                       (ioc->req_sz - 60) / (sizeof(dma_addr_t) + sizeof(u32));
+                       (ioc->req_sz - 60) / ioc->SGE_size;
        } else {
-               numSGE = 1 + (scale - 1) * (ioc->facts.MaxChainDepth-1) + scale +
-                       (ioc->req_sz - 64) / (sizeof(dma_addr_t) + sizeof(u32));
+               numSGE = 1 + (scale - 1) * (ioc->facts.MaxChainDepth-1) +
+                   scale + (ioc->req_sz - 64) / ioc->SGE_size;
        }
        dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "num_sge=%d numSGE=%d\n",
                ioc->name, num_sge, numSGE));
 
-       if ( numSGE > MPT_SCSI_SG_DEPTH )
-               numSGE = MPT_SCSI_SG_DEPTH;
+       if (ioc->bus_type == FC) {
+               if (numSGE > MPT_SCSI_FC_SG_DEPTH)
+                       numSGE = MPT_SCSI_FC_SG_DEPTH;
+       } else {
+               if (numSGE > MPT_SCSI_SG_DEPTH)
+                       numSGE = MPT_SCSI_SG_DEPTH;
+       }
 
        num_chain = 1;
        while (numSGE - num_sge > 0) {
@@ -4161,12 +4402,42 @@ PrimeIocFifos(MPT_ADAPTER *ioc)
        dma_addr_t alloc_dma;
        u8 *mem;
        int i, reply_sz, sz, total_size, num_chain;
+       u64     dma_mask;
+
+       dma_mask = 0;
 
        /*  Prime reply FIFO...  */
 
        if (ioc->reply_frames == NULL) {
                if ( (num_chain = initChainBuffers(ioc)) < 0)
                        return -1;
+               /*
+                * 1078 errata workaround for the 36GB limitation
+                */
+               if (ioc->pcidev->device == MPI_MANUFACTPAGE_DEVID_SAS1078 &&
+                   ioc->dma_mask > DMA_35BIT_MASK) {
+                       if (!pci_set_dma_mask(ioc->pcidev, DMA_BIT_MASK(32))
+                           && !pci_set_consistent_dma_mask(ioc->pcidev,
+                           DMA_BIT_MASK(32))) {
+                               dma_mask = DMA_35BIT_MASK;
+                               d36memprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                                   "setting 35 bit addressing for "
+                                   "Request/Reply/Chain and Sense Buffers\n",
+                                   ioc->name));
+                       } else {
+                               /*Reseting DMA mask to 64 bit*/
+                               pci_set_dma_mask(ioc->pcidev,
+                                       DMA_BIT_MASK(64));
+                               pci_set_consistent_dma_mask(ioc->pcidev,
+                                       DMA_BIT_MASK(64));
+
+                               printk(MYIOC_s_ERR_FMT
+                                   "failed setting 35 bit addressing for "
+                                   "Request/Reply/Chain and Sense Buffers\n",
+                                   ioc->name);
+                               return -1;
+                       }
+               }
 
                total_size = reply_sz = (ioc->reply_sz * ioc->reply_depth);
                dinitprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ReplyBuffer sz=%d bytes, ReplyDepth=%d\n",
@@ -4305,9 +4576,16 @@ PrimeIocFifos(MPT_ADAPTER *ioc)
                alloc_dma += ioc->reply_sz;
        }
 
+       if (dma_mask == DMA_35BIT_MASK && !pci_set_dma_mask(ioc->pcidev,
+           ioc->dma_mask) && !pci_set_consistent_dma_mask(ioc->pcidev,
+           ioc->dma_mask))
+               d36memprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "restoring 64 bit addressing\n", ioc->name));
+
        return 0;
 
 out_fail:
+
        if (ioc->alloc != NULL) {
                sz = ioc->alloc_sz;
                pci_free_consistent(ioc->pcidev,
@@ -4324,6 +4602,13 @@ out_fail:
                                ioc->sense_buf_pool, ioc->sense_buf_pool_dma);
                ioc->sense_buf_pool = NULL;
        }
+
+       if (dma_mask == DMA_35BIT_MASK && !pci_set_dma_mask(ioc->pcidev,
+           DMA_BIT_MASK(64)) && !pci_set_consistent_dma_mask(ioc->pcidev,
+           DMA_BIT_MASK(64)))
+               d36memprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "restoring 64 bit addressing\n", ioc->name));
+
        return -1;
 }
 
@@ -4759,7 +5044,14 @@ mptbase_sas_persist_operation(MPT_ADAPTER *ioc, u8 persist_opcode)
        SasIoUnitControlReply_t         *sasIoUnitCntrReply;
        MPT_FRAME_HDR                   *mf = NULL;
        MPIHeader_t                     *mpi_hdr;
+       int                             ret = 0;
+       unsigned long                   timeleft;
+
+       mutex_lock(&ioc->mptbase_cmds.mutex);
 
+       /* init the internal cmd struct */
+       memset(ioc->mptbase_cmds.reply, 0 , MPT_DEFAULT_FRAME_SIZE);
+       INITIALIZE_MGMT_STATUS(ioc->mptbase_cmds.status)
 
        /* insure garbage is not sent to fw */
        switch(persist_opcode) {
@@ -4769,17 +5061,19 @@ mptbase_sas_persist_operation(MPT_ADAPTER *ioc, u8 persist_opcode)
                break;
 
        default:
-               return -1;
-               break;
+               ret = -1;
+               goto out;
        }
 
-       printk("%s: persist_opcode=%x\n",__func__, persist_opcode);
+       printk(KERN_DEBUG  "%s: persist_opcode=%x\n",
+               __func__, persist_opcode);
 
        /* Get a MF for this command.
         */
        if ((mf = mpt_get_msg_frame(mpt_base_index, ioc)) == NULL) {
-               printk("%s: no msg frames!\n",__func__);
-               return -1;
+               printk(KERN_DEBUG "%s: no msg frames!\n", __func__);
+               ret = -1;
+               goto out;
         }
 
        mpi_hdr = (MPIHeader_t *) mf;
@@ -4789,27 +5083,42 @@ mptbase_sas_persist_operation(MPT_ADAPTER *ioc, u8 persist_opcode)
        sasIoUnitCntrReq->MsgContext = mpi_hdr->MsgContext;
        sasIoUnitCntrReq->Operation = persist_opcode;
 
-       init_timer(&ioc->persist_timer);
-       ioc->persist_timer.data = (unsigned long) ioc;
-       ioc->persist_timer.function = mpt_timer_expired;
-       ioc->persist_timer.expires = jiffies + HZ*10 /* 10 sec */;
-       ioc->persist_wait_done=0;
-       add_timer(&ioc->persist_timer);
        mpt_put_msg_frame(mpt_base_index, ioc, mf);
-       wait_event(mpt_waitq, ioc->persist_wait_done);
+       timeleft = wait_for_completion_timeout(&ioc->mptbase_cmds.done, 10*HZ);
+       if (!(ioc->mptbase_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+               ret = -ETIME;
+               printk(KERN_DEBUG "%s: failed\n", __func__);
+               if (ioc->mptbase_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET)
+                       goto out;
+               if (!timeleft) {
+                       printk(KERN_DEBUG "%s: Issuing Reset from %s!!\n",
+                           ioc->name, __func__);
+                       mpt_HardResetHandler(ioc, CAN_SLEEP);
+                       mpt_free_msg_frame(ioc, mf);
+               }
+               goto out;
+       }
+
+       if (!(ioc->mptbase_cmds.status & MPT_MGMT_STATUS_RF_VALID)) {
+               ret = -1;
+               goto out;
+       }
 
        sasIoUnitCntrReply =
-           (SasIoUnitControlReply_t *)ioc->persist_reply_frame;
+           (SasIoUnitControlReply_t *)ioc->mptbase_cmds.reply;
        if (le16_to_cpu(sasIoUnitCntrReply->IOCStatus) != MPI_IOCSTATUS_SUCCESS) {
-               printk("%s: IOCStatus=0x%X IOCLogInfo=0x%X\n",
-                   __func__,
-                   sasIoUnitCntrReply->IOCStatus,
+               printk(KERN_DEBUG "%s: IOCStatus=0x%X IOCLogInfo=0x%X\n",
+                   __func__, sasIoUnitCntrReply->IOCStatus,
                    sasIoUnitCntrReply->IOCLogInfo);
-               return -1;
-       }
+               printk(KERN_DEBUG "%s: failed\n", __func__);
+               ret = -1;
+       } else
+               printk(KERN_DEBUG "%s: success\n", __func__);
+ out:
 
-       printk("%s: success\n",__func__);
-       return 0;
+       CLEAR_MGMT_STATUS(ioc->mptbase_cmds.status)
+       mutex_unlock(&ioc->mptbase_cmds.mutex);
+       return ret;
 }
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -5394,17 +5703,20 @@ mpt_inactive_raid_volumes(MPT_ADAPTER *ioc, u8 channel, u8 id)
  *     -ENOMEM if pci_alloc failed
  **/
 int
-mpt_raid_phys_disk_pg0(MPT_ADAPTER *ioc, u8 phys_disk_num, pRaidPhysDiskPage0_t phys_disk)
+mpt_raid_phys_disk_pg0(MPT_ADAPTER *ioc, u8 phys_disk_num,
+                       RaidPhysDiskPage0_t *phys_disk)
 {
-       CONFIGPARMS                     cfg;
-       ConfigPageHeader_t              hdr;
+       CONFIGPARMS                     cfg;
+       ConfigPageHeader_t              hdr;
        dma_addr_t                      dma_handle;
        pRaidPhysDiskPage0_t            buffer = NULL;
        int                             rc;
 
        memset(&cfg, 0 , sizeof(CONFIGPARMS));
        memset(&hdr, 0 , sizeof(ConfigPageHeader_t));
+       memset(phys_disk, 0, sizeof(RaidPhysDiskPage0_t));
 
+       hdr.PageVersion = MPI_RAIDPHYSDISKPAGE0_PAGEVERSION;
        hdr.PageType = MPI_CONFIG_PAGETYPE_RAID_PHYSDISK;
        cfg.cfghdr.hdr = &hdr;
        cfg.physAddr = -1;
@@ -5450,6 +5762,161 @@ mpt_raid_phys_disk_pg0(MPT_ADAPTER *ioc, u8 phys_disk_num, pRaidPhysDiskPage0_t
        return rc;
 }
 
+/**
+ *     mpt_raid_phys_disk_get_num_paths - returns number paths associated to this phys_num
+ *     @ioc: Pointer to a Adapter Structure
+ *     @phys_disk_num: io unit unique phys disk num generated by the ioc
+ *
+ *     Return:
+ *     returns number paths
+ **/
+int
+mpt_raid_phys_disk_get_num_paths(MPT_ADAPTER *ioc, u8 phys_disk_num)
+{
+       CONFIGPARMS                     cfg;
+       ConfigPageHeader_t              hdr;
+       dma_addr_t                      dma_handle;
+       pRaidPhysDiskPage1_t            buffer = NULL;
+       int                             rc;
+
+       memset(&cfg, 0 , sizeof(CONFIGPARMS));
+       memset(&hdr, 0 , sizeof(ConfigPageHeader_t));
+
+       hdr.PageVersion = MPI_RAIDPHYSDISKPAGE1_PAGEVERSION;
+       hdr.PageType = MPI_CONFIG_PAGETYPE_RAID_PHYSDISK;
+       hdr.PageNumber = 1;
+       cfg.cfghdr.hdr = &hdr;
+       cfg.physAddr = -1;
+       cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+
+       if (mpt_config(ioc, &cfg) != 0) {
+               rc = 0;
+               goto out;
+       }
+
+       if (!hdr.PageLength) {
+               rc = 0;
+               goto out;
+       }
+
+       buffer = pci_alloc_consistent(ioc->pcidev, hdr.PageLength * 4,
+           &dma_handle);
+
+       if (!buffer) {
+               rc = 0;
+               goto out;
+       }
+
+       cfg.physAddr = dma_handle;
+       cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+       cfg.pageAddr = phys_disk_num;
+
+       if (mpt_config(ioc, &cfg) != 0) {
+               rc = 0;
+               goto out;
+       }
+
+       rc = buffer->NumPhysDiskPaths;
+ out:
+
+       if (buffer)
+               pci_free_consistent(ioc->pcidev, hdr.PageLength * 4, buffer,
+                   dma_handle);
+
+       return rc;
+}
+EXPORT_SYMBOL(mpt_raid_phys_disk_get_num_paths);
+
+/**
+ *     mpt_raid_phys_disk_pg1 - returns phys disk page 1
+ *     @ioc: Pointer to a Adapter Structure
+ *     @phys_disk_num: io unit unique phys disk num generated by the ioc
+ *     @phys_disk: requested payload data returned
+ *
+ *     Return:
+ *     0 on success
+ *     -EFAULT if read of config page header fails or data pointer not NULL
+ *     -ENOMEM if pci_alloc failed
+ **/
+int
+mpt_raid_phys_disk_pg1(MPT_ADAPTER *ioc, u8 phys_disk_num,
+               RaidPhysDiskPage1_t *phys_disk)
+{
+       CONFIGPARMS                     cfg;
+       ConfigPageHeader_t              hdr;
+       dma_addr_t                      dma_handle;
+       pRaidPhysDiskPage1_t            buffer = NULL;
+       int                             rc;
+       int                             i;
+       __le64                          sas_address;
+
+       memset(&cfg, 0 , sizeof(CONFIGPARMS));
+       memset(&hdr, 0 , sizeof(ConfigPageHeader_t));
+       rc = 0;
+
+       hdr.PageVersion = MPI_RAIDPHYSDISKPAGE1_PAGEVERSION;
+       hdr.PageType = MPI_CONFIG_PAGETYPE_RAID_PHYSDISK;
+       hdr.PageNumber = 1;
+       cfg.cfghdr.hdr = &hdr;
+       cfg.physAddr = -1;
+       cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+
+       if (mpt_config(ioc, &cfg) != 0) {
+               rc = -EFAULT;
+               goto out;
+       }
+
+       if (!hdr.PageLength) {
+               rc = -EFAULT;
+               goto out;
+       }
+
+       buffer = pci_alloc_consistent(ioc->pcidev, hdr.PageLength * 4,
+           &dma_handle);
+
+       if (!buffer) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       cfg.physAddr = dma_handle;
+       cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+       cfg.pageAddr = phys_disk_num;
+
+       if (mpt_config(ioc, &cfg) != 0) {
+               rc = -EFAULT;
+               goto out;
+       }
+
+       phys_disk->NumPhysDiskPaths = buffer->NumPhysDiskPaths;
+       phys_disk->PhysDiskNum = phys_disk_num;
+       for (i = 0; i < phys_disk->NumPhysDiskPaths; i++) {
+               phys_disk->Path[i].PhysDiskID = buffer->Path[i].PhysDiskID;
+               phys_disk->Path[i].PhysDiskBus = buffer->Path[i].PhysDiskBus;
+               phys_disk->Path[i].OwnerIdentifier =
+                               buffer->Path[i].OwnerIdentifier;
+               phys_disk->Path[i].Flags = le16_to_cpu(buffer->Path[i].Flags);
+               memcpy(&sas_address, &buffer->Path[i].WWID, sizeof(__le64));
+               sas_address = le64_to_cpu(sas_address);
+               memcpy(&phys_disk->Path[i].WWID, &sas_address, sizeof(__le64));
+               memcpy(&sas_address,
+                               &buffer->Path[i].OwnerWWID, sizeof(__le64));
+               sas_address = le64_to_cpu(sas_address);
+               memcpy(&phys_disk->Path[i].OwnerWWID,
+                               &sas_address, sizeof(__le64));
+       }
+
+ out:
+
+       if (buffer)
+               pci_free_consistent(ioc->pcidev, hdr.PageLength * 4, buffer,
+                   dma_handle);
+
+       return rc;
+}
+EXPORT_SYMBOL(mpt_raid_phys_disk_pg1);
+
+
 /**
  *     mpt_findImVolumes - Identify IDs of hidden disks and RAID Volumes
  *     @ioc: Pointer to a Adapter Strucutre
@@ -5775,30 +6242,28 @@ mpt_get_manufacturing_pg_0(MPT_ADAPTER *ioc)
  *     SendEventNotification - Send EventNotification (on or off) request to adapter
  *     @ioc: Pointer to MPT_ADAPTER structure
  *     @EvSwitch: Event switch flags
+ *     @sleepFlag: Specifies whether the process can sleep
  */
 static int
-SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch)
+SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch, int sleepFlag)
 {
-       EventNotification_t     *evnp;
+       EventNotification_t     evn;
+       MPIDefaultReply_t       reply_buf;
 
-       evnp = (EventNotification_t *) mpt_get_msg_frame(mpt_base_index, ioc);
-       if (evnp == NULL) {
-               devtverboseprintk(ioc, printk(MYIOC_s_WARN_FMT "Unable to allocate event request frame!\n",
-                               ioc->name));
-               return 0;
-       }
-       memset(evnp, 0, sizeof(*evnp));
+       memset(&evn, 0, sizeof(EventNotification_t));
+       memset(&reply_buf, 0, sizeof(MPIDefaultReply_t));
 
-       devtverboseprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Sending EventNotification (%d) request %p\n", ioc->name, EvSwitch, evnp));
+       evn.Function = MPI_FUNCTION_EVENT_NOTIFICATION;
+       evn.Switch = EvSwitch;
+       evn.MsgContext = cpu_to_le32(mpt_base_index << 16);
 
-       evnp->Function = MPI_FUNCTION_EVENT_NOTIFICATION;
-       evnp->ChainOffset = 0;
-       evnp->MsgFlags = 0;
-       evnp->Switch = EvSwitch;
-
-       mpt_put_msg_frame(mpt_base_index, ioc, (MPT_FRAME_HDR *)evnp);
+       devtverboseprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+           "Sending EventNotification (%d) request %p\n",
+           ioc->name, EvSwitch, &evn));
 
-       return 0;
+       return mpt_handshake_req_reply_wait(ioc, sizeof(EventNotification_t),
+           (u32 *)&evn, sizeof(MPIDefaultReply_t), (u16 *)&reply_buf, 30,
+           sleepFlag);
 }
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -5814,7 +6279,7 @@ SendEventAck(MPT_ADAPTER *ioc, EventNotificationReply_t *evnp)
 
        if ((pAck = (EventAck_t *) mpt_get_msg_frame(mpt_base_index, ioc)) == NULL) {
                dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, no msg frames!!\n",
-                   ioc->name,__func__));
+                   ioc->name, __func__));
                return -1;
        }
 
@@ -5851,12 +6316,19 @@ int
 mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *pCfg)
 {
        Config_t        *pReq;
+       ConfigReply_t   *pReply;
        ConfigExtendedPageHeader_t  *pExtHdr = NULL;
        MPT_FRAME_HDR   *mf;
-       unsigned long    flags;
-       int              ii, rc;
+       int              ii;
        int              flagsLength;
-       int              in_isr;
+       long             timeout;
+       int              ret;
+       u8               page_type = 0, extend_page;
+       unsigned long    timeleft;
+       unsigned long    flags;
+    int                 in_isr;
+       u8               issue_hard_reset = 0;
+       u8               retry_count = 0;
 
        /*      Prevent calling wait_event() (below), if caller happens
         *      to be in ISR context, because that is fatal!
@@ -5866,15 +6338,43 @@ mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *pCfg)
                dcprintk(ioc, printk(MYIOC_s_WARN_FMT "Config request not allowed in ISR context!\n",
                                ioc->name));
                return -EPERM;
+    }
+
+       /* don't send a config page during diag reset */
+       spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+       if (ioc->ioc_reset_in_progress) {
+               dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "%s: busy with host reset\n", ioc->name, __func__));
+               spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+               return -EBUSY;
+       }
+       spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+
+       /* don't send if no chance of success */
+       if (!ioc->active ||
+           mpt_GetIocState(ioc, 1) != MPI_IOC_STATE_OPERATIONAL) {
+               dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "%s: ioc not operational, %d, %xh\n",
+                   ioc->name, __func__, ioc->active,
+                   mpt_GetIocState(ioc, 0)));
+               return -EFAULT;
        }
 
+ retry_config:
+       mutex_lock(&ioc->mptbase_cmds.mutex);
+       /* init the internal cmd struct */
+       memset(ioc->mptbase_cmds.reply, 0 , MPT_DEFAULT_FRAME_SIZE);
+       INITIALIZE_MGMT_STATUS(ioc->mptbase_cmds.status)
+
        /* Get and Populate a free Frame
         */
        if ((mf = mpt_get_msg_frame(mpt_base_index, ioc)) == NULL) {
-               dcprintk(ioc, printk(MYIOC_s_WARN_FMT "mpt_config: no msg frames!\n",
-                               ioc->name));
-               return -EAGAIN;
+               dcprintk(ioc, printk(MYIOC_s_WARN_FMT
+               "mpt_config: no msg frames!\n", ioc->name));
+               ret = -EAGAIN;
+               goto out;
        }
+
        pReq = (Config_t *)mf;
        pReq->Action = pCfg->action;
        pReq->Reserved = 0;
@@ -5900,7 +6400,9 @@ mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *pCfg)
                pReq->ExtPageType = pExtHdr->ExtPageType;
                pReq->Header.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
 
-               /* Page Length must be treated as a reserved field for the extended header. */
+               /* Page Length must be treated as a reserved field for the
+                * extended header.
+                */
                pReq->Header.PageLength = 0;
        }
 
@@ -5913,78 +6415,91 @@ mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *pCfg)
        else
                flagsLength = MPT_SGE_FLAGS_SSIMPLE_READ;
 
-       if ((pCfg->cfghdr.hdr->PageType & MPI_CONFIG_PAGETYPE_MASK) == MPI_CONFIG_PAGETYPE_EXTENDED) {
+       if ((pCfg->cfghdr.hdr->PageType & MPI_CONFIG_PAGETYPE_MASK) ==
+           MPI_CONFIG_PAGETYPE_EXTENDED) {
                flagsLength |= pExtHdr->ExtPageLength * 4;
-
-               dcprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Sending Config request type %d, page %d and action %d\n",
-                       ioc->name, pReq->ExtPageType, pReq->Header.PageNumber, pReq->Action));
-       }
-       else {
+               page_type = pReq->ExtPageType;
+               extend_page = 1;
+       } else {
                flagsLength |= pCfg->cfghdr.hdr->PageLength * 4;
-
-               dcprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Sending Config request type %d, page %d and action %d\n",
-                       ioc->name, pReq->Header.PageType, pReq->Header.PageNumber, pReq->Action));
+               page_type = pReq->Header.PageType;
+               extend_page = 0;
        }
 
-       mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, pCfg->physAddr);
-
-       /* Append pCfg pointer to end of mf
-        */
-       *((void **) (((u8 *) mf) + (ioc->req_sz - sizeof(void *)))) =  (void *) pCfg;
+       dcprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+           "Sending Config request type 0x%x, page 0x%x and action %d\n",
+           ioc->name, page_type, pReq->Header.PageNumber, pReq->Action));
 
-       /* Initalize the timer
-        */
-       init_timer_on_stack(&pCfg->timer);
-       pCfg->timer.data = (unsigned long) ioc;
-       pCfg->timer.function = mpt_timer_expired;
-       pCfg->wait_done = 0;
-
-       /* Set the timer; ensure 10 second minimum */
-       if (pCfg->timeout < 10)
-               pCfg->timer.expires = jiffies + HZ*10;
-       else
-               pCfg->timer.expires = jiffies + HZ*pCfg->timeout;
-
-       /* Add to end of Q, set timer and then issue this command */
-       spin_lock_irqsave(&ioc->FreeQlock, flags);
-       list_add_tail(&pCfg->linkage, &ioc->configQ);
-       spin_unlock_irqrestore(&ioc->FreeQlock, flags);
-
-       add_timer(&pCfg->timer);
+       ioc->add_sge((char *)&pReq->PageBufferSGE, flagsLength, pCfg->physAddr);
+       timeout = (pCfg->timeout < 15) ? HZ*15 : HZ*pCfg->timeout;
        mpt_put_msg_frame(mpt_base_index, ioc, mf);
-       wait_event(mpt_waitq, pCfg->wait_done);
-
-       /* mf has been freed - do not access */
+       timeleft = wait_for_completion_timeout(&ioc->mptbase_cmds.done,
+               timeout);
+       if (!(ioc->mptbase_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+               ret = -ETIME;
+               dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "Failed Sending Config request type 0x%x, page 0x%x,"
+                   " action %d, status %xh, time left %ld\n\n",
+                       ioc->name, page_type, pReq->Header.PageNumber,
+                       pReq->Action, ioc->mptbase_cmds.status, timeleft));
+               if (ioc->mptbase_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET)
+                       goto out;
+               if (!timeleft)
+                       issue_hard_reset = 1;
+               goto out;
+       }
 
-       rc = pCfg->status;
+       if (!(ioc->mptbase_cmds.status & MPT_MGMT_STATUS_RF_VALID)) {
+               ret = -1;
+               goto out;
+       }
+       pReply = (ConfigReply_t *)ioc->mptbase_cmds.reply;
+       ret = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK;
+       if (ret == MPI_IOCSTATUS_SUCCESS) {
+               if (extend_page) {
+                       pCfg->cfghdr.ehdr->ExtPageLength =
+                           le16_to_cpu(pReply->ExtPageLength);
+                       pCfg->cfghdr.ehdr->ExtPageType =
+                           pReply->ExtPageType;
+               }
+               pCfg->cfghdr.hdr->PageVersion = pReply->Header.PageVersion;
+               pCfg->cfghdr.hdr->PageLength = pReply->Header.PageLength;
+               pCfg->cfghdr.hdr->PageNumber = pReply->Header.PageNumber;
+               pCfg->cfghdr.hdr->PageType = pReply->Header.PageType;
 
-       return rc;
-}
+       }
 
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- *     mpt_timer_expired - Callback for timer process.
- *     Used only internal config functionality.
- *     @data: Pointer to MPT_SCSI_HOST recast as an unsigned long
- */
-static void
-mpt_timer_expired(unsigned long data)
-{
-       MPT_ADAPTER *ioc = (MPT_ADAPTER *) data;
+       if (retry_count)
+               printk(MYIOC_s_INFO_FMT "Retry completed "
+                   "ret=0x%x timeleft=%ld\n",
+                   ioc->name, ret, timeleft);
 
-       dcprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mpt_timer_expired! \n", ioc->name));
+       dcprintk(ioc, printk(KERN_DEBUG "IOCStatus=%04xh, IOCLogInfo=%08xh\n",
+            ret, le32_to_cpu(pReply->IOCLogInfo)));
 
-       /* Perform a FW reload */
-       if (mpt_HardResetHandler(ioc, NO_SLEEP) < 0)
-               printk(MYIOC_s_WARN_FMT "Firmware Reload FAILED!\n", ioc->name);
+out:
 
-       /* No more processing.
-        * Hard reset clean-up will wake up
-        * process and free all resources.
-        */
-       dcprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mpt_timer_expired complete!\n", ioc->name));
+       CLEAR_MGMT_STATUS(ioc->mptbase_cmds.status)
+       mutex_unlock(&ioc->mptbase_cmds.mutex);
+       if (issue_hard_reset) {
+               issue_hard_reset = 0;
+               printk(MYIOC_s_WARN_FMT "Issuing Reset from %s!!\n",
+                   ioc->name, __func__);
+               mpt_HardResetHandler(ioc, CAN_SLEEP);
+               mpt_free_msg_frame(ioc, mf);
+               /* attempt one retry for a timed out command */
+               if (!retry_count) {
+                       printk(MYIOC_s_INFO_FMT
+                           "Attempting Retry Config request"
+                           " type 0x%x, page 0x%x,"
+                           " action %d\n", ioc->name, page_type,
+                           pCfg->cfghdr.hdr->PageNumber, pCfg->action);
+                       retry_count++;
+                       goto retry_config;
+               }
+       }
+       return ret;
 
-       return;
 }
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -5998,41 +6513,34 @@ mpt_timer_expired(unsigned long data)
 static int
 mpt_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
 {
-       CONFIGPARMS *pCfg;
-       unsigned long flags;
-
-       dprintk(ioc, printk(MYIOC_s_DEBUG_FMT
-           ": IOC %s_reset routed to MPT base driver!\n",
-           ioc->name, reset_phase==MPT_IOC_SETUP_RESET ? "setup" : (
-           reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post")));
-
-       if (reset_phase == MPT_IOC_SETUP_RESET) {
-               ;
-       } else if (reset_phase == MPT_IOC_PRE_RESET) {
-               /* If the internal config Q is not empty -
-                * delete timer. MF resources will be freed when
-                * the FIFO's are primed.
-                */
-               spin_lock_irqsave(&ioc->FreeQlock, flags);
-               list_for_each_entry(pCfg, &ioc->configQ, linkage)
-                       del_timer(&pCfg->timer);
-               spin_unlock_irqrestore(&ioc->FreeQlock, flags);
-
-       } else {
-               CONFIGPARMS *pNext;
-
-               /* Search the configQ for internal commands.
-                * Flush the Q, and wake up all suspended threads.
-                */
-               spin_lock_irqsave(&ioc->FreeQlock, flags);
-               list_for_each_entry_safe(pCfg, pNext, &ioc->configQ, linkage) {
-                       list_del(&pCfg->linkage);
-
-                       pCfg->status = MPT_CONFIG_ERROR;
-                       pCfg->wait_done = 1;
-                       wake_up(&mpt_waitq);
+       switch (reset_phase) {
+       case MPT_IOC_SETUP_RESET:
+               ioc->taskmgmt_quiesce_io = 1;
+               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "%s: MPT_IOC_SETUP_RESET\n", ioc->name, __func__));
+               break;
+       case MPT_IOC_PRE_RESET:
+               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "%s: MPT_IOC_PRE_RESET\n", ioc->name, __func__));
+               break;
+       case MPT_IOC_POST_RESET:
+               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "%s: MPT_IOC_POST_RESET\n",  ioc->name, __func__));
+/* wake up mptbase_cmds */
+               if (ioc->mptbase_cmds.status & MPT_MGMT_STATUS_PENDING) {
+                       ioc->mptbase_cmds.status |=
+                           MPT_MGMT_STATUS_DID_IOCRESET;
+                       complete(&ioc->mptbase_cmds.done);
                }
-               spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+/* wake up taskmgmt_cmds */
+               if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_PENDING) {
+                       ioc->taskmgmt_cmds.status |=
+                               MPT_MGMT_STATUS_DID_IOCRESET;
+                       complete(&ioc->taskmgmt_cmds.done);
+               }
+               break;
+       default:
+               break;
        }
 
        return 1;               /* currently means nothing really */
@@ -6344,6 +6852,59 @@ mpt_print_ioc_summary(MPT_ADAPTER *ioc, char *buffer, int *size, int len, int sh
 
        *size = y;
 }
+/**
+ *     mpt_set_taskmgmt_in_progress_flag - set flags associated with task managment
+ *     @ioc: Pointer to MPT_ADAPTER structure
+ *
+ *     Returns 0 for SUCCESS or -1 if FAILED.
+ *
+ *     If -1 is return, then it was not possible to set the flags
+ **/
+int
+mpt_set_taskmgmt_in_progress_flag(MPT_ADAPTER *ioc)
+{
+       unsigned long    flags;
+       int              retval;
+
+       spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+       if (ioc->ioc_reset_in_progress || ioc->taskmgmt_in_progress ||
+           (ioc->alt_ioc && ioc->alt_ioc->taskmgmt_in_progress)) {
+               retval = -1;
+               goto out;
+       }
+       retval = 0;
+       ioc->taskmgmt_in_progress = 1;
+       ioc->taskmgmt_quiesce_io = 1;
+       if (ioc->alt_ioc) {
+               ioc->alt_ioc->taskmgmt_in_progress = 1;
+               ioc->alt_ioc->taskmgmt_quiesce_io = 1;
+       }
+ out:
+       spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+       return retval;
+}
+EXPORT_SYMBOL(mpt_set_taskmgmt_in_progress_flag);
+
+/**
+ *     mpt_clear_taskmgmt_in_progress_flag - clear flags associated with task managment
+ *     @ioc: Pointer to MPT_ADAPTER structure
+ *
+ **/
+void
+mpt_clear_taskmgmt_in_progress_flag(MPT_ADAPTER *ioc)
+{
+       unsigned long    flags;
+
+       spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+       ioc->taskmgmt_in_progress = 0;
+       ioc->taskmgmt_quiesce_io = 0;
+       if (ioc->alt_ioc) {
+               ioc->alt_ioc->taskmgmt_in_progress = 0;
+               ioc->alt_ioc->taskmgmt_quiesce_io = 0;
+       }
+       spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+}
+EXPORT_SYMBOL(mpt_clear_taskmgmt_in_progress_flag);
 
 
 /**
@@ -6397,7 +6958,9 @@ int
 mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag)
 {
        int              rc;
+       u8       cb_idx;
        unsigned long    flags;
+       unsigned long    time_count;
 
        dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "HardResetHandler Entered!\n", ioc->name));
 #ifdef MFCNT
@@ -6410,14 +6973,15 @@ mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag)
        /* Reset the adapter. Prevent more than 1 call to
         * mpt_do_ioc_recovery at any instant in time.
         */
-       spin_lock_irqsave(&ioc->diagLock, flags);
-       if ((ioc->diagPending) || (ioc->alt_ioc && ioc->alt_ioc->diagPending)){
-               spin_unlock_irqrestore(&ioc->diagLock, flags);
+       spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+       if (ioc->ioc_reset_in_progress) {
+               spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
                return 0;
-       } else {
-               ioc->diagPending = 1;
        }
-       spin_unlock_irqrestore(&ioc->diagLock, flags);
+       ioc->ioc_reset_in_progress = 1;
+       if (ioc->alt_ioc)
+               ioc->alt_ioc->ioc_reset_in_progress = 1;
+       spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
 
        /* FIXME: If do_ioc_recovery fails, repeat....
         */
@@ -6427,47 +6991,57 @@ mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag)
         * Prevents timeouts occurring during a diagnostic reset...very bad.
         * For all other protocol drivers, this is a no-op.
         */
-       {
-               u8       cb_idx;
-               int      r = 0;
-
-               for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) {
-                       if (MptResetHandlers[cb_idx]) {
-                               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Calling IOC reset_setup handler #%d\n",
-                                               ioc->name, cb_idx));
-                               r += mpt_signal_reset(cb_idx, ioc, MPT_IOC_SETUP_RESET);
-                               if (ioc->alt_ioc) {
-                                       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Calling alt-%s setup reset handler #%d\n",
-                                                       ioc->name, ioc->alt_ioc->name, cb_idx));
-                                       r += mpt_signal_reset(cb_idx, ioc->alt_ioc, MPT_IOC_SETUP_RESET);
-                               }
-                       }
+       for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) {
+               if (MptResetHandlers[cb_idx]) {
+                       mpt_signal_reset(cb_idx, ioc, MPT_IOC_SETUP_RESET);
+                       if (ioc->alt_ioc)
+                               mpt_signal_reset(cb_idx, ioc->alt_ioc,
+                                       MPT_IOC_SETUP_RESET);
                }
        }
 
-       if ((rc = mpt_do_ioc_recovery(ioc, MPT_HOSTEVENT_IOC_RECOVER, sleepFlag)) != 0) {
-               printk(MYIOC_s_WARN_FMT "Cannot recover rc = %d!\n", ioc->name, rc);
+       time_count = jiffies;
+       rc = mpt_do_ioc_recovery(ioc, MPT_HOSTEVENT_IOC_RECOVER, sleepFlag);
+       if (rc != 0) {
+               printk(KERN_WARNING MYNAM
+                   ": WARNING - (%d) Cannot recover %s\n", rc, ioc->name);
+       } else {
+               if (ioc->hard_resets < -1)
+                       ioc->hard_resets++;
        }
-       ioc->reload_fw = 0;
-       if (ioc->alt_ioc)
-               ioc->alt_ioc->reload_fw = 0;
 
-       spin_lock_irqsave(&ioc->diagLock, flags);
-       ioc->diagPending = 0;
-       if (ioc->alt_ioc)
-               ioc->alt_ioc->diagPending = 0;
-       spin_unlock_irqrestore(&ioc->diagLock, flags);
+       spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+       ioc->ioc_reset_in_progress = 0;
+       ioc->taskmgmt_quiesce_io = 0;
+       ioc->taskmgmt_in_progress = 0;
+       if (ioc->alt_ioc) {
+               ioc->alt_ioc->ioc_reset_in_progress = 0;
+               ioc->alt_ioc->taskmgmt_quiesce_io = 0;
+               ioc->alt_ioc->taskmgmt_in_progress = 0;
+       }
+       spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
 
-       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "HardResetHandler rc = %d!\n", ioc->name, rc));
+       dtmprintk(ioc,
+           printk(MYIOC_s_DEBUG_FMT
+               "HardResetHandler: completed (%d seconds): %s\n", ioc->name,
+               jiffies_to_msecs(jiffies - time_count)/1000, ((rc == 0) ?
+               "SUCCESS" : "FAILED")));
 
        return rc;
 }
 
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+#ifdef CONFIG_FUSION_LOGGING
 static void
-EventDescriptionStr(u8 event, u32 evData0, char *evStr)
+mpt_display_event_info(MPT_ADAPTER *ioc, EventNotificationReply_t *pEventReply)
 {
        char *ds = NULL;
+       u32 evData0;
+       int ii;
+       u8 event;
+       char *evStr = ioc->evStr;
+
+       event = le32_to_cpu(pEventReply->Event) & 0xFF;
+       evData0 = le32_to_cpu(pEventReply->Data[0]);
 
        switch(event) {
        case MPI_EVENT_NONE:
@@ -6501,9 +7075,9 @@ EventDescriptionStr(u8 event, u32 evData0, char *evStr)
                if (evData0 == MPI_EVENT_LOOP_STATE_CHANGE_LIP)
                        ds = "Loop State(LIP) Change";
                else if (evData0 == MPI_EVENT_LOOP_STATE_CHANGE_LPE)
-                       ds = "Loop State(LPE) Change";          /* ??? */
+                       ds = "Loop State(LPE) Change";
                else
-                       ds = "Loop State(LPB) Change";          /* ??? */
+                       ds = "Loop State(LPB) Change";
                break;
        case MPI_EVENT_LOGOUT:
                ds = "Logout";
@@ -6703,28 +7277,65 @@ EventDescriptionStr(u8 event, u32 evData0, char *evStr)
        }
        case MPI_EVENT_IR2:
        {
+               u8 id = (u8)(evData0);
+               u8 channel = (u8)(evData0 >> 8);
+               u8 phys_num = (u8)(evData0 >> 24);
                u8 ReasonCode = (u8)(evData0 >> 16);
+
                switch (ReasonCode) {
                case MPI_EVENT_IR2_RC_LD_STATE_CHANGED:
-                       ds = "IR2: LD State Changed";
+                       snprintf(evStr, EVENT_DESCR_STR_SZ,
+                           "IR2: LD State Changed: "
+                           "id=%d channel=%d phys_num=%d",
+                           id, channel, phys_num);
                        break;
                case MPI_EVENT_IR2_RC_PD_STATE_CHANGED:
-                       ds = "IR2: PD State Changed";
+                       snprintf(evStr, EVENT_DESCR_STR_SZ,
+                           "IR2: PD State Changed "
+                           "id=%d channel=%d phys_num=%d",
+                           id, channel, phys_num);
                        break;
                case MPI_EVENT_IR2_RC_BAD_BLOCK_TABLE_FULL:
-                       ds = "IR2: Bad Block Table Full";
+                       snprintf(evStr, EVENT_DESCR_STR_SZ,
+                           "IR2: Bad Block Table Full: "
+                           "id=%d channel=%d phys_num=%d",
+                           id, channel, phys_num);
                        break;
                case MPI_EVENT_IR2_RC_PD_INSERTED:
-                       ds = "IR2: PD Inserted";
+                       snprintf(evStr, EVENT_DESCR_STR_SZ,
+                           "IR2: PD Inserted: "
+                           "id=%d channel=%d phys_num=%d",
+                           id, channel, phys_num);
                        break;
                case MPI_EVENT_IR2_RC_PD_REMOVED:
-                       ds = "IR2: PD Removed";
+                       snprintf(evStr, EVENT_DESCR_STR_SZ,
+                           "IR2: PD Removed: "
+                           "id=%d channel=%d phys_num=%d",
+                           id, channel, phys_num);
                        break;
                case MPI_EVENT_IR2_RC_FOREIGN_CFG_DETECTED:
-                       ds = "IR2: Foreign CFG Detected";
+                       snprintf(evStr, EVENT_DESCR_STR_SZ,
+                           "IR2: Foreign CFG Detected: "
+                           "id=%d channel=%d phys_num=%d",
+                           id, channel, phys_num);
                        break;
                case MPI_EVENT_IR2_RC_REBUILD_MEDIUM_ERROR:
-                       ds = "IR2: Rebuild Medium Error";
+                       snprintf(evStr, EVENT_DESCR_STR_SZ,
+                           "IR2: Rebuild Medium Error: "
+                           "id=%d channel=%d phys_num=%d",
+                           id, channel, phys_num);
+                       break;
+               case MPI_EVENT_IR2_RC_DUAL_PORT_ADDED:
+                       snprintf(evStr, EVENT_DESCR_STR_SZ,
+                           "IR2: Dual Port Added: "
+                           "id=%d channel=%d phys_num=%d",
+                           id, channel, phys_num);
+                       break;
+               case MPI_EVENT_IR2_RC_DUAL_PORT_REMOVED:
+                       snprintf(evStr, EVENT_DESCR_STR_SZ,
+                           "IR2: Dual Port Removed: "
+                           "id=%d channel=%d phys_num=%d",
+                           id, channel, phys_num);
                        break;
                default:
                        ds = "IR2";
@@ -6760,13 +7371,18 @@ EventDescriptionStr(u8 event, u32 evData0, char *evStr)
        case MPI_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE:
        {
                u8 reason = (u8)(evData0);
-               u8 port_num = (u8)(evData0 >> 8);
-               u16 handle = le16_to_cpu(evData0 >> 16);
 
-               snprintf(evStr, EVENT_DESCR_STR_SZ,
-                   "SAS Initiator Device Status Change: reason=0x%02x "
-                   "port=%d handle=0x%04x",
-                   reason, port_num, handle);
+               switch (reason) {
+               case MPI_EVENT_SAS_INIT_RC_ADDED:
+                       ds = "SAS Initiator Status Change: Added";
+                       break;
+               case MPI_EVENT_SAS_INIT_RC_REMOVED:
+                       ds = "SAS Initiator Status Change: Deleted";
+                       break;
+               default:
+                       ds = "SAS Initiator Status Change";
+                       break;
+               }
                break;
        }
 
@@ -6814,6 +7430,24 @@ EventDescriptionStr(u8 event, u32 evData0, char *evStr)
                break;
        }
 
+       case MPI_EVENT_SAS_EXPANDER_STATUS_CHANGE:
+       {
+               u8 reason = (u8)(evData0);
+
+               switch (reason) {
+               case MPI_EVENT_SAS_EXP_RC_ADDED:
+                       ds = "Expander Status Change: Added";
+                       break;
+               case MPI_EVENT_SAS_EXP_RC_NOT_RESPONDING:
+                       ds = "Expander Status Change: Deleted";
+                       break;
+               default:
+                       ds = "Expander Status Change";
+                       break;
+               }
+               break;
+       }
+
        /*
         *  MPT base "custom" events may be added here...
         */
@@ -6823,8 +7457,20 @@ EventDescriptionStr(u8 event, u32 evData0, char *evStr)
        }
        if (ds)
                strncpy(evStr, ds, EVENT_DESCR_STR_SZ);
-}
 
+
+       devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+           "MPT event:(%02Xh) : %s\n",
+           ioc->name, event, evStr));
+
+       devtverboseprintk(ioc, printk(KERN_DEBUG MYNAM
+           ": Event data:\n"));
+       for (ii = 0; ii < le16_to_cpu(pEventReply->EventDataLength); ii++)
+               devtverboseprintk(ioc, printk(" %08x",
+                   le32_to_cpu(pEventReply->Data[ii])));
+       devtverboseprintk(ioc, printk(KERN_DEBUG "\n"));
+}
+#endif
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /**
  *     ProcessEventNotification - Route EventNotificationReply to all event handlers
@@ -6841,37 +7487,24 @@ ProcessEventNotification(MPT_ADAPTER *ioc, EventNotificationReply_t *pEventReply
 {
        u16 evDataLen;
        u32 evData0 = 0;
-//     u32 evCtx;
        int ii;
        u8 cb_idx;
        int r = 0;
        int handlers = 0;
-       char evStr[EVENT_DESCR_STR_SZ];
        u8 event;
 
        /*
         *  Do platform normalization of values
         */
        event = le32_to_cpu(pEventReply->Event) & 0xFF;
-//     evCtx = le32_to_cpu(pEventReply->EventContext);
        evDataLen = le16_to_cpu(pEventReply->EventDataLength);
        if (evDataLen) {
                evData0 = le32_to_cpu(pEventReply->Data[0]);
        }
 
-       EventDescriptionStr(event, evData0, evStr);
-       devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "MPT event:(%02Xh) : %s\n",
-                       ioc->name,
-                       event,
-                       evStr));
-
 #ifdef CONFIG_FUSION_LOGGING
-       devtverboseprintk(ioc, printk(MYIOC_s_DEBUG_FMT
-           ": Event data:\n", ioc->name));
-       for (ii = 0; ii < evDataLen; ii++)
-               devtverboseprintk(ioc, printk(" %08x",
-                   le32_to_cpu(pEventReply->Data[ii])));
-       devtverboseprintk(ioc, printk("\n"));
+       if (evDataLen)
+               mpt_display_event_info(ioc, pEventReply);
 #endif
 
        /*
@@ -6926,8 +7559,9 @@ ProcessEventNotification(MPT_ADAPTER *ioc, EventNotificationReply_t *pEventReply
         */
        for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) {
                if (MptEvHandlers[cb_idx]) {
-                       devtverboseprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Routing Event to event handler #%d\n",
-                                       ioc->name, cb_idx));
+                       devtverboseprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                           "Routing Event to event handler #%d\n",
+                           ioc->name, cb_idx));
                        r += (*(MptEvHandlers[cb_idx]))(ioc, pEventReply);
                        handlers++;
                }
@@ -7011,8 +7645,6 @@ mpt_spi_log_info(MPT_ADAPTER *ioc, u32 log_info)
        switch (info) {
        case 0x00010000:
                desc = "bug! MID not found";
-               if (ioc->reload_fw == 0)
-                       ioc->reload_fw++;
                break;
 
        case 0x00020000:
@@ -7613,7 +8245,6 @@ EXPORT_SYMBOL(mpt_get_msg_frame);
 EXPORT_SYMBOL(mpt_put_msg_frame);
 EXPORT_SYMBOL(mpt_put_msg_frame_hi_pri);
 EXPORT_SYMBOL(mpt_free_msg_frame);
-EXPORT_SYMBOL(mpt_add_sge);
 EXPORT_SYMBOL(mpt_send_handshake_request);
 EXPORT_SYMBOL(mpt_verify_adapter);
 EXPORT_SYMBOL(mpt_GetIocState);
@@ -7650,7 +8281,7 @@ fusion_init(void)
        /*  Register ourselves (mptbase) in order to facilitate
         *  EventNotification handling.
         */
-       mpt_base_index = mpt_register(mpt_base_reply, MPTBASE_DRIVER);
+       mpt_base_index = mpt_register(mptbase_reply, MPTBASE_DRIVER);
 
        /* Register for hard reset handling callbacks.
         */
index b3e981d2a506fd6aeabd27795e7b492eb466344a..1c8514dc31ca3ec51df54ca2a9711f6fb085b02f 100644 (file)
@@ -76,8 +76,8 @@
 #define COPYRIGHT      "Copyright (c) 1999-2008 " MODULEAUTHOR
 #endif
 
-#define MPT_LINUX_VERSION_COMMON       "3.04.07"
-#define MPT_LINUX_PACKAGE_NAME         "@(#)mptlinux-3.04.07"
+#define MPT_LINUX_VERSION_COMMON       "3.04.10"
+#define MPT_LINUX_PACKAGE_NAME         "@(#)mptlinux-3.04.09"
 #define WHAT_MAGIC_STRING              "@" "(" "#" ")"
 
 #define show_mptmod_ver(s,ver)  \
 #endif
 
 #define MPT_NAME_LENGTH                        32
+#define MPT_KOBJ_NAME_LEN              20
 
 #define MPT_PROCFS_MPTBASEDIR          "mpt"
                                                /* chg it to "driver/fusion" ? */
 
 #define MPT_COALESCING_TIMEOUT         0x10
 
+
 /*
  * SCSI transfer rate defines.
  */
 /*
  * Set the MAX_SGE value based on user input.
  */
-#ifdef  CONFIG_FUSION_MAX_SGE
-#if     CONFIG_FUSION_MAX_SGE  < 16
+#ifdef CONFIG_FUSION_MAX_SGE
+#if CONFIG_FUSION_MAX_SGE  < 16
 #define MPT_SCSI_SG_DEPTH      16
-#elif   CONFIG_FUSION_MAX_SGE  > 128
+#elif CONFIG_FUSION_MAX_SGE  > 128
 #define MPT_SCSI_SG_DEPTH      128
 #else
 #define MPT_SCSI_SG_DEPTH      CONFIG_FUSION_MAX_SGE
 #define MPT_SCSI_SG_DEPTH      40
 #endif
 
+#ifdef CONFIG_FUSION_MAX_FC_SGE
+#if CONFIG_FUSION_MAX_FC_SGE  < 16
+#define MPT_SCSI_FC_SG_DEPTH   16
+#elif CONFIG_FUSION_MAX_FC_SGE  > 256
+#define MPT_SCSI_FC_SG_DEPTH   256
+#else
+#define MPT_SCSI_FC_SG_DEPTH   CONFIG_FUSION_MAX_FC_SGE
+#endif
+#else
+#define MPT_SCSI_FC_SG_DEPTH   40
+#endif
+
 /* debug print string length used for events and iocstatus */
 # define EVENT_DESCR_STR_SZ             100
 
@@ -431,38 +445,36 @@ do { \
  *     IOCTL structure and associated defines
  */
 
-#define MPT_IOCTL_STATUS_DID_IOCRESET  0x01    /* IOC Reset occurred on the current*/
-#define MPT_IOCTL_STATUS_RF_VALID      0x02    /* The Reply Frame is VALID */
-#define MPT_IOCTL_STATUS_TIMER_ACTIVE  0x04    /* The timer is running */
-#define MPT_IOCTL_STATUS_SENSE_VALID   0x08    /* Sense data is valid */
-#define MPT_IOCTL_STATUS_COMMAND_GOOD  0x10    /* Command Status GOOD */
-#define MPT_IOCTL_STATUS_TMTIMER_ACTIVE        0x20    /* The TM timer is running */
-#define MPT_IOCTL_STATUS_TM_FAILED     0x40    /* User TM request failed */
-
 #define MPTCTL_RESET_OK                        0x01    /* Issue Bus Reset */
 
-typedef struct _MPT_IOCTL {
-       struct _MPT_ADAPTER     *ioc;
-       u8                       ReplyFrame[MPT_DEFAULT_FRAME_SIZE];    /* reply frame data */
-       u8                       sense[MPT_SENSE_BUFFER_ALLOC];
-       int                      wait_done;     /* wake-up value for this ioc */
-       u8                       rsvd;
-       u8                       status;        /* current command status */
-       u8                       reset;         /* 1 if bus reset allowed */
-       u8                       id;            /* target for reset */
-       struct mutex             ioctl_mutex;
-} MPT_IOCTL;
-
-#define MPT_SAS_MGMT_STATUS_RF_VALID   0x02    /* The Reply Frame is VALID */
-#define MPT_SAS_MGMT_STATUS_COMMAND_GOOD       0x10    /* Command Status GOOD */
-#define MPT_SAS_MGMT_STATUS_TM_FAILED  0x40    /* User TM request failed */
-
-typedef struct _MPT_SAS_MGMT {
+#define MPT_MGMT_STATUS_RF_VALID       0x01    /* The Reply Frame is VALID */
+#define MPT_MGMT_STATUS_COMMAND_GOOD   0x02    /* Command Status GOOD */
+#define MPT_MGMT_STATUS_PENDING                0x04    /* command is pending */
+#define MPT_MGMT_STATUS_DID_IOCRESET   0x08    /* IOC Reset occurred
+                                                  on the current*/
+#define MPT_MGMT_STATUS_SENSE_VALID    0x10    /* valid sense info */
+#define MPT_MGMT_STATUS_TIMER_ACTIVE   0x20    /* obsolete */
+#define MPT_MGMT_STATUS_FREE_MF                0x40    /* free the mf from
+                                                  complete routine */
+
+#define INITIALIZE_MGMT_STATUS(status) \
+       status = MPT_MGMT_STATUS_PENDING;
+#define CLEAR_MGMT_STATUS(status) \
+       status = 0;
+#define CLEAR_MGMT_PENDING_STATUS(status) \
+       status &= ~MPT_MGMT_STATUS_PENDING;
+#define SET_MGMT_MSG_CONTEXT(msg_context, value) \
+       msg_context = value;
+
+typedef struct _MPT_MGMT {
        struct mutex             mutex;
        struct completion        done;
        u8                       reply[MPT_DEFAULT_FRAME_SIZE]; /* reply frame data */
+       u8                       sense[MPT_SENSE_BUFFER_ALLOC];
        u8                       status;        /* current command status */
-}MPT_SAS_MGMT;
+       int                      completion_code;
+       u32                      msg_context;
+} MPT_MGMT;
 
 /*
  *  Event Structure and define
@@ -564,6 +576,10 @@ struct mptfc_rport_info
        u8              flags;
 };
 
+typedef void (*MPT_ADD_SGE)(void *pAddr, u32 flagslength, dma_addr_t dma_addr);
+typedef void (*MPT_ADD_CHAIN)(void *pAddr, u8 next, u16 length,
+               dma_addr_t dma_addr);
+
 /*
  *  Adapter Structure - pci_dev specific. Maximum: MPT_MAX_ADAPTERS
  */
@@ -573,6 +589,10 @@ typedef struct _MPT_ADAPTER
        int                      pci_irq;       /* This irq           */
        char                     name[MPT_NAME_LENGTH]; /* "iocN"             */
        char                     prod_name[MPT_NAME_LENGTH];    /* "LSIFC9x9"         */
+#ifdef CONFIG_FUSION_LOGGING
+       /* used in mpt_display_event_info */
+       char                     evStr[EVENT_DESCR_STR_SZ];
+#endif
        char                     board_name[16];
        char                     board_assembly[16];
        char                     board_tracer[16];
@@ -600,6 +620,10 @@ typedef struct _MPT_ADAPTER
        int                      reply_depth;   /* Num Allocated reply frames */
        int                      reply_sz;      /* Reply frame size */
        int                      num_chain;     /* Number of chain buffers */
+       MPT_ADD_SGE              add_sge;       /* Pointer to add_sge
+                                                  function */
+       MPT_ADD_CHAIN            add_chain;     /* Pointer to add_chain
+                                                  function */
                /* Pool of buffers for chaining. ReqToChain
                 * and ChainToChain track index of chain buffers.
                 * ChainBuffer (DMA) virt/phys addresses.
@@ -640,11 +664,8 @@ typedef struct _MPT_ADAPTER
        RaidCfgData             raid_data;      /* Raid config. data */
        SasCfgData              sas_data;       /* Sas config. data */
        FcCfgData               fc_data;        /* Fc config. data */
-       MPT_IOCTL               *ioctl;         /* ioctl data pointer */
        struct proc_dir_entry   *ioc_dentry;
        struct _MPT_ADAPTER     *alt_ioc;       /* ptr to 929 bound adapter port */
-       spinlock_t               diagLock;      /* diagnostic reset lock */
-       int                      diagPending;
        u32                      biosVersion;   /* BIOS version from IO Unit Page 2 */
        int                      eventTypes;    /* Event logging parameters */
        int                      eventContext;  /* Next event context */
@@ -652,7 +673,6 @@ typedef struct _MPT_ADAPTER
        struct _mpt_ioctl_events *events;       /* pointer to event log */
        u8                      *cached_fw;     /* Pointer to FW */
        dma_addr_t              cached_fw_dma;
-       struct list_head         configQ;       /* linked list of config. requests */
        int                      hs_reply_idx;
 #ifndef MFCNT
        u32                      pad0;
@@ -665,9 +685,6 @@ typedef struct _MPT_ADAPTER
        IOCFactsReply_t          facts;
        PortFactsReply_t         pfacts[2];
        FCPortPage0_t            fc_port_page0[2];
-       struct timer_list        persist_timer; /* persist table timer */
-       int                      persist_wait_done; /* persist completion flag */
-       u8                       persist_reply_frame[MPT_DEFAULT_FRAME_SIZE]; /* persist reply */
        LANPage0_t               lan_cnfg_page0;
        LANPage1_t               lan_cnfg_page1;
 
@@ -682,23 +699,44 @@ typedef struct _MPT_ADAPTER
        int                      aen_event_read_flag; /* flag to indicate event log was read*/
        u8                       FirstWhoInit;
        u8                       upload_fw;     /* If set, do a fw upload */
-       u8                       reload_fw;     /* Force a FW Reload on next reset */
        u8                       NBShiftFactor;  /* NB Shift Factor based on Block Size (Facts)  */
        u8                       pad1[4];
        u8                       DoneCtx;
        u8                       TaskCtx;
        u8                       InternalCtx;
-       spinlock_t               initializing_hba_lock;
-       int                      initializing_hba_lock_flag;
        struct list_head         list;
        struct net_device       *netdev;
        struct list_head         sas_topology;
        struct mutex             sas_topology_mutex;
+
+       struct workqueue_struct *fw_event_q;
+       struct list_head         fw_event_list;
+       spinlock_t               fw_event_lock;
+       u8                       fw_events_off; /* if '1', then ignore events */
+       char                     fw_event_q_name[MPT_KOBJ_NAME_LEN];
+
        struct mutex             sas_discovery_mutex;
        u8                       sas_discovery_runtime;
        u8                       sas_discovery_ignore_events;
+
+       /* port_info object for the host */
+       struct mptsas_portinfo  *hba_port_info;
+       u64                      hba_port_sas_addr;
+       u16                      hba_port_num_phy;
+       struct list_head         sas_device_info_list;
+       struct mutex             sas_device_info_mutex;
+       u8                       old_sas_discovery_protocal;
+       u8                       sas_discovery_quiesce_io;
        int                      sas_index; /* index refrencing */
-       MPT_SAS_MGMT             sas_mgmt;
+       MPT_MGMT                 sas_mgmt;
+       MPT_MGMT                 mptbase_cmds; /* for sending config pages */
+       MPT_MGMT                 internal_cmds;
+       MPT_MGMT                 taskmgmt_cmds;
+       MPT_MGMT                 ioctl_cmds;
+       spinlock_t               taskmgmt_lock; /* diagnostic reset lock */
+       int                      taskmgmt_in_progress;
+       u8                       taskmgmt_quiesce_io;
+       u8                       ioc_reset_in_progress;
        struct work_struct       sas_persist_task;
 
        struct work_struct       fc_setup_reset_work;
@@ -707,15 +745,27 @@ typedef struct _MPT_ADAPTER
        u8                       fc_link_speed[2];
        spinlock_t               fc_rescan_work_lock;
        struct work_struct       fc_rescan_work;
-       char                     fc_rescan_work_q_name[20];
+       char                     fc_rescan_work_q_name[MPT_KOBJ_NAME_LEN];
        struct workqueue_struct *fc_rescan_work_q;
+
+       /* driver forced bus resets count */
+       unsigned long             hard_resets;
+       /* fw/external bus resets count */
+       unsigned long             soft_resets;
+       /* cmd timeouts */
+       unsigned long             timeouts;
+
        struct scsi_cmnd        **ScsiLookup;
        spinlock_t                scsi_lookup_lock;
-
-       char                     reset_work_q_name[20];
+       u64                     dma_mask;
+       u32                       broadcast_aen_busy;
+       char                     reset_work_q_name[MPT_KOBJ_NAME_LEN];
        struct workqueue_struct *reset_work_q;
        struct delayed_work      fault_reset_work;
-       spinlock_t               fault_reset_work_lock;
+
+       u8                      sg_addr_size;
+       u8                      in_rescan;
+       u8                      SGE_size;
 
 } MPT_ADAPTER;
 
@@ -753,13 +803,14 @@ typedef struct _mpt_sge {
        dma_addr_t      Address;
 } MptSge_t;
 
-#define mpt_addr_size() \
-       ((sizeof(dma_addr_t) == sizeof(u64)) ? MPI_SGE_FLAGS_64_BIT_ADDRESSING : \
-               MPI_SGE_FLAGS_32_BIT_ADDRESSING)
 
-#define mpt_msg_flags() \
-       ((sizeof(dma_addr_t) == sizeof(u64)) ? MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_64 : \
-               MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_32)
+#define mpt_msg_flags(ioc) \
+       (ioc->sg_addr_size == sizeof(u64)) ?            \
+       MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_64 :             \
+       MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_32
+
+#define MPT_SGE_FLAGS_64_BIT_ADDRESSING \
+       (MPI_SGE_FLAGS_64_BIT_ADDRESSING << MPI_SGE_FLAGS_SHIFT)
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /*
@@ -835,22 +886,14 @@ typedef struct _MPT_SCSI_HOST {
                /* Pool of memory for holding SCpnts before doing
                 * OS callbacks. freeQ is the free pool.
                 */
-       u8                        tmPending;
-       u8                        resetPending;
        u8                        negoNvram;            /* DV disabled, nego NVRAM */
        u8                        pad1;
-       u8                        tmState;
        u8                        rsvd[2];
        MPT_FRAME_HDR            *cmdPtr;               /* Ptr to nonOS request */
        struct scsi_cmnd         *abortSCpnt;
        MPT_LOCAL_REPLY           localReply;           /* internal cmd reply struct */
-       unsigned long             hard_resets;          /* driver forced bus resets count */
-       unsigned long             soft_resets;          /* fw/external bus resets count */
-       unsigned long             timeouts;             /* cmd timeouts */
        ushort                    sel_timeout[MPT_MAX_FC_DEVICES];
        char                      *info_kbuf;
-       wait_queue_head_t         scandv_waitq;
-       int                       scandv_wait_done;
        long                      last_queue_full;
        u16                       tm_iocstatus;
        u16                       spi_pending;
@@ -870,21 +913,16 @@ struct scsi_cmnd;
  * Generic structure passed to the base mpt_config function.
  */
 typedef struct _x_config_parms {
-       struct list_head         linkage;       /* linked list */
-       struct timer_list        timer;         /* timer function for this request  */
        union {
                ConfigExtendedPageHeader_t      *ehdr;
                ConfigPageHeader_t      *hdr;
        } cfghdr;
        dma_addr_t               physAddr;
-       int                      wait_done;     /* wait for this request */
        u32                      pageAddr;      /* properly formatted */
+       u16                      status;
        u8                       action;
        u8                       dir;
        u8                       timeout;       /* seconds */
-       u8                       pad1;
-       u16                      status;
-       u16                      pad2;
 } CONFIGPARMS;
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -909,7 +947,6 @@ extern MPT_FRAME_HDR        *mpt_get_msg_frame(u8 cb_idx, MPT_ADAPTER *ioc);
 extern void     mpt_free_msg_frame(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf);
 extern void     mpt_put_msg_frame(u8 cb_idx, MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf);
 extern void     mpt_put_msg_frame_hi_pri(u8 cb_idx, MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf);
-extern void     mpt_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr);
 
 extern int      mpt_send_handshake_request(u8 cb_idx, MPT_ADAPTER *ioc, int reqBytes, u32 *req, int sleepFlag);
 extern int      mpt_verify_adapter(int iocid, MPT_ADAPTER **iocpp);
@@ -922,6 +959,12 @@ extern void         mpt_free_fw_memory(MPT_ADAPTER *ioc);
 extern int      mpt_findImVolumes(MPT_ADAPTER *ioc);
 extern int      mptbase_sas_persist_operation(MPT_ADAPTER *ioc, u8 persist_opcode);
 extern int      mpt_raid_phys_disk_pg0(MPT_ADAPTER *ioc, u8 phys_disk_num, pRaidPhysDiskPage0_t phys_disk);
+extern int     mpt_raid_phys_disk_pg1(MPT_ADAPTER *ioc, u8 phys_disk_num,
+               pRaidPhysDiskPage1_t phys_disk);
+extern int     mpt_raid_phys_disk_get_num_paths(MPT_ADAPTER *ioc,
+               u8 phys_disk_num);
+extern int      mpt_set_taskmgmt_in_progress_flag(MPT_ADAPTER *ioc);
+extern void     mpt_clear_taskmgmt_in_progress_flag(MPT_ADAPTER *ioc);
 extern void     mpt_halt_firmware(MPT_ADAPTER *ioc);
 
 
@@ -959,7 +1002,6 @@ extern int mpt_fwfault_debug;
 #define MPT_SGE_FLAGS_END_OF_BUFFER            (0x40000000)
 #define MPT_SGE_FLAGS_LOCAL_ADDRESS            (0x08000000)
 #define MPT_SGE_FLAGS_DIRECTION                        (0x04000000)
-#define MPT_SGE_FLAGS_ADDRESSING               (mpt_addr_size() << MPI_SGE_FLAGS_SHIFT)
 #define MPT_SGE_FLAGS_END_OF_LIST              (0x01000000)
 
 #define MPT_SGE_FLAGS_TRANSACTION_ELEMENT      (0x00000000)
@@ -972,14 +1014,12 @@ extern int mpt_fwfault_debug;
         MPT_SGE_FLAGS_END_OF_BUFFER |  \
         MPT_SGE_FLAGS_END_OF_LIST |    \
         MPT_SGE_FLAGS_SIMPLE_ELEMENT | \
-        MPT_SGE_FLAGS_ADDRESSING | \
         MPT_TRANSFER_IOC_TO_HOST)
 #define MPT_SGE_FLAGS_SSIMPLE_WRITE \
        (MPT_SGE_FLAGS_LAST_ELEMENT |   \
         MPT_SGE_FLAGS_END_OF_BUFFER |  \
         MPT_SGE_FLAGS_END_OF_LIST |    \
         MPT_SGE_FLAGS_SIMPLE_ELEMENT | \
-        MPT_SGE_FLAGS_ADDRESSING | \
         MPT_TRANSFER_HOST_TO_IOC)
 
 /*}-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
index c63817117c0a881a3855fad1399790429b2da3cc..9b2e2198aee9dd07c12328c026fd7712bac95c03 100644 (file)
@@ -84,6 +84,7 @@ MODULE_VERSION(my_VERSION);
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 
 static u8 mptctl_id = MPT_MAX_PROTOCOL_DRIVERS;
+static u8 mptctl_taskmgmt_id = MPT_MAX_PROTOCOL_DRIVERS;
 
 static DECLARE_WAIT_QUEUE_HEAD ( mptctl_wait );
 
@@ -127,10 +128,7 @@ static MptSge_t *kbuf_alloc_2_sgl(int bytes, u32 dir, int sge_offset, int *frags
                struct buflist **blp, dma_addr_t *sglbuf_dma, MPT_ADAPTER *ioc);
 static void kfree_sgl(MptSge_t *sgl, dma_addr_t sgl_dma,
                struct buflist *buflist, MPT_ADAPTER *ioc);
-static void mptctl_timeout_expired (MPT_IOCTL *ioctl);
-static int  mptctl_bus_reset(MPT_IOCTL *ioctl);
-static int mptctl_set_tm_flags(MPT_SCSI_HOST *hd);
-static void mptctl_free_tm_flags(MPT_ADAPTER *ioc);
+static int mptctl_bus_reset(MPT_ADAPTER *ioc, u8 function);
 
 /*
  * Reset Handler cleanup function
@@ -183,10 +181,10 @@ mptctl_syscall_down(MPT_ADAPTER *ioc, int nonblock)
        int rc = 0;
 
        if (nonblock) {
-               if (!mutex_trylock(&ioc->ioctl->ioctl_mutex))
+               if (!mutex_trylock(&ioc->ioctl_cmds.mutex))
                        rc = -EAGAIN;
        } else {
-               if (mutex_lock_interruptible(&ioc->ioctl->ioctl_mutex))
+               if (mutex_lock_interruptible(&ioc->ioctl_cmds.mutex))
                        rc = -ERESTARTSYS;
        }
        return rc;
@@ -202,99 +200,78 @@ mptctl_syscall_down(MPT_ADAPTER *ioc, int nonblock)
 static int
 mptctl_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply)
 {
-       char *sense_data;
-       int sz, req_index;
-       u16 iocStatus;
-       u8 cmd;
+       char    *sense_data;
+       int     req_index;
+       int     sz;
 
-       if (req)
-                cmd = req->u.hdr.Function;
-       else
-               return 1;
-       dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "\tcompleting mpi function (0x%02X), req=%p, "
-           "reply=%p\n", ioc->name,  req->u.hdr.Function, req, reply));
-
-       if (ioc->ioctl) {
-
-               if (reply==NULL) {
-
-                       dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_reply() NULL Reply "
-                               "Function=%x!\n", ioc->name, cmd));
+       if (!req)
+               return 0;
 
-                       ioc->ioctl->status |= MPT_IOCTL_STATUS_COMMAND_GOOD;
-                       ioc->ioctl->reset &= ~MPTCTL_RESET_OK;
+       dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "completing mpi function "
+           "(0x%02X), req=%p, reply=%p\n", ioc->name,  req->u.hdr.Function,
+           req, reply));
 
-                       /* We are done, issue wake up
-                       */
-                       ioc->ioctl->wait_done = 1;
-                       wake_up (&mptctl_wait);
-                       return 1;
+       /*
+        * Handling continuation of the same reply. Processing the first
+        * reply, and eating the other replys that come later.
+        */
+       if (ioc->ioctl_cmds.msg_context != req->u.hdr.MsgContext)
+               goto out_continuation;
 
-               }
+       ioc->ioctl_cmds.status |= MPT_MGMT_STATUS_COMMAND_GOOD;
 
-               /* Copy the reply frame (which much exist
-                * for non-SCSI I/O) to the IOC structure.
-                */
-               memcpy(ioc->ioctl->ReplyFrame, reply,
-                       min(ioc->reply_sz, 4*reply->u.reply.MsgLength));
-               ioc->ioctl->status |= MPT_IOCTL_STATUS_RF_VALID;
+       if (!reply)
+               goto out;
 
-               /* Set the command status to GOOD if IOC Status is GOOD
-                * OR if SCSI I/O cmd and data underrun or recovered error.
-                */
-               iocStatus = le16_to_cpu(reply->u.reply.IOCStatus) & MPI_IOCSTATUS_MASK;
-               if (iocStatus  == MPI_IOCSTATUS_SUCCESS)
-                       ioc->ioctl->status |= MPT_IOCTL_STATUS_COMMAND_GOOD;
-
-               if (iocStatus || reply->u.reply.IOCLogInfo)
-                       dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "\tiocstatus (0x%04X), "
-                               "loginfo (0x%08X)\n", ioc->name,
-                               iocStatus,
-                               le32_to_cpu(reply->u.reply.IOCLogInfo)));
-
-               if ((cmd == MPI_FUNCTION_SCSI_IO_REQUEST) ||
-                       (cmd == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) {
-
-                       if (reply->u.sreply.SCSIStatus || reply->u.sreply.SCSIState)
-                               dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT
-                                       "\tscsi_status (0x%02x), scsi_state (0x%02x), "
-                                       "tag = (0x%04x), transfer_count (0x%08x)\n", ioc->name,
-                                       reply->u.sreply.SCSIStatus,
-                                       reply->u.sreply.SCSIState,
-                                       le16_to_cpu(reply->u.sreply.TaskTag),
-                                       le32_to_cpu(reply->u.sreply.TransferCount)));
-
-                       ioc->ioctl->reset &= ~MPTCTL_RESET_OK;
-
-                       if ((iocStatus == MPI_IOCSTATUS_SCSI_DATA_UNDERRUN) ||
-                       (iocStatus == MPI_IOCSTATUS_SCSI_RECOVERED_ERROR)) {
-                       ioc->ioctl->status |= MPT_IOCTL_STATUS_COMMAND_GOOD;
-                       }
-               }
+       ioc->ioctl_cmds.status |= MPT_MGMT_STATUS_RF_VALID;
+       sz = min(ioc->reply_sz, 4*reply->u.reply.MsgLength);
+       memcpy(ioc->ioctl_cmds.reply, reply, sz);
 
-               /* Copy the sense data - if present
-                */
-               if ((cmd == MPI_FUNCTION_SCSI_IO_REQUEST) &&
-                       (reply->u.sreply.SCSIState &
-                        MPI_SCSI_STATE_AUTOSENSE_VALID)){
+       if (reply->u.reply.IOCStatus || reply->u.reply.IOCLogInfo)
+               dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "iocstatus (0x%04X), loginfo (0x%08X)\n", ioc->name,
+                   le16_to_cpu(reply->u.reply.IOCStatus),
+                   le32_to_cpu(reply->u.reply.IOCLogInfo)));
+
+       if ((req->u.hdr.Function == MPI_FUNCTION_SCSI_IO_REQUEST) ||
+               (req->u.hdr.Function ==
+                MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) {
+
+               if (reply->u.sreply.SCSIStatus || reply->u.sreply.SCSIState)
+                       dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                       "scsi_status (0x%02x), scsi_state (0x%02x), "
+                       "tag = (0x%04x), transfer_count (0x%08x)\n", ioc->name,
+                       reply->u.sreply.SCSIStatus,
+                       reply->u.sreply.SCSIState,
+                       le16_to_cpu(reply->u.sreply.TaskTag),
+                       le32_to_cpu(reply->u.sreply.TransferCount)));
+
+               if (reply->u.sreply.SCSIState &
+                       MPI_SCSI_STATE_AUTOSENSE_VALID) {
                        sz = req->u.scsireq.SenseBufferLength;
                        req_index =
                            le16_to_cpu(req->u.frame.hwhdr.msgctxu.fld.req_idx);
-                       sense_data =
-                           ((u8 *)ioc->sense_buf_pool +
+                       sense_data = ((u8 *)ioc->sense_buf_pool +
                             (req_index * MPT_SENSE_BUFFER_ALLOC));
-                       memcpy(ioc->ioctl->sense, sense_data, sz);
-                       ioc->ioctl->status |= MPT_IOCTL_STATUS_SENSE_VALID;
+                       memcpy(ioc->ioctl_cmds.sense, sense_data, sz);
+                       ioc->ioctl_cmds.status |= MPT_MGMT_STATUS_SENSE_VALID;
                }
+       }
 
-               if (cmd == MPI_FUNCTION_SCSI_TASK_MGMT)
-                       mptctl_free_tm_flags(ioc);
-
-               /* We are done, issue wake up
-                */
-               ioc->ioctl->wait_done = 1;
-               wake_up (&mptctl_wait);
+ out:
+       /* We are done, issue wake up
+        */
+       if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_PENDING) {
+               if (req->u.hdr.Function == MPI_FUNCTION_SCSI_TASK_MGMT)
+                       mpt_clear_taskmgmt_in_progress_flag(ioc);
+               ioc->ioctl_cmds.status &= ~MPT_MGMT_STATUS_PENDING;
+               complete(&ioc->ioctl_cmds.done);
        }
+
+ out_continuation:
+       if (reply && (reply->u.reply.MsgFlags &
+           MPI_MSGFLAGS_CONTINUATION_REPLY))
+               return 0;
        return 1;
 }
 
@@ -304,30 +281,66 @@ mptctl_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply)
  * Expecting an interrupt, however timed out.
  *
  */
-static void mptctl_timeout_expired (MPT_IOCTL *ioctl)
+static void
+mptctl_timeout_expired(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf)
 {
-       int rc = 1;
+       unsigned long flags;
 
-       if (ioctl == NULL)
-               return;
-       dctlprintk(ioctl->ioc,
-                  printk(MYIOC_s_DEBUG_FMT ": Timeout Expired! Host %d\n",
-                  ioctl->ioc->name, ioctl->ioc->id));
+       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": %s\n",
+               ioc->name, __func__));
 
-       ioctl->wait_done = 0;
-       if (ioctl->reset & MPTCTL_RESET_OK)
-               rc = mptctl_bus_reset(ioctl);
+       if (mpt_fwfault_debug)
+               mpt_halt_firmware(ioc);
 
-       if (rc) {
-               /* Issue a reset for this device.
-                * The IOC is not responding.
-                */
-               dctlprintk(ioctl->ioc, printk(MYIOC_s_DEBUG_FMT "Calling HardReset! \n",
-                        ioctl->ioc->name));
-               mpt_HardResetHandler(ioctl->ioc, CAN_SLEEP);
+       spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+       if (ioc->ioc_reset_in_progress) {
+               spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+               CLEAR_MGMT_PENDING_STATUS(ioc->ioctl_cmds.status)
+               mpt_free_msg_frame(ioc, mf);
+               return;
        }
-       return;
+       spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+
 
+       if (!mptctl_bus_reset(ioc, mf->u.hdr.Function))
+               return;
+
+       /* Issue a reset for this device.
+        * The IOC is not responding.
+        */
+       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Calling HardReset! \n",
+                ioc->name));
+       CLEAR_MGMT_PENDING_STATUS(ioc->ioctl_cmds.status)
+       mpt_HardResetHandler(ioc, CAN_SLEEP);
+       mpt_free_msg_frame(ioc, mf);
+}
+
+static int
+mptctl_taskmgmt_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
+{
+       if (!mf)
+               return 0;
+
+       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+               "TaskMgmt completed (mf=%p, mr=%p)\n",
+               ioc->name, mf, mr));
+
+       ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_COMMAND_GOOD;
+
+       if (!mr)
+               goto out;
+
+       ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_RF_VALID;
+       memcpy(ioc->taskmgmt_cmds.reply, mr,
+           min(MPT_DEFAULT_FRAME_SIZE, 4 * mr->u.reply.MsgLength));
+ out:
+       if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_PENDING) {
+               mpt_clear_taskmgmt_in_progress_flag(ioc);
+               ioc->taskmgmt_cmds.status &= ~MPT_MGMT_STATUS_PENDING;
+               complete(&ioc->taskmgmt_cmds.done);
+               return 1;
+       }
+       return 0;
 }
 
 /* mptctl_bus_reset
@@ -335,133 +348,150 @@ static void mptctl_timeout_expired (MPT_IOCTL *ioctl)
  * Bus reset code.
  *
  */
-static int mptctl_bus_reset(MPT_IOCTL *ioctl)
+static int mptctl_bus_reset(MPT_ADAPTER *ioc, u8 function)
 {
        MPT_FRAME_HDR   *mf;
        SCSITaskMgmt_t  *pScsiTm;
-       MPT_SCSI_HOST   *hd;
+       SCSITaskMgmtReply_t *pScsiTmReply;
        int              ii;
-       int              retval=0;
-
-
-       ioctl->reset &= ~MPTCTL_RESET_OK;
-
-       if (ioctl->ioc->sh == NULL)
+       int              retval;
+       unsigned long    timeout;
+       unsigned long    time_count;
+       u16              iocstatus;
+
+       /* bus reset is only good for SCSI IO, RAID PASSTHRU */
+       if (!(function == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) ||
+           (function == MPI_FUNCTION_SCSI_IO_REQUEST)) {
+               dtmprintk(ioc, printk(MYIOC_s_WARN_FMT
+                       "TaskMgmt, not SCSI_IO!!\n", ioc->name));
                return -EPERM;
+       }
 
-       hd = shost_priv(ioctl->ioc->sh);
-       if (hd == NULL)
+       mutex_lock(&ioc->taskmgmt_cmds.mutex);
+       if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0) {
+               mutex_unlock(&ioc->taskmgmt_cmds.mutex);
                return -EPERM;
+       }
 
-       /* Single threading ....
-        */
-       if (mptctl_set_tm_flags(hd) != 0)
-               return -EPERM;
+       retval = 0;
 
        /* Send request
         */
-       if ((mf = mpt_get_msg_frame(mptctl_id, ioctl->ioc)) == NULL) {
-               dtmprintk(ioctl->ioc, printk(MYIOC_s_DEBUG_FMT "IssueTaskMgmt, no msg frames!!\n",
-                               ioctl->ioc->name));
-
-               mptctl_free_tm_flags(ioctl->ioc);
-               return -ENOMEM;
+       mf = mpt_get_msg_frame(mptctl_taskmgmt_id, ioc);
+       if (mf == NULL) {
+               dtmprintk(ioc, printk(MYIOC_s_WARN_FMT
+                       "TaskMgmt, no msg frames!!\n", ioc->name));
+               mpt_clear_taskmgmt_in_progress_flag(ioc);
+               retval = -ENOMEM;
+               goto mptctl_bus_reset_done;
        }
 
-       dtmprintk(ioctl->ioc, printk(MYIOC_s_DEBUG_FMT "IssueTaskMgmt request @ %p\n",
-                       ioctl->ioc->name, mf));
+       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt request (mf=%p)\n",
+               ioc->name, mf));
 
        pScsiTm = (SCSITaskMgmt_t *) mf;
-       pScsiTm->TargetID = ioctl->id;
-       pScsiTm->Bus = hd->port;        /* 0 */
-       pScsiTm->ChainOffset = 0;
+       memset(pScsiTm, 0, sizeof(SCSITaskMgmt_t));
        pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
-       pScsiTm->Reserved = 0;
        pScsiTm->TaskType = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS;
-       pScsiTm->Reserved1 = 0;
        pScsiTm->MsgFlags = MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION;
-
+       pScsiTm->TargetID = 0;
+       pScsiTm->Bus = 0;
+       pScsiTm->ChainOffset = 0;
+       pScsiTm->Reserved = 0;
+       pScsiTm->Reserved1 = 0;
+       pScsiTm->TaskMsgContext = 0;
        for (ii= 0; ii < 8; ii++)
                pScsiTm->LUN[ii] = 0;
-
        for (ii=0; ii < 7; ii++)
                pScsiTm->Reserved2[ii] = 0;
 
-       pScsiTm->TaskMsgContext = 0;
-       dtmprintk(ioctl->ioc, printk(MYIOC_s_DEBUG_FMT
-               "mptctl_bus_reset: issued.\n", ioctl->ioc->name));
-
-       DBG_DUMP_TM_REQUEST_FRAME(ioctl->ioc, (u32 *)mf);
+       switch (ioc->bus_type) {
+       case FC:
+               timeout = 40;
+               break;
+       case SAS:
+               timeout = 30;
+               break;
+       case SPI:
+       default:
+               timeout = 2;
+               break;
+       }
 
-       ioctl->wait_done=0;
+       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+               "TaskMgmt type=%d timeout=%ld\n",
+               ioc->name, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, timeout));
 
-       if ((ioctl->ioc->facts.IOCCapabilities & MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q) &&
-           (ioctl->ioc->facts.MsgVersion >= MPI_VERSION_01_05))
-               mpt_put_msg_frame_hi_pri(mptctl_id, ioctl->ioc, mf);
+       INITIALIZE_MGMT_STATUS(ioc->taskmgmt_cmds.status)
+       CLEAR_MGMT_STATUS(ioc->taskmgmt_cmds.status)
+       time_count = jiffies;
+       if ((ioc->facts.IOCCapabilities & MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q) &&
+           (ioc->facts.MsgVersion >= MPI_VERSION_01_05))
+               mpt_put_msg_frame_hi_pri(mptctl_taskmgmt_id, ioc, mf);
        else {
-               retval = mpt_send_handshake_request(mptctl_id, ioctl->ioc,
-                       sizeof(SCSITaskMgmt_t), (u32*)pScsiTm, CAN_SLEEP);
+               retval = mpt_send_handshake_request(mptctl_taskmgmt_id, ioc,
+                   sizeof(SCSITaskMgmt_t), (u32 *)pScsiTm, CAN_SLEEP);
                if (retval != 0) {
-                       dfailprintk(ioctl->ioc, printk(MYIOC_s_ERR_FMT "_send_handshake FAILED!"
-                               " (hd %p, ioc %p, mf %p) \n", hd->ioc->name, hd,
-                               hd->ioc, mf));
+                       dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+                               "TaskMgmt send_handshake FAILED!"
+                               " (ioc %p, mf %p, rc=%d) \n", ioc->name,
+                               ioc, mf, retval));
+                       mpt_clear_taskmgmt_in_progress_flag(ioc);
                        goto mptctl_bus_reset_done;
                }
        }
 
        /* Now wait for the command to complete */
-       ii = wait_event_timeout(mptctl_wait,
-            ioctl->wait_done == 1,
-            HZ*5 /* 5 second timeout */);
+       ii = wait_for_completion_timeout(&ioc->taskmgmt_cmds.done, timeout*HZ);
+       if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "TaskMgmt failed\n", ioc->name));
+               mpt_free_msg_frame(ioc, mf);
+               mpt_clear_taskmgmt_in_progress_flag(ioc);
+               if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET)
+                       retval = 0;
+               else
+                       retval = -1; /* return failure */
+               goto mptctl_bus_reset_done;
+       }
 
-       if(ii <=0 && (ioctl->wait_done != 1 ))  {
-               mpt_free_msg_frame(hd->ioc, mf);
-               ioctl->wait_done = 0;
+       if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_RF_VALID)) {
+               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "TaskMgmt failed\n", ioc->name));
+               retval = -1; /* return failure */
+               goto mptctl_bus_reset_done;
+       }
+
+       pScsiTmReply = (SCSITaskMgmtReply_t *) ioc->taskmgmt_cmds.reply;
+       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+           "TaskMgmt fw_channel = %d, fw_id = %d, task_type=0x%02X, "
+           "iocstatus=0x%04X\n\tloginfo=0x%08X, response_code=0x%02X, "
+           "term_cmnds=%d\n", ioc->name, pScsiTmReply->Bus,
+           pScsiTmReply->TargetID, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS,
+           le16_to_cpu(pScsiTmReply->IOCStatus),
+           le32_to_cpu(pScsiTmReply->IOCLogInfo),
+           pScsiTmReply->ResponseCode,
+           le32_to_cpu(pScsiTmReply->TerminationCount)));
+
+       iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK;
+
+       if (iocstatus == MPI_IOCSTATUS_SCSI_TASK_TERMINATED ||
+          iocstatus == MPI_IOCSTATUS_SCSI_IOC_TERMINATED ||
+          iocstatus == MPI_IOCSTATUS_SUCCESS)
+               retval = 0;
+       else {
+               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "TaskMgmt failed\n", ioc->name));
                retval = -1; /* return failure */
        }
 
-mptctl_bus_reset_done:
 
-       mptctl_free_tm_flags(ioctl->ioc);
+ mptctl_bus_reset_done:
+       mutex_unlock(&ioc->taskmgmt_cmds.mutex);
+       CLEAR_MGMT_STATUS(ioc->taskmgmt_cmds.status)
        return retval;
 }
 
-static int
-mptctl_set_tm_flags(MPT_SCSI_HOST *hd) {
-       unsigned long flags;
-
-       spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
-
-       if (hd->tmState == TM_STATE_NONE) {
-               hd->tmState = TM_STATE_IN_PROGRESS;
-               hd->tmPending = 1;
-               spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
-       } else {
-               spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
-               return -EBUSY;
-       }
-
-       return 0;
-}
-
-static void
-mptctl_free_tm_flags(MPT_ADAPTER *ioc)
-{
-       MPT_SCSI_HOST * hd;
-       unsigned long flags;
-
-       hd = shost_priv(ioc->sh);
-       if (hd == NULL)
-               return;
-
-       spin_lock_irqsave(&ioc->FreeQlock, flags);
-
-       hd->tmState = TM_STATE_NONE;
-       hd->tmPending = 0;
-       spin_unlock_irqrestore(&ioc->FreeQlock, flags);
-
-       return;
-}
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /* mptctl_ioc_reset
@@ -473,22 +503,23 @@ mptctl_free_tm_flags(MPT_ADAPTER *ioc)
 static int
 mptctl_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
 {
-       MPT_IOCTL *ioctl = ioc->ioctl;
-       dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "IOC %s_reset routed to IOCTL driver!\n", ioc->name,
-               reset_phase==MPT_IOC_SETUP_RESET ? "setup" : (
-               reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post")));
-
-       if(ioctl == NULL)
-               return 1;
-
        switch(reset_phase) {
        case MPT_IOC_SETUP_RESET:
-               ioctl->status |= MPT_IOCTL_STATUS_DID_IOCRESET;
+               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "%s: MPT_IOC_SETUP_RESET\n", ioc->name, __func__));
+               break;
+       case MPT_IOC_PRE_RESET:
+               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "%s: MPT_IOC_PRE_RESET\n", ioc->name, __func__));
                break;
        case MPT_IOC_POST_RESET:
-               ioctl->status &= ~MPT_IOCTL_STATUS_DID_IOCRESET;
+               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "%s: MPT_IOC_POST_RESET\n", ioc->name, __func__));
+               if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_PENDING) {
+                       ioc->ioctl_cmds.status |= MPT_MGMT_STATUS_DID_IOCRESET;
+                       complete(&ioc->ioctl_cmds.done);
+               }
                break;
-       case MPT_IOC_PRE_RESET:
        default:
                break;
        }
@@ -642,7 +673,7 @@ __mptctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        else
                ret = -EINVAL;
 
-       mutex_unlock(&iocp->ioctl->ioctl_mutex);
+       mutex_unlock(&iocp->ioctl_cmds.mutex);
 
        return ret;
 }
@@ -758,6 +789,7 @@ mptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen)
        int                      sge_offset = 0;
        u16                      iocstat;
        pFWDownloadReply_t       ReplyMsg = NULL;
+       unsigned long            timeleft;
 
        if (mpt_verify_adapter(ioc, &iocp) < 0) {
                printk(KERN_DEBUG MYNAM "ioctl_fwdl - ioc%d not found!\n",
@@ -841,8 +873,9 @@ mptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen)
         *      96              8
         *      64              4
         */
-       maxfrags = (iocp->req_sz - sizeof(MPIHeader_t) - sizeof(FWDownloadTCSGE_t))
-                       / (sizeof(dma_addr_t) + sizeof(u32));
+       maxfrags = (iocp->req_sz - sizeof(MPIHeader_t) -
+                       sizeof(FWDownloadTCSGE_t))
+                       / iocp->SGE_size;
        if (numfrags > maxfrags) {
                ret = -EMLINK;
                goto fwdl_out;
@@ -870,7 +903,7 @@ mptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen)
                if (nib == 0 || nib == 3) {
                        ;
                } else if (sgIn->Address) {
-                       mpt_add_sge(sgOut, sgIn->FlagsLength, sgIn->Address);
+                       iocp->add_sge(sgOut, sgIn->FlagsLength, sgIn->Address);
                        n++;
                        if (copy_from_user(bl->kptr, ufwbuf+fw_bytes_copied, bl->len)) {
                                printk(MYIOC_s_ERR_FMT "%s@%d::_ioctl_fwdl - "
@@ -882,7 +915,7 @@ mptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen)
                }
                sgIn++;
                bl++;
-               sgOut += (sizeof(dma_addr_t) + sizeof(u32));
+               sgOut += iocp->SGE_size;
        }
 
        DBG_DUMP_FW_DOWNLOAD(iocp, (u32 *)mf, numfrags);
@@ -891,16 +924,30 @@ mptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen)
         * Finally, perform firmware download.
         */
        ReplyMsg = NULL;
+       SET_MGMT_MSG_CONTEXT(iocp->ioctl_cmds.msg_context, dlmsg->MsgContext);
+       INITIALIZE_MGMT_STATUS(iocp->ioctl_cmds.status)
        mpt_put_msg_frame(mptctl_id, iocp, mf);
 
        /* Now wait for the command to complete */
-       ret = wait_event_timeout(mptctl_wait,
-            iocp->ioctl->wait_done == 1,
-            HZ*60);
+retry_wait:
+       timeleft = wait_for_completion_timeout(&iocp->ioctl_cmds.done, HZ*60);
+       if (!(iocp->ioctl_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+               ret = -ETIME;
+               printk(MYIOC_s_WARN_FMT "%s: failed\n", iocp->name, __func__);
+               if (iocp->ioctl_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET) {
+                       mpt_free_msg_frame(iocp, mf);
+                       goto fwdl_out;
+               }
+               if (!timeleft)
+                       mptctl_timeout_expired(iocp, mf);
+               else
+                       goto retry_wait;
+               goto fwdl_out;
+       }
 
-       if(ret <=0 && (iocp->ioctl->wait_done != 1 )) {
-       /* Now we need to reset the board */
-               mptctl_timeout_expired(iocp->ioctl);
+       if (!(iocp->ioctl_cmds.status & MPT_MGMT_STATUS_RF_VALID)) {
+               printk(MYIOC_s_WARN_FMT "%s: failed\n", iocp->name, __func__);
+               mpt_free_msg_frame(iocp, mf);
                ret = -ENODATA;
                goto fwdl_out;
        }
@@ -908,7 +955,7 @@ mptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen)
        if (sgl)
                kfree_sgl(sgl, sgl_dma, buflist, iocp);
 
-       ReplyMsg = (pFWDownloadReply_t)iocp->ioctl->ReplyFrame;
+       ReplyMsg = (pFWDownloadReply_t)iocp->ioctl_cmds.reply;
        iocstat = le16_to_cpu(ReplyMsg->IOCStatus) & MPI_IOCSTATUS_MASK;
        if (iocstat == MPI_IOCSTATUS_SUCCESS) {
                printk(MYIOC_s_INFO_FMT "F/W update successfull!\n", iocp->name);
@@ -932,6 +979,9 @@ mptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen)
        return 0;
 
 fwdl_out:
+
+       CLEAR_MGMT_STATUS(iocp->ioctl_cmds.status);
+       SET_MGMT_MSG_CONTEXT(iocp->ioctl_cmds.msg_context, 0);
         kfree_sgl(sgl, sgl_dma, buflist, iocp);
        return ret;
 }
@@ -1003,7 +1053,7 @@ kbuf_alloc_2_sgl(int bytes, u32 sgdir, int sge_offset, int *frags,
         *
         */
        sgl = sglbuf;
-       sg_spill = ((ioc->req_sz - sge_offset)/(sizeof(dma_addr_t) + sizeof(u32))) - 1;
+       sg_spill = ((ioc->req_sz - sge_offset)/ioc->SGE_size) - 1;
        while (bytes_allocd < bytes) {
                this_alloc = min(alloc_sz, bytes-bytes_allocd);
                buflist[buflist_ent].len = this_alloc;
@@ -1024,8 +1074,9 @@ kbuf_alloc_2_sgl(int bytes, u32 sgdir, int sge_offset, int *frags,
                        dma_addr_t dma_addr;
 
                        bytes_allocd += this_alloc;
-                       sgl->FlagsLength = (0x10000000|MPT_SGE_FLAGS_ADDRESSING|sgdir|this_alloc);
-                       dma_addr = pci_map_single(ioc->pcidev, buflist[buflist_ent].kptr, this_alloc, dir);
+                       sgl->FlagsLength = (0x10000000|sgdir|this_alloc);
+                       dma_addr = pci_map_single(ioc->pcidev,
+                               buflist[buflist_ent].kptr, this_alloc, dir);
                        sgl->Address = dma_addr;
 
                        fragcnt++;
@@ -1771,7 +1822,10 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
        int             msgContext;
        u16             req_idx;
        ulong           timeout;
+       unsigned long   timeleft;
        struct scsi_device *sdev;
+       unsigned long    flags;
+       u8               function;
 
        /* bufIn and bufOut are used for user to kernel space transfers
         */
@@ -1784,24 +1838,23 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
                                __FILE__, __LINE__, iocnum);
                return -ENODEV;
        }
-       if (!ioc->ioctl) {
-               printk(KERN_ERR MYNAM "%s@%d::mptctl_do_mpt_command - "
-                       "No memory available during driver init.\n",
-                               __FILE__, __LINE__);
-               return -ENOMEM;
-       } else if (ioc->ioctl->status & MPT_IOCTL_STATUS_DID_IOCRESET) {
+
+       spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+       if (ioc->ioc_reset_in_progress) {
+               spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
                printk(KERN_ERR MYNAM "%s@%d::mptctl_do_mpt_command - "
-                       "Busy with IOC Reset \n", __FILE__, __LINE__);
+                       "Busy with diagnostic reset\n", __FILE__, __LINE__);
                return -EBUSY;
        }
+       spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
 
        /* Verify that the final request frame will not be too large.
         */
        sz = karg.dataSgeOffset * 4;
        if (karg.dataInSize > 0)
-               sz += sizeof(dma_addr_t) + sizeof(u32);
+               sz += ioc->SGE_size;
        if (karg.dataOutSize > 0)
-               sz += sizeof(dma_addr_t) + sizeof(u32);
+               sz += ioc->SGE_size;
 
        if (sz > ioc->req_sz) {
                printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_do_mpt_command - "
@@ -1827,10 +1880,12 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
                printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_do_mpt_command - "
                        "Unable to read MF from mpt_ioctl_command struct @ %p\n",
                        ioc->name, __FILE__, __LINE__, mfPtr);
+               function = -1;
                rc = -EFAULT;
                goto done_free_mem;
        }
        hdr->MsgContext = cpu_to_le32(msgContext);
+       function = hdr->Function;
 
 
        /* Verify that this request is allowed.
@@ -1838,7 +1893,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
        dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "sending mpi function (0x%02X), req=%p\n",
            ioc->name, hdr->Function, mf));
 
-       switch (hdr->Function) {
+       switch (function) {
        case MPI_FUNCTION_IOC_FACTS:
        case MPI_FUNCTION_PORT_FACTS:
                karg.dataOutSize  = karg.dataInSize = 0;
@@ -1893,7 +1948,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
                        }
 
                        pScsiReq->MsgFlags &= ~MPI_SCSIIO_MSGFLGS_SENSE_WIDTH;
-                       pScsiReq->MsgFlags |= mpt_msg_flags();
+                       pScsiReq->MsgFlags |= mpt_msg_flags(ioc);
 
 
                        /* verify that app has not requested
@@ -1935,8 +1990,6 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
                        pScsiReq->Control = cpu_to_le32(scsidir | qtag);
                        pScsiReq->DataLength = cpu_to_le32(dataSize);
 
-                       ioc->ioctl->reset = MPTCTL_RESET_OK;
-                       ioc->ioctl->id = pScsiReq->TargetID;
 
                } else {
                        printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_do_mpt_command - "
@@ -1979,7 +2032,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
                        int dataSize;
 
                        pScsiReq->MsgFlags &= ~MPI_SCSIIO_MSGFLGS_SENSE_WIDTH;
-                       pScsiReq->MsgFlags |= mpt_msg_flags();
+                       pScsiReq->MsgFlags |= mpt_msg_flags(ioc);
 
 
                        /* verify that app has not requested
@@ -2014,8 +2067,6 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
                        pScsiReq->Control = cpu_to_le32(scsidir | qtag);
                        pScsiReq->DataLength = cpu_to_le32(dataSize);
 
-                       ioc->ioctl->reset = MPTCTL_RESET_OK;
-                       ioc->ioctl->id = pScsiReq->TargetID;
                } else {
                        printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_do_mpt_command - "
                                "SCSI driver is not loaded. \n",
@@ -2026,20 +2077,17 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
                break;
 
        case MPI_FUNCTION_SCSI_TASK_MGMT:
-               {
-                       MPT_SCSI_HOST *hd = NULL;
-                       if ((ioc->sh == NULL) || ((hd = shost_priv(ioc->sh)) == NULL)) {
-                               printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_do_mpt_command - "
-                                       "SCSI driver not loaded or SCSI host not found. \n",
-                                       ioc->name, __FILE__, __LINE__);
-                               rc = -EFAULT;
-                               goto done_free_mem;
-                       } else if (mptctl_set_tm_flags(hd) != 0) {
-                               rc = -EPERM;
-                               goto done_free_mem;
-                       }
-               }
+       {
+               SCSITaskMgmt_t  *pScsiTm;
+               pScsiTm = (SCSITaskMgmt_t *)mf;
+               dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                       "\tTaskType=0x%x MsgFlags=0x%x "
+                       "TaskMsgContext=0x%x id=%d channel=%d\n",
+                       ioc->name, pScsiTm->TaskType, le32_to_cpu
+                       (pScsiTm->TaskMsgContext), pScsiTm->MsgFlags,
+                       pScsiTm->TargetID, pScsiTm->Bus));
                break;
+       }
 
        case MPI_FUNCTION_IOC_INIT:
                {
@@ -2123,8 +2171,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
                        if (karg.dataInSize > 0) {
                                flagsLength = ( MPI_SGE_FLAGS_SIMPLE_ELEMENT |
                                                MPI_SGE_FLAGS_END_OF_BUFFER |
-                                               MPI_SGE_FLAGS_DIRECTION |
-                                               mpt_addr_size() )
+                                               MPI_SGE_FLAGS_DIRECTION)
                                                << MPI_SGE_FLAGS_SHIFT;
                        } else {
                                flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE;
@@ -2141,8 +2188,8 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
                                /* Set up this SGE.
                                 * Copy to MF and to sglbuf
                                 */
-                               mpt_add_sge(psge, flagsLength, dma_addr_out);
-                               psge += (sizeof(u32) + sizeof(dma_addr_t));
+                               ioc->add_sge(psge, flagsLength, dma_addr_out);
+                               psge += ioc->SGE_size;
 
                                /* Copy user data to kernel space.
                                 */
@@ -2175,18 +2222,25 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
                                /* Set up this SGE
                                 * Copy to MF and to sglbuf
                                 */
-                               mpt_add_sge(psge, flagsLength, dma_addr_in);
+                               ioc->add_sge(psge, flagsLength, dma_addr_in);
                        }
                }
        } else  {
                /* Add a NULL SGE
                 */
-               mpt_add_sge(psge, flagsLength, (dma_addr_t) -1);
+               ioc->add_sge(psge, flagsLength, (dma_addr_t) -1);
        }
 
-       ioc->ioctl->wait_done = 0;
+       SET_MGMT_MSG_CONTEXT(ioc->ioctl_cmds.msg_context, hdr->MsgContext);
+       INITIALIZE_MGMT_STATUS(ioc->ioctl_cmds.status)
        if (hdr->Function == MPI_FUNCTION_SCSI_TASK_MGMT) {
 
+               mutex_lock(&ioc->taskmgmt_cmds.mutex);
+               if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0) {
+                       mutex_unlock(&ioc->taskmgmt_cmds.mutex);
+                       goto done_free_mem;
+               }
+
                DBG_DUMP_TM_REQUEST_FRAME(ioc, (u32 *)mf);
 
                if ((ioc->facts.IOCCapabilities & MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q) &&
@@ -2197,10 +2251,11 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
                                sizeof(SCSITaskMgmt_t), (u32*)mf, CAN_SLEEP);
                        if (rc != 0) {
                                dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
-                                   "_send_handshake FAILED! (ioc %p, mf %p)\n",
+                                   "send_handshake FAILED! (ioc %p, mf %p)\n",
                                    ioc->name, ioc, mf));
-                               mptctl_free_tm_flags(ioc);
+                               mpt_clear_taskmgmt_in_progress_flag(ioc);
                                rc = -ENODATA;
+                               mutex_unlock(&ioc->taskmgmt_cmds.mutex);
                                goto done_free_mem;
                        }
                }
@@ -2210,36 +2265,47 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
 
        /* Now wait for the command to complete */
        timeout = (karg.timeout > 0) ? karg.timeout : MPT_IOCTL_DEFAULT_TIMEOUT;
-       timeout = wait_event_timeout(mptctl_wait,
-            ioc->ioctl->wait_done == 1,
-            HZ*timeout);
-
-       if(timeout <=0 && (ioc->ioctl->wait_done != 1 )) {
-       /* Now we need to reset the board */
-
-               if (hdr->Function == MPI_FUNCTION_SCSI_TASK_MGMT)
-                       mptctl_free_tm_flags(ioc);
-
-               mptctl_timeout_expired(ioc->ioctl);
-               rc = -ENODATA;
+retry_wait:
+       timeleft = wait_for_completion_timeout(&ioc->ioctl_cmds.done,
+                               HZ*timeout);
+       if (!(ioc->ioctl_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+               rc = -ETIME;
+               dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "%s: TIMED OUT!\n",
+                   ioc->name, __func__));
+               if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET) {
+                       if (function == MPI_FUNCTION_SCSI_TASK_MGMT)
+                               mutex_unlock(&ioc->taskmgmt_cmds.mutex);
+                       goto done_free_mem;
+               }
+               if (!timeleft) {
+                       if (function == MPI_FUNCTION_SCSI_TASK_MGMT)
+                               mutex_unlock(&ioc->taskmgmt_cmds.mutex);
+                       mptctl_timeout_expired(ioc, mf);
+                       mf = NULL;
+               } else
+                       goto retry_wait;
                goto done_free_mem;
        }
 
+       if (function == MPI_FUNCTION_SCSI_TASK_MGMT)
+               mutex_unlock(&ioc->taskmgmt_cmds.mutex);
+
+
        mf = NULL;
 
        /* If a valid reply frame, copy to the user.
         * Offset 2: reply length in U32's
         */
-       if (ioc->ioctl->status & MPT_IOCTL_STATUS_RF_VALID) {
+       if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_RF_VALID) {
                if (karg.maxReplyBytes < ioc->reply_sz) {
-                        sz = min(karg.maxReplyBytes, 4*ioc->ioctl->ReplyFrame[2]);
+                       sz = min(karg.maxReplyBytes,
+                               4*ioc->ioctl_cmds.reply[2]);
                } else {
-                        sz = min(ioc->reply_sz, 4*ioc->ioctl->ReplyFrame[2]);
+                        sz = min(ioc->reply_sz, 4*ioc->ioctl_cmds.reply[2]);
                }
-
                if (sz > 0) {
                        if (copy_to_user(karg.replyFrameBufPtr,
-                                &ioc->ioctl->ReplyFrame, sz)){
+                                ioc->ioctl_cmds.reply, sz)){
                                 printk(MYIOC_s_ERR_FMT
                                     "%s@%d::mptctl_do_mpt_command - "
                                 "Unable to write out reply frame %p\n",
@@ -2252,10 +2318,11 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
 
        /* If valid sense data, copy to user.
         */
-       if (ioc->ioctl->status & MPT_IOCTL_STATUS_SENSE_VALID) {
+       if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_SENSE_VALID) {
                sz = min(karg.maxSenseBytes, MPT_SENSE_BUFFER_SIZE);
                if (sz > 0) {
-                       if (copy_to_user(karg.senseDataPtr, ioc->ioctl->sense, sz)) {
+                       if (copy_to_user(karg.senseDataPtr,
+                               ioc->ioctl_cmds.sense, sz)) {
                                printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_do_mpt_command - "
                                "Unable to write sense data to user %p\n",
                                ioc->name, __FILE__, __LINE__,
@@ -2269,7 +2336,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
        /* If the overall status is _GOOD and data in, copy data
         * to user.
         */
-       if ((ioc->ioctl->status & MPT_IOCTL_STATUS_COMMAND_GOOD) &&
+       if ((ioc->ioctl_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD) &&
                                (karg.dataInSize > 0) && (bufIn.kptr)) {
 
                if (copy_to_user(karg.dataInBufPtr,
@@ -2284,9 +2351,8 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr)
 
 done_free_mem:
 
-       ioc->ioctl->status &= ~(MPT_IOCTL_STATUS_COMMAND_GOOD |
-               MPT_IOCTL_STATUS_SENSE_VALID |
-               MPT_IOCTL_STATUS_RF_VALID );
+       CLEAR_MGMT_STATUS(ioc->ioctl_cmds.status)
+       SET_MGMT_MSG_CONTEXT(ioc->ioctl_cmds.msg_context, 0);
 
        /* Free the allocated memory.
         */
@@ -2336,6 +2402,8 @@ mptctl_hp_hostinfo(unsigned long arg, unsigned int data_size)
        ToolboxIstwiReadWriteRequest_t  *IstwiRWRequest;
        MPT_FRAME_HDR           *mf = NULL;
        MPIHeader_t             *mpi_hdr;
+       unsigned long           timeleft;
+       int                     retval;
 
        /* Reset long to int. Should affect IA64 and SPARC only
         */
@@ -2466,9 +2534,9 @@ mptctl_hp_hostinfo(unsigned long arg, unsigned int data_size)
                MPT_SCSI_HOST *hd =  shost_priv(ioc->sh);
 
                if (hd && (cim_rev == 1)) {
-                       karg.hard_resets = hd->hard_resets;
-                       karg.soft_resets = hd->soft_resets;
-                       karg.timeouts = hd->timeouts;
+                       karg.hard_resets = ioc->hard_resets;
+                       karg.soft_resets = ioc->soft_resets;
+                       karg.timeouts = ioc->timeouts;
                }
        }
 
@@ -2476,8 +2544,8 @@ mptctl_hp_hostinfo(unsigned long arg, unsigned int data_size)
         * Gather ISTWI(Industry Standard Two Wire Interface) Data
         */
        if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) {
-               dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, no msg frames!!\n",
-                   ioc->name,__func__));
+               dfailprintk(ioc, printk(MYIOC_s_WARN_FMT
+                       "%s, no msg frames!!\n", ioc->name, __func__));
                goto out;
        }
 
@@ -2498,22 +2566,29 @@ mptctl_hp_hostinfo(unsigned long arg, unsigned int data_size)
        pbuf = pci_alloc_consistent(ioc->pcidev, 4, &buf_dma);
        if (!pbuf)
                goto out;
-       mpt_add_sge((char *)&IstwiRWRequest->SGL,
+       ioc->add_sge((char *)&IstwiRWRequest->SGL,
            (MPT_SGE_FLAGS_SSIMPLE_READ|4), buf_dma);
 
-       ioc->ioctl->wait_done = 0;
+       retval = 0;
+       SET_MGMT_MSG_CONTEXT(ioc->ioctl_cmds.msg_context,
+                               IstwiRWRequest->MsgContext);
+       INITIALIZE_MGMT_STATUS(ioc->ioctl_cmds.status)
        mpt_put_msg_frame(mptctl_id, ioc, mf);
 
-       rc = wait_event_timeout(mptctl_wait,
-            ioc->ioctl->wait_done == 1,
-            HZ*MPT_IOCTL_DEFAULT_TIMEOUT /* 10 sec */);
-
-       if(rc <=0 && (ioc->ioctl->wait_done != 1 )) {
-               /*
-                * Now we need to reset the board
-                */
-               mpt_free_msg_frame(ioc, mf);
-               mptctl_timeout_expired(ioc->ioctl);
+retry_wait:
+       timeleft = wait_for_completion_timeout(&ioc->ioctl_cmds.done,
+                       HZ*MPT_IOCTL_DEFAULT_TIMEOUT);
+       if (!(ioc->ioctl_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+               retval = -ETIME;
+               printk(MYIOC_s_WARN_FMT "%s: failed\n", ioc->name, __func__);
+               if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET) {
+                       mpt_free_msg_frame(ioc, mf);
+                       goto out;
+               }
+               if (!timeleft)
+                       mptctl_timeout_expired(ioc, mf);
+               else
+                       goto retry_wait;
                goto out;
        }
 
@@ -2526,10 +2601,13 @@ mptctl_hp_hostinfo(unsigned long arg, unsigned int data_size)
         *   bays have drives in them
         * pbuf[3] = Checksum (0x100 = (byte0 + byte2 + byte3)
         */
-       if (ioc->ioctl->status & MPT_IOCTL_STATUS_RF_VALID)
+       if (ioc->ioctl_cmds.status & MPT_MGMT_STATUS_RF_VALID)
                karg.rsvd = *(u32 *)pbuf;
 
  out:
+       CLEAR_MGMT_STATUS(ioc->ioctl_cmds.status)
+       SET_MGMT_MSG_CONTEXT(ioc->ioctl_cmds.msg_context, 0);
+
        if (pbuf)
                pci_free_consistent(ioc->pcidev, 4, pbuf, buf_dma);
 
@@ -2753,7 +2831,7 @@ compat_mptfwxfer_ioctl(struct file *filp, unsigned int cmd,
 
        ret = mptctl_do_fw_download(kfw.iocnum, kfw.bufp, kfw.fwlen);
 
-       mutex_unlock(&iocp->ioctl->ioctl_mutex);
+       mutex_unlock(&iocp->ioctl_cmds.mutex);
 
        return ret;
 }
@@ -2807,7 +2885,7 @@ compat_mpt_command(struct file *filp, unsigned int cmd,
         */
        ret = mptctl_do_mpt_command (karg, &uarg->MF);
 
-       mutex_unlock(&iocp->ioctl->ioctl_mutex);
+       mutex_unlock(&iocp->ioctl_cmds.mutex);
 
        return ret;
 }
@@ -2859,21 +2937,10 @@ static long compat_mpctl_ioctl(struct file *f, unsigned int cmd, unsigned long a
 static int
 mptctl_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
-       MPT_IOCTL *mem;
        MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
 
-       /*
-        * Allocate and inite a MPT_IOCTL structure
-       */
-       mem = kzalloc(sizeof(MPT_IOCTL), GFP_KERNEL);
-       if (!mem) {
-               mptctl_remove(pdev);
-               return -ENOMEM;
-       }
-
-       ioc->ioctl = mem;
-       ioc->ioctl->ioc = ioc;
-       mutex_init(&ioc->ioctl->ioctl_mutex);
+       mutex_init(&ioc->ioctl_cmds.mutex);
+       init_completion(&ioc->ioctl_cmds.done);
        return 0;
 }
 
@@ -2887,9 +2954,6 @@ mptctl_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 static void
 mptctl_remove(struct pci_dev *pdev)
 {
-       MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
-
-       kfree ( ioc->ioctl );
 }
 
 static struct mpt_pci_driver mptctl_driver = {
@@ -2929,6 +2993,7 @@ static int __init mptctl_init(void)
                goto out_fail;
        }
 
+       mptctl_taskmgmt_id = mpt_register(mptctl_taskmgmt_reply, MPTCTL_DRIVER);
        mpt_reset_register(mptctl_id, mptctl_ioc_reset);
        mpt_event_register(mptctl_id, mptctl_event_process);
 
@@ -2953,6 +3018,7 @@ static void mptctl_exit(void)
 
        /* De-register callback handler from base module */
        mpt_deregister(mptctl_id);
+       mpt_reset_deregister(mptctl_taskmgmt_id);
 
         mpt_device_driver_deregister(MPTCTL_DRIVER);
 
index 510b9f492093432ea63ef5d03dadd39cd80ca048..28e4788792848d4854618ce9d0c8f2146399a998 100644 (file)
@@ -58,6 +58,7 @@
 #define MPT_DEBUG_FC                   0x00080000
 #define MPT_DEBUG_SAS                  0x00100000
 #define MPT_DEBUG_SAS_WIDE             0x00200000
+#define MPT_DEBUG_36GB_MEM              0x00400000
 
 /*
  * CONFIG_FUSION_LOGGING - enabled in Kconfig
 #define dsaswideprintk(IOC, CMD)               \
        MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SAS_WIDE)
 
+#define d36memprintk(IOC, CMD)         \
+       MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_36GB_MEM)
 
 
 /*
index c3c24fdf9fb621c20acb8249496c6440cad8b8b6..e61df133a59e8a934b25e9d93d419af6e6a50899 100644 (file)
@@ -1251,17 +1251,15 @@ mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
         * A slightly different algorithm is required for
         * 64bit SGEs.
         */
-       scale = ioc->req_sz/(sizeof(dma_addr_t) + sizeof(u32));
-       if (sizeof(dma_addr_t) == sizeof(u64)) {
+       scale = ioc->req_sz/ioc->SGE_size;
+       if (ioc->sg_addr_size == sizeof(u64)) {
                numSGE = (scale - 1) *
                  (ioc->facts.MaxChainDepth-1) + scale +
-                 (ioc->req_sz - 60) / (sizeof(dma_addr_t) +
-                 sizeof(u32));
+                 (ioc->req_sz - 60) / ioc->SGE_size;
        } else {
                numSGE = 1 + (scale - 1) *
                  (ioc->facts.MaxChainDepth-1) + scale +
-                 (ioc->req_sz - 64) / (sizeof(dma_addr_t) +
-                 sizeof(u32));
+                 (ioc->req_sz - 64) / ioc->SGE_size;
        }
 
        if (numSGE < sh->sg_tablesize) {
@@ -1292,9 +1290,6 @@ mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
        /* Clear the TM flags
         */
-       hd->tmPending = 0;
-       hd->tmState = TM_STATE_NONE;
-       hd->resetPending = 0;
        hd->abortSCpnt = NULL;
 
        /* Clear the pointer used to store
@@ -1312,8 +1307,6 @@ mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        hd->timer.data = (unsigned long) hd;
        hd->timer.function = mptscsih_timer_expired;
 
-       init_waitqueue_head(&hd->scandv_waitq);
-       hd->scandv_wait_done = 0;
        hd->last_queue_full = 0;
 
        sh->transportt = mptfc_transport_template;
index 79f5433359f9b3fc1b346cb78b5d11dcd60d15de..20e0b447e8e83a0ce07455de4d7f56215f43a568 100644 (file)
@@ -93,8 +93,37 @@ static u8    mptsasDoneCtx = MPT_MAX_PROTOCOL_DRIVERS;
 static u8      mptsasTaskCtx = MPT_MAX_PROTOCOL_DRIVERS;
 static u8      mptsasInternalCtx = MPT_MAX_PROTOCOL_DRIVERS; /* Used only for internal commands */
 static u8      mptsasMgmtCtx = MPT_MAX_PROTOCOL_DRIVERS;
-
-static void mptsas_hotplug_work(struct work_struct *work);
+static u8      mptsasDeviceResetCtx = MPT_MAX_PROTOCOL_DRIVERS;
+
+static void mptsas_firmware_event_work(struct work_struct *work);
+static void mptsas_send_sas_event(struct fw_event_work *fw_event);
+static void mptsas_send_raid_event(struct fw_event_work *fw_event);
+static void mptsas_send_ir2_event(struct fw_event_work *fw_event);
+static void mptsas_parse_device_info(struct sas_identify *identify,
+               struct mptsas_devinfo *device_info);
+static inline void mptsas_set_rphy(MPT_ADAPTER *ioc,
+               struct mptsas_phyinfo *phy_info, struct sas_rphy *rphy);
+static struct mptsas_phyinfo   *mptsas_find_phyinfo_by_sas_address
+               (MPT_ADAPTER *ioc, u64 sas_address);
+static int mptsas_sas_device_pg0(MPT_ADAPTER *ioc,
+       struct mptsas_devinfo *device_info, u32 form, u32 form_specific);
+static int mptsas_sas_enclosure_pg0(MPT_ADAPTER *ioc,
+       struct mptsas_enclosure *enclosure, u32 form, u32 form_specific);
+static int mptsas_add_end_device(MPT_ADAPTER *ioc,
+       struct mptsas_phyinfo *phy_info);
+static void mptsas_del_end_device(MPT_ADAPTER *ioc,
+       struct mptsas_phyinfo *phy_info);
+static void mptsas_send_link_status_event(struct fw_event_work *fw_event);
+static struct mptsas_portinfo  *mptsas_find_portinfo_by_sas_address
+               (MPT_ADAPTER *ioc, u64 sas_address);
+static void mptsas_expander_delete(MPT_ADAPTER *ioc,
+               struct mptsas_portinfo *port_info, u8 force);
+static void mptsas_send_expander_event(struct fw_event_work *fw_event);
+static void mptsas_not_responding_devices(MPT_ADAPTER *ioc);
+static void mptsas_scan_sas_topology(MPT_ADAPTER *ioc);
+static void mptsas_broadcast_primative_work(struct fw_event_work *fw_event);
+static void mptsas_handle_queue_full_event(struct fw_event_work *fw_event);
+static void mptsas_volume_delete(MPT_ADAPTER *ioc, u8 id);
 
 static void mptsas_print_phy_data(MPT_ADAPTER *ioc,
                                        MPI_SAS_IO_UNIT0_PHY_DATA *phy_data)
@@ -218,30 +247,125 @@ static void mptsas_print_expander_pg1(MPT_ADAPTER *ioc, SasExpanderPage1_t *pg1)
            le16_to_cpu(pg1->AttachedDevHandle)));
 }
 
-static inline MPT_ADAPTER *phy_to_ioc(struct sas_phy *phy)
+/* inhibit sas firmware event handling */
+static void
+mptsas_fw_event_off(MPT_ADAPTER *ioc)
 {
-       struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
-       return ((MPT_SCSI_HOST *)shost->hostdata)->ioc;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ioc->fw_event_lock, flags);
+       ioc->fw_events_off = 1;
+       ioc->sas_discovery_quiesce_io = 0;
+       spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+
 }
 
-static inline MPT_ADAPTER *rphy_to_ioc(struct sas_rphy *rphy)
+/* enable sas firmware event handling */
+static void
+mptsas_fw_event_on(MPT_ADAPTER *ioc)
 {
-       struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent);
-       return ((MPT_SCSI_HOST *)shost->hostdata)->ioc;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ioc->fw_event_lock, flags);
+       ioc->fw_events_off = 0;
+       spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
 }
 
-static struct mptsas_portinfo *
-mptsas_get_hba_portinfo(MPT_ADAPTER *ioc)
+/* queue a sas firmware event */
+static void
+mptsas_add_fw_event(MPT_ADAPTER *ioc, struct fw_event_work *fw_event,
+    unsigned long delay)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&ioc->fw_event_lock, flags);
+       list_add_tail(&fw_event->list, &ioc->fw_event_list);
+       INIT_DELAYED_WORK(&fw_event->work, mptsas_firmware_event_work);
+       devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: add (fw_event=0x%p)\n",
+           ioc->name, __func__, fw_event));
+       queue_delayed_work(ioc->fw_event_q, &fw_event->work,
+           delay);
+       spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+}
+
+/* requeue a sas firmware event */
+static void
+mptsas_requeue_fw_event(MPT_ADAPTER *ioc, struct fw_event_work *fw_event,
+    unsigned long delay)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&ioc->fw_event_lock, flags);
+       devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: reschedule task "
+           "(fw_event=0x%p)\n", ioc->name, __func__, fw_event));
+       fw_event->retries++;
+       queue_delayed_work(ioc->fw_event_q, &fw_event->work,
+           msecs_to_jiffies(delay));
+       spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+}
+
+/* free memory assoicated to a sas firmware event */
+static void
+mptsas_free_fw_event(MPT_ADAPTER *ioc, struct fw_event_work *fw_event)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&ioc->fw_event_lock, flags);
+       devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: kfree (fw_event=0x%p)\n",
+           ioc->name, __func__, fw_event));
+       list_del(&fw_event->list);
+       kfree(fw_event);
+       spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+}
+
+/* walk the firmware event queue, and either stop or wait for
+ * outstanding events to complete */
+static void
+mptsas_cleanup_fw_event_q(MPT_ADAPTER *ioc)
 {
-       struct list_head        *head = &ioc->sas_topology;
-       struct mptsas_portinfo  *pi = NULL;
+       struct fw_event_work *fw_event, *next;
+       struct mptsas_target_reset_event *target_reset_list, *n;
+       u8      flush_q;
+       MPT_SCSI_HOST   *hd = shost_priv(ioc->sh);
+
+       /* flush the target_reset_list */
+       if (!list_empty(&hd->target_reset_list)) {
+               list_for_each_entry_safe(target_reset_list, n,
+                   &hd->target_reset_list, list) {
+                       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                           "%s: removing target reset for id=%d\n",
+                           ioc->name, __func__,
+                          target_reset_list->sas_event_data.TargetID));
+                       list_del(&target_reset_list->list);
+                       kfree(target_reset_list);
+               }
+       }
+
+       if (list_empty(&ioc->fw_event_list) ||
+            !ioc->fw_event_q || in_interrupt())
+               return;
+
+       flush_q = 0;
+       list_for_each_entry_safe(fw_event, next, &ioc->fw_event_list, list) {
+               if (cancel_delayed_work(&fw_event->work))
+                       mptsas_free_fw_event(ioc, fw_event);
+               else
+                       flush_q = 1;
+       }
+       if (flush_q)
+               flush_workqueue(ioc->fw_event_q);
+}
 
-       /* always the first entry on sas_topology list */
 
-       if (!list_empty(head))
-               pi = list_entry(head->next, struct mptsas_portinfo, list);
+static inline MPT_ADAPTER *phy_to_ioc(struct sas_phy *phy)
+{
+       struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
+       return ((MPT_SCSI_HOST *)shost->hostdata)->ioc;
+}
 
-       return pi;
+static inline MPT_ADAPTER *rphy_to_ioc(struct sas_rphy *rphy)
+{
+       struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent);
+       return ((MPT_SCSI_HOST *)shost->hostdata)->ioc;
 }
 
 /*
@@ -265,6 +389,38 @@ mptsas_find_portinfo_by_handle(MPT_ADAPTER *ioc, u16 handle)
        return rc;
 }
 
+/**
+ *     mptsas_find_portinfo_by_sas_address -
+ *     @ioc: Pointer to MPT_ADAPTER structure
+ *     @handle:
+ *
+ *     This function should be called with the sas_topology_mutex already held
+ *
+ **/
+static struct mptsas_portinfo *
+mptsas_find_portinfo_by_sas_address(MPT_ADAPTER *ioc, u64 sas_address)
+{
+       struct mptsas_portinfo *port_info, *rc = NULL;
+       int i;
+
+       if (sas_address >= ioc->hba_port_sas_addr &&
+           sas_address < (ioc->hba_port_sas_addr +
+           ioc->hba_port_num_phy))
+               return ioc->hba_port_info;
+
+       mutex_lock(&ioc->sas_topology_mutex);
+       list_for_each_entry(port_info, &ioc->sas_topology, list)
+               for (i = 0; i < port_info->num_phys; i++)
+                       if (port_info->phy_info[i].identify.sas_address ==
+                           sas_address) {
+                               rc = port_info;
+                               goto out;
+                       }
+ out:
+       mutex_unlock(&ioc->sas_topology_mutex);
+       return rc;
+}
+
 /*
  * Returns true if there is a scsi end device
  */
@@ -308,6 +464,7 @@ mptsas_port_delete(MPT_ADAPTER *ioc, struct mptsas_portinfo_details * port_detai
                if(phy_info->port_details != port_details)
                        continue;
                memset(&phy_info->attached, 0, sizeof(struct mptsas_devinfo));
+               mptsas_set_rphy(ioc, phy_info, NULL);
                phy_info->port_details = NULL;
        }
        kfree(port_details);
@@ -379,6 +536,285 @@ starget)
                phy_info->port_details->starget = starget;
 }
 
+/**
+ *     mptsas_add_device_component -
+ *     @ioc: Pointer to MPT_ADAPTER structure
+ *     @channel: fw mapped id's
+ *     @id:
+ *     @sas_address:
+ *     @device_info:
+ *
+ **/
+static void
+mptsas_add_device_component(MPT_ADAPTER *ioc, u8 channel, u8 id,
+       u64 sas_address, u32 device_info, u16 slot, u64 enclosure_logical_id)
+{
+       struct mptsas_device_info       *sas_info, *next;
+       struct scsi_device      *sdev;
+       struct scsi_target      *starget;
+       struct sas_rphy *rphy;
+
+       /*
+        * Delete all matching devices out of the list
+        */
+       mutex_lock(&ioc->sas_device_info_mutex);
+       list_for_each_entry_safe(sas_info, next, &ioc->sas_device_info_list,
+           list) {
+               if (!sas_info->is_logical_volume &&
+                   (sas_info->sas_address == sas_address ||
+                   (sas_info->fw.channel == channel &&
+                    sas_info->fw.id == id))) {
+                       list_del(&sas_info->list);
+                       kfree(sas_info);
+               }
+       }
+
+       sas_info = kzalloc(sizeof(struct mptsas_device_info), GFP_KERNEL);
+       if (!sas_info)
+               goto out;
+
+       /*
+        * Set Firmware mapping
+        */
+       sas_info->fw.id = id;
+       sas_info->fw.channel = channel;
+
+       sas_info->sas_address = sas_address;
+       sas_info->device_info = device_info;
+       sas_info->slot = slot;
+       sas_info->enclosure_logical_id = enclosure_logical_id;
+       INIT_LIST_HEAD(&sas_info->list);
+       list_add_tail(&sas_info->list, &ioc->sas_device_info_list);
+
+       /*
+        * Set OS mapping
+        */
+       shost_for_each_device(sdev, ioc->sh) {
+               starget = scsi_target(sdev);
+               rphy = dev_to_rphy(starget->dev.parent);
+               if (rphy->identify.sas_address == sas_address) {
+                       sas_info->os.id = starget->id;
+                       sas_info->os.channel = starget->channel;
+               }
+       }
+
+ out:
+       mutex_unlock(&ioc->sas_device_info_mutex);
+       return;
+}
+
+/**
+ *     mptsas_add_device_component_by_fw -
+ *     @ioc: Pointer to MPT_ADAPTER structure
+ *     @channel:  fw mapped id's
+ *     @id:
+ *
+ **/
+static void
+mptsas_add_device_component_by_fw(MPT_ADAPTER *ioc, u8 channel, u8 id)
+{
+       struct mptsas_devinfo sas_device;
+       struct mptsas_enclosure enclosure_info;
+       int rc;
+
+       rc = mptsas_sas_device_pg0(ioc, &sas_device,
+           (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
+            MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
+           (channel << 8) + id);
+       if (rc)
+               return;
+
+       memset(&enclosure_info, 0, sizeof(struct mptsas_enclosure));
+       mptsas_sas_enclosure_pg0(ioc, &enclosure_info,
+           (MPI_SAS_ENCLOS_PGAD_FORM_HANDLE <<
+            MPI_SAS_ENCLOS_PGAD_FORM_SHIFT),
+            sas_device.handle_enclosure);
+
+       mptsas_add_device_component(ioc, sas_device.channel,
+           sas_device.id, sas_device.sas_address, sas_device.device_info,
+           sas_device.slot, enclosure_info.enclosure_logical_id);
+}
+
+/**
+ *     mptsas_add_device_component_starget_ir - Handle Integrated RAID, adding each individual device to list
+ *     @ioc: Pointer to MPT_ADAPTER structure
+ *     @channel: fw mapped id's
+ *     @id:
+ *
+ **/
+static void
+mptsas_add_device_component_starget_ir(MPT_ADAPTER *ioc,
+               struct scsi_target *starget)
+{
+       CONFIGPARMS                     cfg;
+       ConfigPageHeader_t              hdr;
+       dma_addr_t                      dma_handle;
+       pRaidVolumePage0_t              buffer = NULL;
+       int                             i;
+       RaidPhysDiskPage0_t             phys_disk;
+       struct mptsas_device_info       *sas_info, *next;
+
+       memset(&cfg, 0 , sizeof(CONFIGPARMS));
+       memset(&hdr, 0 , sizeof(ConfigPageHeader_t));
+       hdr.PageType = MPI_CONFIG_PAGETYPE_RAID_VOLUME;
+       /* assumption that all volumes on channel = 0 */
+       cfg.pageAddr = starget->id;
+       cfg.cfghdr.hdr = &hdr;
+       cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+       cfg.timeout = 10;
+
+       if (mpt_config(ioc, &cfg) != 0)
+               goto out;
+
+       if (!hdr.PageLength)
+               goto out;
+
+       buffer = pci_alloc_consistent(ioc->pcidev, hdr.PageLength * 4,
+           &dma_handle);
+
+       if (!buffer)
+               goto out;
+
+       cfg.physAddr = dma_handle;
+       cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+
+       if (mpt_config(ioc, &cfg) != 0)
+               goto out;
+
+       if (!buffer->NumPhysDisks)
+               goto out;
+
+       /*
+        * Adding entry for hidden components
+        */
+       for (i = 0; i < buffer->NumPhysDisks; i++) {
+
+               if (mpt_raid_phys_disk_pg0(ioc,
+                   buffer->PhysDisk[i].PhysDiskNum, &phys_disk) != 0)
+                       continue;
+
+               mptsas_add_device_component_by_fw(ioc, phys_disk.PhysDiskBus,
+                   phys_disk.PhysDiskID);
+
+               mutex_lock(&ioc->sas_device_info_mutex);
+               list_for_each_entry(sas_info, &ioc->sas_device_info_list,
+                   list) {
+                       if (!sas_info->is_logical_volume &&
+                           (sas_info->fw.channel == phys_disk.PhysDiskBus &&
+                           sas_info->fw.id == phys_disk.PhysDiskID)) {
+                               sas_info->is_hidden_raid_component = 1;
+                               sas_info->volume_id = starget->id;
+                       }
+               }
+               mutex_unlock(&ioc->sas_device_info_mutex);
+
+       }
+
+       /*
+        * Delete all matching devices out of the list
+        */
+       mutex_lock(&ioc->sas_device_info_mutex);
+       list_for_each_entry_safe(sas_info, next, &ioc->sas_device_info_list,
+           list) {
+               if (sas_info->is_logical_volume && sas_info->fw.id ==
+                   starget->id) {
+                       list_del(&sas_info->list);
+                       kfree(sas_info);
+               }
+       }
+
+       sas_info = kzalloc(sizeof(struct mptsas_device_info), GFP_KERNEL);
+       if (sas_info) {
+               sas_info->fw.id = starget->id;
+               sas_info->os.id = starget->id;
+               sas_info->os.channel = starget->channel;
+               sas_info->is_logical_volume = 1;
+               INIT_LIST_HEAD(&sas_info->list);
+               list_add_tail(&sas_info->list, &ioc->sas_device_info_list);
+       }
+       mutex_unlock(&ioc->sas_device_info_mutex);
+
+ out:
+       if (buffer)
+               pci_free_consistent(ioc->pcidev, hdr.PageLength * 4, buffer,
+                   dma_handle);
+}
+
+/**
+ *     mptsas_add_device_component_starget -
+ *     @ioc: Pointer to MPT_ADAPTER structure
+ *     @starget:
+ *
+ **/
+static void
+mptsas_add_device_component_starget(MPT_ADAPTER *ioc,
+       struct scsi_target *starget)
+{
+       VirtTarget      *vtarget;
+       struct sas_rphy *rphy;
+       struct mptsas_phyinfo   *phy_info = NULL;
+       struct mptsas_enclosure enclosure_info;
+
+       rphy = dev_to_rphy(starget->dev.parent);
+       vtarget = starget->hostdata;
+       phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
+                       rphy->identify.sas_address);
+       if (!phy_info)
+               return;
+
+       memset(&enclosure_info, 0, sizeof(struct mptsas_enclosure));
+       mptsas_sas_enclosure_pg0(ioc, &enclosure_info,
+               (MPI_SAS_ENCLOS_PGAD_FORM_HANDLE <<
+               MPI_SAS_ENCLOS_PGAD_FORM_SHIFT),
+               phy_info->attached.handle_enclosure);
+
+       mptsas_add_device_component(ioc, phy_info->attached.channel,
+               phy_info->attached.id, phy_info->attached.sas_address,
+               phy_info->attached.device_info,
+               phy_info->attached.slot, enclosure_info.enclosure_logical_id);
+}
+
+/**
+ *     mptsas_del_device_component_by_os - Once a device has been removed, we mark the entry in the list as being cached
+ *     @ioc: Pointer to MPT_ADAPTER structure
+ *     @channel: os mapped id's
+ *     @id:
+ *
+ **/
+static void
+mptsas_del_device_component_by_os(MPT_ADAPTER *ioc, u8 channel, u8 id)
+{
+       struct mptsas_device_info       *sas_info, *next;
+
+       /*
+        * Set is_cached flag
+        */
+       list_for_each_entry_safe(sas_info, next, &ioc->sas_device_info_list,
+               list) {
+               if (sas_info->os.channel == channel && sas_info->os.id == id)
+                       sas_info->is_cached = 1;
+       }
+}
+
+/**
+ *     mptsas_del_device_components - Cleaning the list
+ *     @ioc: Pointer to MPT_ADAPTER structure
+ *
+ **/
+static void
+mptsas_del_device_components(MPT_ADAPTER *ioc)
+{
+       struct mptsas_device_info       *sas_info, *next;
+
+       mutex_lock(&ioc->sas_device_info_mutex);
+       list_for_each_entry_safe(sas_info, next, &ioc->sas_device_info_list,
+               list) {
+               list_del(&sas_info->list);
+               kfree(sas_info);
+       }
+       mutex_unlock(&ioc->sas_device_info_mutex);
+}
+
 
 /*
  * mptsas_setup_wide_ports
@@ -434,8 +870,8 @@ mptsas_setup_wide_ports(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info)
                 * Forming a port
                 */
                if (!port_details) {
-                       port_details = kzalloc(sizeof(*port_details),
-                               GFP_KERNEL);
+                       port_details = kzalloc(sizeof(struct
+                               mptsas_portinfo_details), GFP_KERNEL);
                        if (!port_details)
                                goto out;
                        port_details->num_phys = 1;
@@ -523,15 +959,62 @@ mptsas_find_vtarget(MPT_ADAPTER *ioc, u8 channel, u8 id)
        VirtTarget                      *vtarget = NULL;
 
        shost_for_each_device(sdev, ioc->sh) {
-               if ((vdevice = sdev->hostdata) == NULL)
+               vdevice = sdev->hostdata;
+               if ((vdevice == NULL) ||
+                       (vdevice->vtarget == NULL))
+                       continue;
+               if ((vdevice->vtarget->tflags &
+                   MPT_TARGET_FLAGS_RAID_COMPONENT ||
+                   vdevice->vtarget->raidVolume))
                        continue;
                if (vdevice->vtarget->id == id &&
-                   vdevice->vtarget->channel == channel)
+                       vdevice->vtarget->channel == channel)
                        vtarget = vdevice->vtarget;
        }
        return vtarget;
 }
 
+static void
+mptsas_queue_device_delete(MPT_ADAPTER *ioc,
+       MpiEventDataSasDeviceStatusChange_t *sas_event_data)
+{
+       struct fw_event_work *fw_event;
+       int sz;
+
+       sz = offsetof(struct fw_event_work, event_data) +
+           sizeof(MpiEventDataSasDeviceStatusChange_t);
+       fw_event = kzalloc(sz, GFP_ATOMIC);
+       if (!fw_event) {
+               printk(MYIOC_s_WARN_FMT "%s: failed at (line=%d)\n",
+                   ioc->name, __func__, __LINE__);
+               return;
+       }
+       memcpy(fw_event->event_data, sas_event_data,
+           sizeof(MpiEventDataSasDeviceStatusChange_t));
+       fw_event->event = MPI_EVENT_SAS_DEVICE_STATUS_CHANGE;
+       fw_event->ioc = ioc;
+       mptsas_add_fw_event(ioc, fw_event, msecs_to_jiffies(1));
+}
+
+static void
+mptsas_queue_rescan(MPT_ADAPTER *ioc)
+{
+       struct fw_event_work *fw_event;
+       int sz;
+
+       sz = offsetof(struct fw_event_work, event_data);
+       fw_event = kzalloc(sz, GFP_ATOMIC);
+       if (!fw_event) {
+               printk(MYIOC_s_WARN_FMT "%s: failed at (line=%d)\n",
+                   ioc->name, __func__, __LINE__);
+               return;
+       }
+       fw_event->event = -1;
+       fw_event->ioc = ioc;
+       mptsas_add_fw_event(ioc, fw_event, msecs_to_jiffies(1));
+}
+
+
 /**
  * mptsas_target_reset
  *
@@ -550,13 +1033,21 @@ mptsas_target_reset(MPT_ADAPTER *ioc, u8 channel, u8 id)
 {
        MPT_FRAME_HDR   *mf;
        SCSITaskMgmt_t  *pScsiTm;
-
-       if ((mf = mpt_get_msg_frame(ioc->TaskCtx, ioc)) == NULL) {
-               dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, no msg frames @%d!!\n",
-                   ioc->name,__func__, __LINE__));
+       if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0)
                return 0;
+
+
+       mf = mpt_get_msg_frame(mptsasDeviceResetCtx, ioc);
+       if (mf == NULL) {
+               dfailprintk(ioc, printk(MYIOC_s_WARN_FMT
+                       "%s, no msg frames @%d!!\n", ioc->name,
+                       __func__, __LINE__));
+               goto out_fail;
        }
 
+       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt request (mf=%p)\n",
+               ioc->name, mf));
+
        /* Format the Request
         */
        pScsiTm = (SCSITaskMgmt_t *) mf;
@@ -569,9 +1060,18 @@ mptsas_target_reset(MPT_ADAPTER *ioc, u8 channel, u8 id)
 
        DBG_DUMP_TM_REQUEST_FRAME(ioc, (u32 *)mf);
 
-       mpt_put_msg_frame_hi_pri(ioc->TaskCtx, ioc, mf);
+       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+          "TaskMgmt type=%d (sas device delete) fw_channel = %d fw_id = %d)\n",
+          ioc->name, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET, channel, id));
+
+       mpt_put_msg_frame_hi_pri(mptsasDeviceResetCtx, ioc, mf);
 
        return 1;
+
+ out_fail:
+
+       mpt_clear_taskmgmt_in_progress_flag(ioc);
+       return 0;
 }
 
 /**
@@ -602,11 +1102,12 @@ mptsas_target_reset_queue(MPT_ADAPTER *ioc,
 
        vtarget->deleted = 1; /* block IO */
 
-       target_reset_list = kzalloc(sizeof(*target_reset_list),
+       target_reset_list = kzalloc(sizeof(struct mptsas_target_reset_event),
            GFP_ATOMIC);
        if (!target_reset_list) {
-               dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, failed to allocate mem @%d..!!\n",
-                   ioc->name,__func__, __LINE__));
+               dfailprintk(ioc, printk(MYIOC_s_WARN_FMT
+                       "%s, failed to allocate mem @%d..!!\n",
+                       ioc->name, __func__, __LINE__));
                return;
        }
 
@@ -614,84 +1115,101 @@ mptsas_target_reset_queue(MPT_ADAPTER *ioc,
                sizeof(*sas_event_data));
        list_add_tail(&target_reset_list->list, &hd->target_reset_list);
 
-       if (hd->resetPending)
-               return;
+       target_reset_list->time_count = jiffies;
 
        if (mptsas_target_reset(ioc, channel, id)) {
                target_reset_list->target_reset_issued = 1;
-               hd->resetPending = 1;
        }
 }
 
 /**
- * mptsas_dev_reset_complete
- *
- * Completion for TARGET_RESET after NOT_RESPONDING_EVENT,
- * enable work queue to finish off removing device from upper layers.
- * then send next TARGET_RESET in the queue.
- *
- * @ioc
+ *     mptsas_taskmgmt_complete - complete SAS task management function
+ *     @ioc: Pointer to MPT_ADAPTER structure
  *
+ *     Completion for TARGET_RESET after NOT_RESPONDING_EVENT, enable work
+ *     queue to finish off removing device from upper layers. then send next
+ *     TARGET_RESET in the queue.
  **/
-static void
-mptsas_dev_reset_complete(MPT_ADAPTER *ioc)
+static int
+mptsas_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
 {
        MPT_SCSI_HOST   *hd = shost_priv(ioc->sh);
         struct list_head *head = &hd->target_reset_list;
-       struct mptsas_target_reset_event *target_reset_list;
-       struct mptsas_hotplug_event *ev;
-       EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data;
        u8              id, channel;
-       __le64          sas_address;
+       struct mptsas_target_reset_event        *target_reset_list;
+       SCSITaskMgmtReply_t *pScsiTmReply;
+
+       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt completed: "
+           "(mf = %p, mr = %p)\n", ioc->name, mf, mr));
+
+       pScsiTmReply = (SCSITaskMgmtReply_t *)mr;
+       if (pScsiTmReply) {
+               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "\tTaskMgmt completed: fw_channel = %d, fw_id = %d,\n"
+                   "\ttask_type = 0x%02X, iocstatus = 0x%04X "
+                   "loginfo = 0x%08X,\n\tresponse_code = 0x%02X, "
+                   "term_cmnds = %d\n", ioc->name,
+                   pScsiTmReply->Bus, pScsiTmReply->TargetID,
+                   pScsiTmReply->TaskType,
+                   le16_to_cpu(pScsiTmReply->IOCStatus),
+                   le32_to_cpu(pScsiTmReply->IOCLogInfo),
+                   pScsiTmReply->ResponseCode,
+                   le32_to_cpu(pScsiTmReply->TerminationCount)));
+
+               if (pScsiTmReply->ResponseCode)
+                       mptscsih_taskmgmt_response_code(ioc,
+                       pScsiTmReply->ResponseCode);
+       }
+
+       if (pScsiTmReply && (pScsiTmReply->TaskType ==
+           MPI_SCSITASKMGMT_TASKTYPE_QUERY_TASK || pScsiTmReply->TaskType ==
+            MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET)) {
+               ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_COMMAND_GOOD;
+               ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_RF_VALID;
+               memcpy(ioc->taskmgmt_cmds.reply, mr,
+                   min(MPT_DEFAULT_FRAME_SIZE, 4 * mr->u.reply.MsgLength));
+               if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_PENDING) {
+                       ioc->taskmgmt_cmds.status &= ~MPT_MGMT_STATUS_PENDING;
+                       complete(&ioc->taskmgmt_cmds.done);
+                       return 1;
+               }
+               return 0;
+       }
+
+       mpt_clear_taskmgmt_in_progress_flag(ioc);
 
        if (list_empty(head))
-               return;
+               return 1;
 
-       target_reset_list = list_entry(head->next, struct mptsas_target_reset_event, list);
+       target_reset_list = list_entry(head->next,
+           struct mptsas_target_reset_event, list);
 
-       sas_event_data = &target_reset_list->sas_event_data;
-       id = sas_event_data->TargetID;
-       channel = sas_event_data->Bus;
-       hd->resetPending = 0;
+       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+           "TaskMgmt: completed (%d seconds)\n",
+           ioc->name, jiffies_to_msecs(jiffies -
+           target_reset_list->time_count)/1000));
+
+       id = pScsiTmReply->TargetID;
+       channel = pScsiTmReply->Bus;
+       target_reset_list->time_count = jiffies;
 
        /*
         * retry target reset
         */
        if (!target_reset_list->target_reset_issued) {
-               if (mptsas_target_reset(ioc, channel, id)) {
+               if (mptsas_target_reset(ioc, channel, id))
                        target_reset_list->target_reset_issued = 1;
-                       hd->resetPending = 1;
-               }
-               return;
+               return 1;
        }
 
        /*
         * enable work queue to remove device from upper layers
         */
        list_del(&target_reset_list->list);
+       if ((mptsas_find_vtarget(ioc, channel, id)) && !ioc->fw_events_off)
+               mptsas_queue_device_delete(ioc,
+                       &target_reset_list->sas_event_data);
 
-       ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
-       if (!ev) {
-               dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, failed to allocate mem @%d..!!\n",
-                   ioc->name,__func__, __LINE__));
-               return;
-       }
-
-       INIT_WORK(&ev->work, mptsas_hotplug_work);
-       ev->ioc = ioc;
-       ev->handle = le16_to_cpu(sas_event_data->DevHandle);
-       ev->parent_handle =
-           le16_to_cpu(sas_event_data->ParentDevHandle);
-       ev->channel = channel;
-       ev->id =id;
-       ev->phy_id = sas_event_data->PhyNum;
-       memcpy(&sas_address, &sas_event_data->SASAddress,
-           sizeof(__le64));
-       ev->sas_address = le64_to_cpu(sas_address);
-       ev->device_info = le32_to_cpu(sas_event_data->DeviceInfo);
-       ev->event_type = MPTSAS_DEL_DEVICE;
-       schedule_work(&ev->work);
-       kfree(target_reset_list);
 
        /*
         * issue target reset to next device in the queue
@@ -699,34 +1217,19 @@ mptsas_dev_reset_complete(MPT_ADAPTER *ioc)
 
        head = &hd->target_reset_list;
        if (list_empty(head))
-               return;
+               return 1;
 
        target_reset_list = list_entry(head->next, struct mptsas_target_reset_event,
            list);
 
-       sas_event_data = &target_reset_list->sas_event_data;
-       id = sas_event_data->TargetID;
-       channel = sas_event_data->Bus;
+       id = target_reset_list->sas_event_data.TargetID;
+       channel = target_reset_list->sas_event_data.Bus;
+       target_reset_list->time_count = jiffies;
 
-       if (mptsas_target_reset(ioc, channel, id)) {
+       if (mptsas_target_reset(ioc, channel, id))
                target_reset_list->target_reset_issued = 1;
-               hd->resetPending = 1;
-       }
-}
 
-/**
- * mptsas_taskmgmt_complete
- *
- * @ioc
- * @mf
- * @mr
- *
- **/
-static int
-mptsas_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
-{
-       mptsas_dev_reset_complete(ioc);
-       return mptscsih_taskmgmt_complete(ioc, mf, mr);
+       return 1;
 }
 
 /**
@@ -740,37 +1243,59 @@ static int
 mptsas_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
 {
        MPT_SCSI_HOST   *hd;
-       struct mptsas_target_reset_event *target_reset_list, *n;
        int rc;
 
        rc = mptscsih_ioc_reset(ioc, reset_phase);
+       if ((ioc->bus_type != SAS) || (!rc))
+               return rc;
 
-       if (ioc->bus_type != SAS)
-               goto out;
-
-       if (reset_phase != MPT_IOC_POST_RESET)
-               goto out;
-
-       if (!ioc->sh || !ioc->sh->hostdata)
-               goto out;
        hd = shost_priv(ioc->sh);
        if (!hd->ioc)
                goto out;
 
-       if (list_empty(&hd->target_reset_list))
-               goto out;
-
-       /* flush the target_reset_list */
-       list_for_each_entry_safe(target_reset_list, n,
-           &hd->target_reset_list, list) {
-               list_del(&target_reset_list->list);
-               kfree(target_reset_list);
+       switch (reset_phase) {
+       case MPT_IOC_SETUP_RESET:
+               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "%s: MPT_IOC_SETUP_RESET\n", ioc->name, __func__));
+               mptsas_fw_event_off(ioc);
+               break;
+       case MPT_IOC_PRE_RESET:
+               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "%s: MPT_IOC_PRE_RESET\n", ioc->name, __func__));
+               break;
+       case MPT_IOC_POST_RESET:
+               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "%s: MPT_IOC_POST_RESET\n", ioc->name, __func__));
+               if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_PENDING) {
+                       ioc->sas_mgmt.status |= MPT_MGMT_STATUS_DID_IOCRESET;
+                       complete(&ioc->sas_mgmt.done);
+               }
+               mptsas_cleanup_fw_event_q(ioc);
+               mptsas_queue_rescan(ioc);
+               mptsas_fw_event_on(ioc);
+               break;
+       default:
+               break;
        }
 
  out:
        return rc;
 }
 
+
+/**
+ * enum device_state -
+ * @DEVICE_RETRY: need to retry the TUR
+ * @DEVICE_ERROR: TUR return error, don't add device
+ * @DEVICE_READY: device can be added
+ *
+ */
+enum device_state{
+       DEVICE_RETRY,
+       DEVICE_ERROR,
+       DEVICE_READY,
+};
+
 static int
 mptsas_sas_enclosure_pg0(MPT_ADAPTER *ioc, struct mptsas_enclosure *enclosure,
                u32 form, u32 form_specific)
@@ -836,15 +1361,308 @@ mptsas_sas_enclosure_pg0(MPT_ADAPTER *ioc, struct mptsas_enclosure *enclosure,
        return error;
 }
 
+/**
+ *     mptsas_add_end_device - report a new end device to sas transport layer
+ *     @ioc: Pointer to MPT_ADAPTER structure
+ *     @phy_info: decribes attached device
+ *
+ *     return (0) success (1) failure
+ *
+ **/
+static int
+mptsas_add_end_device(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info)
+{
+       struct sas_rphy *rphy;
+       struct sas_port *port;
+       struct sas_identify identify;
+       char *ds = NULL;
+       u8 fw_id;
+
+       if (!phy_info) {
+               dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+                       "%s: exit at line=%d\n", ioc->name,
+                        __func__, __LINE__));
+               return 1;
+       }
+
+       fw_id = phy_info->attached.id;
+
+       if (mptsas_get_rphy(phy_info)) {
+               dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+                       "%s: fw_id=%d exit at line=%d\n", ioc->name,
+                        __func__, fw_id, __LINE__));
+               return 2;
+       }
+
+       port = mptsas_get_port(phy_info);
+       if (!port) {
+               dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+                       "%s: fw_id=%d exit at line=%d\n", ioc->name,
+                        __func__, fw_id, __LINE__));
+               return 3;
+       }
+
+       if (phy_info->attached.device_info &
+           MPI_SAS_DEVICE_INFO_SSP_TARGET)
+               ds = "ssp";
+       if (phy_info->attached.device_info &
+           MPI_SAS_DEVICE_INFO_STP_TARGET)
+               ds = "stp";
+       if (phy_info->attached.device_info &
+           MPI_SAS_DEVICE_INFO_SATA_DEVICE)
+               ds = "sata";
+
+       printk(MYIOC_s_INFO_FMT "attaching %s device: fw_channel %d, fw_id %d,"
+           " phy %d, sas_addr 0x%llx\n", ioc->name, ds,
+           phy_info->attached.channel, phy_info->attached.id,
+           phy_info->attached.phy_id, (unsigned long long)
+           phy_info->attached.sas_address);
+
+       mptsas_parse_device_info(&identify, &phy_info->attached);
+       rphy = sas_end_device_alloc(port);
+       if (!rphy) {
+               dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+                       "%s: fw_id=%d exit at line=%d\n", ioc->name,
+                        __func__, fw_id, __LINE__));
+               return 5; /* non-fatal: an rphy can be added later */
+       }
+
+       rphy->identify = identify;
+       if (sas_rphy_add(rphy)) {
+               dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+                       "%s: fw_id=%d exit at line=%d\n", ioc->name,
+                        __func__, fw_id, __LINE__));
+               sas_rphy_free(rphy);
+               return 6;
+       }
+       mptsas_set_rphy(ioc, phy_info, rphy);
+       return 0;
+}
+
+/**
+ *     mptsas_del_end_device - report a deleted end device to sas transport layer
+ *     @ioc: Pointer to MPT_ADAPTER structure
+ *     @phy_info: decribes attached device
+ *
+ **/
+static void
+mptsas_del_end_device(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info)
+{
+       struct sas_rphy *rphy;
+       struct sas_port *port;
+       struct mptsas_portinfo *port_info;
+       struct mptsas_phyinfo *phy_info_parent;
+       int i;
+       char *ds = NULL;
+       u8 fw_id;
+       u64 sas_address;
+
+       if (!phy_info)
+               return;
+
+       fw_id = phy_info->attached.id;
+       sas_address = phy_info->attached.sas_address;
+
+       if (!phy_info->port_details) {
+               dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+                       "%s: fw_id=%d exit at line=%d\n", ioc->name,
+                        __func__, fw_id, __LINE__));
+               return;
+       }
+       rphy = mptsas_get_rphy(phy_info);
+       if (!rphy) {
+               dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+                       "%s: fw_id=%d exit at line=%d\n", ioc->name,
+                        __func__, fw_id, __LINE__));
+               return;
+       }
+
+       if (phy_info->attached.device_info & MPI_SAS_DEVICE_INFO_SSP_INITIATOR
+               || phy_info->attached.device_info
+                       & MPI_SAS_DEVICE_INFO_SMP_INITIATOR
+               || phy_info->attached.device_info
+                       & MPI_SAS_DEVICE_INFO_STP_INITIATOR)
+               ds = "initiator";
+       if (phy_info->attached.device_info &
+           MPI_SAS_DEVICE_INFO_SSP_TARGET)
+               ds = "ssp";
+       if (phy_info->attached.device_info &
+           MPI_SAS_DEVICE_INFO_STP_TARGET)
+               ds = "stp";
+       if (phy_info->attached.device_info &
+           MPI_SAS_DEVICE_INFO_SATA_DEVICE)
+               ds = "sata";
+
+       dev_printk(KERN_DEBUG, &rphy->dev, MYIOC_s_FMT
+           "removing %s device: fw_channel %d, fw_id %d, phy %d,"
+           "sas_addr 0x%llx\n", ioc->name, ds, phy_info->attached.channel,
+           phy_info->attached.id, phy_info->attached.phy_id,
+           (unsigned long long) sas_address);
+
+       port = mptsas_get_port(phy_info);
+       if (!port) {
+               dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+                       "%s: fw_id=%d exit at line=%d\n", ioc->name,
+                        __func__, fw_id, __LINE__));
+               return;
+       }
+       port_info = phy_info->portinfo;
+       phy_info_parent = port_info->phy_info;
+       for (i = 0; i < port_info->num_phys; i++, phy_info_parent++) {
+               if (!phy_info_parent->phy)
+                       continue;
+               if (phy_info_parent->attached.sas_address !=
+                   sas_address)
+                       continue;
+               dev_printk(KERN_DEBUG, &phy_info_parent->phy->dev,
+                   MYIOC_s_FMT "delete phy %d, phy-obj (0x%p)\n",
+                   ioc->name, phy_info_parent->phy_id,
+                   phy_info_parent->phy);
+               sas_port_delete_phy(port, phy_info_parent->phy);
+       }
+
+       dev_printk(KERN_DEBUG, &port->dev, MYIOC_s_FMT
+           "delete port %d, sas_addr (0x%llx)\n", ioc->name,
+            port->port_identifier, (unsigned long long)sas_address);
+       sas_port_delete(port);
+       mptsas_set_port(ioc, phy_info, NULL);
+       mptsas_port_delete(ioc, phy_info->port_details);
+}
+
+struct mptsas_phyinfo *
+mptsas_refreshing_device_handles(MPT_ADAPTER *ioc,
+       struct mptsas_devinfo *sas_device)
+{
+       struct mptsas_phyinfo *phy_info;
+       struct mptsas_portinfo *port_info;
+       int i;
+
+       phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
+           sas_device->sas_address);
+       if (!phy_info)
+               goto out;
+       port_info = phy_info->portinfo;
+       if (!port_info)
+               goto out;
+       mutex_lock(&ioc->sas_topology_mutex);
+       for (i = 0; i < port_info->num_phys; i++) {
+               if (port_info->phy_info[i].attached.sas_address !=
+                       sas_device->sas_address)
+                       continue;
+               port_info->phy_info[i].attached.channel = sas_device->channel;
+               port_info->phy_info[i].attached.id = sas_device->id;
+               port_info->phy_info[i].attached.sas_address =
+                   sas_device->sas_address;
+               port_info->phy_info[i].attached.handle = sas_device->handle;
+               port_info->phy_info[i].attached.handle_parent =
+                   sas_device->handle_parent;
+               port_info->phy_info[i].attached.handle_enclosure =
+                   sas_device->handle_enclosure;
+       }
+       mutex_unlock(&ioc->sas_topology_mutex);
+ out:
+       return phy_info;
+}
+
+/**
+ * mptsas_firmware_event_work - work thread for processing fw events
+ * @work: work queue payload containing info describing the event
+ * Context: user
+ *
+ */
+static void
+mptsas_firmware_event_work(struct work_struct *work)
+{
+       struct fw_event_work *fw_event =
+               container_of(work, struct fw_event_work, work.work);
+       MPT_ADAPTER *ioc = fw_event->ioc;
+
+       /* special rescan topology handling */
+       if (fw_event->event == -1) {
+               if (ioc->in_rescan) {
+                       devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                               "%s: rescan ignored as it is in progress\n",
+                               ioc->name, __func__));
+                       return;
+               }
+               devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: rescan after "
+                   "reset\n", ioc->name, __func__));
+               ioc->in_rescan = 1;
+               mptsas_not_responding_devices(ioc);
+               mptsas_scan_sas_topology(ioc);
+               ioc->in_rescan = 0;
+               mptsas_free_fw_event(ioc, fw_event);
+               return;
+       }
+
+       /* events handling turned off during host reset */
+       if (ioc->fw_events_off) {
+               mptsas_free_fw_event(ioc, fw_event);
+               return;
+       }
+
+       devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: fw_event=(0x%p), "
+           "event = (0x%02x)\n", ioc->name, __func__, fw_event,
+           (fw_event->event & 0xFF)));
+
+       switch (fw_event->event) {
+       case MPI_EVENT_SAS_DEVICE_STATUS_CHANGE:
+               mptsas_send_sas_event(fw_event);
+               break;
+       case MPI_EVENT_INTEGRATED_RAID:
+               mptsas_send_raid_event(fw_event);
+               break;
+       case MPI_EVENT_IR2:
+               mptsas_send_ir2_event(fw_event);
+               break;
+       case MPI_EVENT_PERSISTENT_TABLE_FULL:
+               mptbase_sas_persist_operation(ioc,
+                   MPI_SAS_OP_CLEAR_NOT_PRESENT);
+               mptsas_free_fw_event(ioc, fw_event);
+               break;
+       case MPI_EVENT_SAS_BROADCAST_PRIMITIVE:
+               mptsas_broadcast_primative_work(fw_event);
+               break;
+       case MPI_EVENT_SAS_EXPANDER_STATUS_CHANGE:
+               mptsas_send_expander_event(fw_event);
+               break;
+       case MPI_EVENT_SAS_PHY_LINK_STATUS:
+               mptsas_send_link_status_event(fw_event);
+               break;
+       case MPI_EVENT_QUEUE_FULL:
+               mptsas_handle_queue_full_event(fw_event);
+               break;
+       }
+}
+
+
+
 static int
 mptsas_slave_configure(struct scsi_device *sdev)
 {
+       struct Scsi_Host        *host = sdev->host;
+       MPT_SCSI_HOST   *hd = shost_priv(host);
+       MPT_ADAPTER     *ioc = hd->ioc;
+       VirtDevice      *vdevice = sdev->hostdata;
 
-       if (sdev->channel == MPTSAS_RAID_CHANNEL)
+       if (vdevice->vtarget->deleted) {
+               sdev_printk(KERN_INFO, sdev, "clearing deleted flag\n");
+               vdevice->vtarget->deleted = 0;
+       }
+
+       /*
+        * RAID volumes placed beyond the last expected port.
+        * Ignore sending sas mode pages in that case..
+        */
+       if (sdev->channel == MPTSAS_RAID_CHANNEL) {
+               mptsas_add_device_component_starget_ir(ioc, scsi_target(sdev));
                goto out;
+       }
 
        sas_read_port_mode_page(sdev);
 
+       mptsas_add_device_component_starget(ioc, scsi_target(sdev));
+
  out:
        return mptscsih_slave_configure(sdev);
 }
@@ -875,9 +1693,18 @@ mptsas_target_alloc(struct scsi_target *starget)
         * RAID volumes placed beyond the last expected port.
         */
        if (starget->channel == MPTSAS_RAID_CHANNEL) {
-               for (i=0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++)
-                       if (id == ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID)
-                               channel = ioc->raid_data.pIocPg2->RaidVolume[i].VolumeBus;
+               if (!ioc->raid_data.pIocPg2) {
+                       kfree(vtarget);
+                       return -ENXIO;
+               }
+               for (i = 0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++) {
+                       if (id == ioc->raid_data.pIocPg2->
+                                       RaidVolume[i].VolumeID) {
+                               channel = ioc->raid_data.pIocPg2->
+                                       RaidVolume[i].VolumeBus;
+                       }
+               }
+               vtarget->raidVolume = 1;
                goto out;
        }
 
@@ -926,11 +1753,18 @@ mptsas_target_destroy(struct scsi_target *starget)
        struct sas_rphy         *rphy;
        struct mptsas_portinfo  *p;
        int                      i;
-       MPT_ADAPTER *ioc = hd->ioc;
+       MPT_ADAPTER     *ioc = hd->ioc;
+       VirtTarget      *vtarget;
 
        if (!starget->hostdata)
                return;
 
+       vtarget = starget->hostdata;
+
+       mptsas_del_device_component_by_os(ioc, starget->channel,
+           starget->id);
+
+
        if (starget->channel == MPTSAS_RAID_CHANNEL)
                goto out;
 
@@ -940,12 +1774,21 @@ mptsas_target_destroy(struct scsi_target *starget)
                        if (p->phy_info[i].attached.sas_address !=
                                        rphy->identify.sas_address)
                                continue;
+
+                       starget_printk(KERN_INFO, starget, MYIOC_s_FMT
+                       "delete device: fw_channel %d, fw_id %d, phy %d, "
+                       "sas_addr 0x%llx\n", ioc->name,
+                       p->phy_info[i].attached.channel,
+                       p->phy_info[i].attached.id,
+                       p->phy_info[i].attached.phy_id, (unsigned long long)
+                       p->phy_info[i].attached.sas_address);
+
                        mptsas_set_starget(&p->phy_info[i], NULL);
-                       goto out;
                }
        }
 
  out:
+       vtarget->starget = NULL;
        kfree(starget->hostdata);
        starget->hostdata = NULL;
 }
@@ -1008,6 +1851,8 @@ mptsas_slave_alloc(struct scsi_device *sdev)
 static int
 mptsas_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
 {
+       MPT_SCSI_HOST   *hd;
+       MPT_ADAPTER     *ioc;
        VirtDevice      *vdevice = SCpnt->device->hostdata;
 
        if (!vdevice || !vdevice->vtarget || vdevice->vtarget->deleted) {
@@ -1016,6 +1861,12 @@ mptsas_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
                return 0;
        }
 
+       hd = shost_priv(SCpnt->device->host);
+       ioc = hd->ioc;
+
+       if (ioc->sas_discovery_quiesce_io)
+               return SCSI_MLQUEUE_HOST_BUSY;
+
 //     scsi_print_command(SCpnt);
 
        return mptscsih_qcmd(SCpnt,done);
@@ -1114,14 +1965,19 @@ static int mptsas_get_linkerrors(struct sas_phy *phy)
 static int mptsas_mgmt_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req,
                MPT_FRAME_HDR *reply)
 {
-       ioc->sas_mgmt.status |= MPT_SAS_MGMT_STATUS_COMMAND_GOOD;
+       ioc->sas_mgmt.status |= MPT_MGMT_STATUS_COMMAND_GOOD;
        if (reply != NULL) {
-               ioc->sas_mgmt.status |= MPT_SAS_MGMT_STATUS_RF_VALID;
+               ioc->sas_mgmt.status |= MPT_MGMT_STATUS_RF_VALID;
                memcpy(ioc->sas_mgmt.reply, reply,
                    min(ioc->reply_sz, 4 * reply->u.reply.MsgLength));
        }
-       complete(&ioc->sas_mgmt.done);
-       return 1;
+
+       if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_PENDING) {
+               ioc->sas_mgmt.status &= ~MPT_MGMT_STATUS_PENDING;
+               complete(&ioc->sas_mgmt.done);
+               return 1;
+       }
+       return 0;
 }
 
 static int mptsas_phy_reset(struct sas_phy *phy, int hard_reset)
@@ -1160,6 +2016,7 @@ static int mptsas_phy_reset(struct sas_phy *phy, int hard_reset)
                MPI_SAS_OP_PHY_HARD_RESET : MPI_SAS_OP_PHY_LINK_RESET;
        req->PhyNum = phy->identify.phy_identifier;
 
+       INITIALIZE_MGMT_STATUS(ioc->sas_mgmt.status)
        mpt_put_msg_frame(mptsasMgmtCtx, ioc, mf);
 
        timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done,
@@ -1174,7 +2031,7 @@ static int mptsas_phy_reset(struct sas_phy *phy, int hard_reset)
 
        /* a reply frame is expected */
        if ((ioc->sas_mgmt.status &
-           MPT_IOCTL_STATUS_RF_VALID) == 0) {
+           MPT_MGMT_STATUS_RF_VALID) == 0) {
                error = -ENXIO;
                goto out_unlock;
        }
@@ -1191,6 +2048,7 @@ static int mptsas_phy_reset(struct sas_phy *phy, int hard_reset)
        error = 0;
 
  out_unlock:
+       CLEAR_MGMT_STATUS(ioc->sas_mgmt.status)
        mutex_unlock(&ioc->sas_mgmt.mutex);
  out:
        return error;
@@ -1304,7 +2162,7 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
                struct mptsas_portinfo *port_info;
 
                mutex_lock(&ioc->sas_topology_mutex);
-               port_info = mptsas_get_hba_portinfo(ioc);
+               port_info = ioc->hba_port_info;
                if (port_info && port_info->phy_info)
                        sas_address =
                                port_info->phy_info[0].phy->identify.sas_address;
@@ -1319,26 +2177,32 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
        /* request */
        flagsLength = (MPI_SGE_FLAGS_SIMPLE_ELEMENT |
                       MPI_SGE_FLAGS_END_OF_BUFFER |
-                      MPI_SGE_FLAGS_DIRECTION |
-                      mpt_addr_size()) << MPI_SGE_FLAGS_SHIFT;
+                      MPI_SGE_FLAGS_DIRECTION)
+                      << MPI_SGE_FLAGS_SHIFT;
        flagsLength |= (blk_rq_bytes(req) - 4);
 
        dma_addr_out = pci_map_single(ioc->pcidev, bio_data(req->bio),
                                      blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL);
        if (!dma_addr_out)
                goto put_mf;
-       mpt_add_sge(psge, flagsLength, dma_addr_out);
-       psge += (sizeof(u32) + sizeof(dma_addr_t));
+       ioc->add_sge(psge, flagsLength, dma_addr_out);
+       psge += ioc->SGE_size;
 
        /* response */
-       flagsLength = MPT_SGE_FLAGS_SSIMPLE_READ;
+       flagsLength = MPI_SGE_FLAGS_SIMPLE_ELEMENT |
+               MPI_SGE_FLAGS_SYSTEM_ADDRESS |
+               MPI_SGE_FLAGS_IOC_TO_HOST |
+               MPI_SGE_FLAGS_END_OF_BUFFER;
+
+       flagsLength = flagsLength << MPI_SGE_FLAGS_SHIFT;
        flagsLength |= blk_rq_bytes(rsp) + 4;
        dma_addr_in =  pci_map_single(ioc->pcidev, bio_data(rsp->bio),
                                      blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL);
        if (!dma_addr_in)
                goto unmap;
-       mpt_add_sge(psge, flagsLength, dma_addr_in);
+       ioc->add_sge(psge, flagsLength, dma_addr_in);
 
+       INITIALIZE_MGMT_STATUS(ioc->sas_mgmt.status)
        mpt_put_msg_frame(mptsasMgmtCtx, ioc, mf);
 
        timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done, 10 * HZ);
@@ -1351,7 +2215,7 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
        }
        mf = NULL;
 
-       if (ioc->sas_mgmt.status & MPT_IOCTL_STATUS_RF_VALID) {
+       if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_RF_VALID) {
                SmpPassthroughReply_t *smprep;
 
                smprep = (SmpPassthroughReply_t *)ioc->sas_mgmt.reply;
@@ -1360,7 +2224,8 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
                req->resid_len = 0;
                rsp->resid_len -= smprep->ResponseDataLength;
        } else {
-               printk(MYIOC_s_ERR_FMT "%s: smp passthru reply failed to be returned\n",
+               printk(MYIOC_s_ERR_FMT
+                   "%s: smp passthru reply failed to be returned\n",
                    ioc->name, __func__);
                ret = -ENXIO;
        }
@@ -1375,6 +2240,7 @@ put_mf:
        if (mf)
                mpt_free_msg_frame(ioc, mf);
 out_unlock:
+       CLEAR_MGMT_STATUS(ioc->sas_mgmt.status)
        mutex_unlock(&ioc->sas_mgmt.mutex);
 out:
        return ret;
@@ -1438,7 +2304,7 @@ mptsas_sas_io_unit_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info)
 
        port_info->num_phys = buffer->NumPhys;
        port_info->phy_info = kcalloc(port_info->num_phys,
-               sizeof(*port_info->phy_info),GFP_KERNEL);
+               sizeof(struct mptsas_phyinfo), GFP_KERNEL);
        if (!port_info->phy_info) {
                error = -ENOMEM;
                goto out_free_consistent;
@@ -1600,10 +2466,6 @@ mptsas_sas_device_pg0(MPT_ADAPTER *ioc, struct mptsas_devinfo *device_info,
        __le64 sas_address;
        int error=0;
 
-       if (ioc->sas_discovery_runtime &&
-               mptsas_is_end_device(device_info))
-                       goto out;
-
        hdr.PageVersion = MPI_SASDEVICE0_PAGEVERSION;
        hdr.ExtPageLength = 0;
        hdr.PageNumber = 0;
@@ -1644,6 +2506,7 @@ mptsas_sas_device_pg0(MPT_ADAPTER *ioc, struct mptsas_devinfo *device_info,
 
        mptsas_print_device_pg0(ioc, buffer);
 
+       memset(device_info, 0, sizeof(struct mptsas_devinfo));
        device_info->handle = le16_to_cpu(buffer->DevHandle);
        device_info->handle_parent = le16_to_cpu(buffer->ParentDevHandle);
        device_info->handle_enclosure =
@@ -1675,7 +2538,9 @@ mptsas_sas_expander_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info,
        SasExpanderPage0_t *buffer;
        dma_addr_t dma_handle;
        int i, error;
+       __le64 sas_address;
 
+       memset(port_info, 0, sizeof(struct mptsas_portinfo));
        hdr.PageVersion = MPI_SASEXPANDER0_PAGEVERSION;
        hdr.ExtPageLength = 0;
        hdr.PageNumber = 0;
@@ -1721,18 +2586,23 @@ mptsas_sas_expander_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info,
        }
 
        /* save config data */
-       port_info->num_phys = buffer->NumPhys;
+       port_info->num_phys = (buffer->NumPhys) ? buffer->NumPhys : 1;
        port_info->phy_info = kcalloc(port_info->num_phys,
-               sizeof(*port_info->phy_info),GFP_KERNEL);
+               sizeof(struct mptsas_phyinfo), GFP_KERNEL);
        if (!port_info->phy_info) {
                error = -ENOMEM;
                goto out_free_consistent;
        }
 
+       memcpy(&sas_address, &buffer->SASAddress, sizeof(__le64));
        for (i = 0; i < port_info->num_phys; i++) {
                port_info->phy_info[i].portinfo = port_info;
                port_info->phy_info[i].handle =
                    le16_to_cpu(buffer->DevHandle);
+               port_info->phy_info[i].identify.sas_address =
+                   le64_to_cpu(sas_address);
+               port_info->phy_info[i].identify.handle_parent =
+                   le16_to_cpu(buffer->ParentDevHandle);
        }
 
  out_free_consistent:
@@ -1752,11 +2622,7 @@ mptsas_sas_expander_pg1(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info,
        dma_addr_t dma_handle;
        int error=0;
 
-       if (ioc->sas_discovery_runtime &&
-               mptsas_is_end_device(&phy_info->attached))
-                       goto out;
-
-       hdr.PageVersion = MPI_SASEXPANDER0_PAGEVERSION;
+       hdr.PageVersion = MPI_SASEXPANDER1_PAGEVERSION;
        hdr.ExtPageLength = 0;
        hdr.PageNumber = 1;
        hdr.Reserved1 = 0;
@@ -1791,6 +2657,12 @@ mptsas_sas_expander_pg1(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info,
        cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
 
        error = mpt_config(ioc, &cfg);
+
+       if (error == MPI_IOCSTATUS_CONFIG_INVALID_PAGE) {
+               error = -ENODEV;
+               goto out;
+       }
+
        if (error)
                goto out_free_consistent;
 
@@ -2010,16 +2882,21 @@ static int mptsas_probe_one_phy(struct device *dev,
                                goto out;
                        }
                        mptsas_set_port(ioc, phy_info, port);
-                       dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT
-                           "sas_port_alloc: port=%p dev=%p port_id=%d\n",
-                           ioc->name, port, dev, port->port_identifier));
+                       devtprintk(ioc, dev_printk(KERN_DEBUG, &port->dev,
+                           MYIOC_s_FMT "add port %d, sas_addr (0x%llx)\n",
+                           ioc->name, port->port_identifier,
+                           (unsigned long long)phy_info->
+                           attached.sas_address));
                }
-               dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "sas_port_add_phy: phy_id=%d\n",
-                   ioc->name, phy_info->phy_id));
+               dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                       "sas_port_add_phy: phy_id=%d\n",
+                       ioc->name, phy_info->phy_id));
                sas_port_add_phy(port, phy_info->phy);
                phy_info->sas_port_add_phy = 0;
+               devtprintk(ioc, dev_printk(KERN_DEBUG, &phy_info->phy->dev,
+                   MYIOC_s_FMT "add phy %d, phy-obj (0x%p)\n", ioc->name,
+                    phy_info->phy_id, phy_info->phy));
        }
-
        if (!mptsas_get_rphy(phy_info) && port && !port->rphy) {
 
                struct sas_rphy *rphy;
@@ -2032,18 +2909,17 @@ static int mptsas_probe_one_phy(struct device *dev,
                 * the adding/removing of devices that occur
                 * after start of day.
                 */
-               if (ioc->sas_discovery_runtime &&
-                       mptsas_is_end_device(&phy_info->attached))
-                               goto out;
+               if (mptsas_is_end_device(&phy_info->attached) &&
+                   phy_info->attached.handle_parent) {
+                       goto out;
+               }
 
                mptsas_parse_device_info(&identify, &phy_info->attached);
                if (scsi_is_host_device(parent)) {
                        struct mptsas_portinfo *port_info;
                        int i;
 
-                       mutex_lock(&ioc->sas_topology_mutex);
-                       port_info = mptsas_get_hba_portinfo(ioc);
-                       mutex_unlock(&ioc->sas_topology_mutex);
+                       port_info = ioc->hba_port_info;
 
                        for (i = 0; i < port_info->num_phys; i++)
                                if (port_info->phy_info[i].identify.sas_address ==
@@ -2102,7 +2978,7 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc)
        struct mptsas_portinfo *port_info, *hba;
        int error = -ENOMEM, i;
 
-       hba = kzalloc(sizeof(*port_info), GFP_KERNEL);
+       hba = kzalloc(sizeof(struct mptsas_portinfo), GFP_KERNEL);
        if (! hba)
                goto out;
 
@@ -2112,9 +2988,10 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc)
 
        mptsas_sas_io_unit_pg1(ioc);
        mutex_lock(&ioc->sas_topology_mutex);
-       port_info = mptsas_get_hba_portinfo(ioc);
+       port_info = ioc->hba_port_info;
        if (!port_info) {
-               port_info = hba;
+               ioc->hba_port_info = port_info = hba;
+               ioc->hba_port_num_phy = port_info->num_phys;
                list_add_tail(&port_info->list, &ioc->sas_topology);
        } else {
                for (i = 0; i < hba->num_phys; i++) {
@@ -2130,15 +3007,22 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc)
                hba = NULL;
        }
        mutex_unlock(&ioc->sas_topology_mutex);
+#if defined(CPQ_CIM)
+       ioc->num_ports = port_info->num_phys;
+#endif
        for (i = 0; i < port_info->num_phys; i++) {
                mptsas_sas_phy_pg0(ioc, &port_info->phy_info[i],
                        (MPI_SAS_PHY_PGAD_FORM_PHY_NUMBER <<
                         MPI_SAS_PHY_PGAD_FORM_SHIFT), i);
-
+               port_info->phy_info[i].identify.handle =
+                   port_info->phy_info[i].handle;
                mptsas_sas_device_pg0(ioc, &port_info->phy_info[i].identify,
                        (MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
                         MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
-                        port_info->phy_info[i].handle);
+                        port_info->phy_info[i].identify.handle);
+               if (!ioc->hba_port_sas_addr)
+                       ioc->hba_port_sas_addr =
+                           port_info->phy_info[i].identify.sas_address;
                port_info->phy_info[i].identify.phy_id =
                    port_info->phy_info[i].phy_id = i;
                if (port_info->phy_info[i].attached.handle)
@@ -2163,248 +3047,721 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc)
        return error;
 }
 
-static int
-mptsas_probe_expander_phys(MPT_ADAPTER *ioc, u32 *handle)
+static void
+mptsas_expander_refresh(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info)
 {
-       struct mptsas_portinfo *port_info, *p, *ex;
-       struct device *parent;
-       struct sas_rphy *rphy;
-       int error = -ENOMEM, i, j;
-
-       ex = kzalloc(sizeof(*port_info), GFP_KERNEL);
-       if (!ex)
-               goto out;
-
-       error = mptsas_sas_expander_pg0(ioc, ex,
-           (MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE <<
-            MPI_SAS_EXPAND_PGAD_FORM_SHIFT), *handle);
-       if (error)
-               goto out_free_port_info;
-
-       *handle = ex->phy_info[0].handle;
-
-       mutex_lock(&ioc->sas_topology_mutex);
-       port_info = mptsas_find_portinfo_by_handle(ioc, *handle);
-       if (!port_info) {
-               port_info = ex;
-               list_add_tail(&port_info->list, &ioc->sas_topology);
-       } else {
-               for (i = 0; i < ex->num_phys; i++) {
-                       port_info->phy_info[i].handle =
-                               ex->phy_info[i].handle;
-                       port_info->phy_info[i].port_id =
-                               ex->phy_info[i].port_id;
-               }
-               kfree(ex->phy_info);
-               kfree(ex);
-               ex = NULL;
-       }
-       mutex_unlock(&ioc->sas_topology_mutex);
-
+       struct mptsas_portinfo *parent;
+       struct device *parent_dev;
+       struct sas_rphy *rphy;
+       int             i;
+       u64             sas_address; /* expander sas address */
+       u32             handle;
+
+       handle = port_info->phy_info[0].handle;
+       sas_address = port_info->phy_info[0].identify.sas_address;
        for (i = 0; i < port_info->num_phys; i++) {
                mptsas_sas_expander_pg1(ioc, &port_info->phy_info[i],
-                       (MPI_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM <<
-                        MPI_SAS_EXPAND_PGAD_FORM_SHIFT), (i << 16) + *handle);
-
-               if (port_info->phy_info[i].identify.handle) {
-                       mptsas_sas_device_pg0(ioc,
-                               &port_info->phy_info[i].identify,
-                               (MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
-                                MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
-                               port_info->phy_info[i].identify.handle);
-                       port_info->phy_info[i].identify.phy_id =
-                           port_info->phy_info[i].phy_id;
-               }
+                   (MPI_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM <<
+                   MPI_SAS_EXPAND_PGAD_FORM_SHIFT), (i << 16) + handle);
+
+               mptsas_sas_device_pg0(ioc,
+                   &port_info->phy_info[i].identify,
+                   (MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
+                   MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
+                   port_info->phy_info[i].identify.handle);
+               port_info->phy_info[i].identify.phy_id =
+                   port_info->phy_info[i].phy_id;
 
                if (port_info->phy_info[i].attached.handle) {
                        mptsas_sas_device_pg0(ioc,
-                               &port_info->phy_info[i].attached,
-                               (MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
-                                MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
-                               port_info->phy_info[i].attached.handle);
+                           &port_info->phy_info[i].attached,
+                           (MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
+                            MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
+                           port_info->phy_info[i].attached.handle);
                        port_info->phy_info[i].attached.phy_id =
                            port_info->phy_info[i].phy_id;
                }
        }
 
-       parent = &ioc->sh->shost_gendev;
-       for (i = 0; i < port_info->num_phys; i++) {
-               mutex_lock(&ioc->sas_topology_mutex);
-               list_for_each_entry(p, &ioc->sas_topology, list) {
-                       for (j = 0; j < p->num_phys; j++) {
-                               if (port_info->phy_info[i].identify.handle !=
-                                               p->phy_info[j].attached.handle)
-                                       continue;
-                               rphy = mptsas_get_rphy(&p->phy_info[j]);
-                               parent = &rphy->dev;
-                       }
-               }
+       mutex_lock(&ioc->sas_topology_mutex);
+       parent = mptsas_find_portinfo_by_handle(ioc,
+           port_info->phy_info[0].identify.handle_parent);
+       if (!parent) {
                mutex_unlock(&ioc->sas_topology_mutex);
+               return;
+       }
+       for (i = 0, parent_dev = NULL; i < parent->num_phys && !parent_dev;
+           i++) {
+               if (parent->phy_info[i].attached.sas_address == sas_address) {
+                       rphy = mptsas_get_rphy(&parent->phy_info[i]);
+                       parent_dev = &rphy->dev;
+               }
        }
+       mutex_unlock(&ioc->sas_topology_mutex);
 
        mptsas_setup_wide_ports(ioc, port_info);
-
        for (i = 0; i < port_info->num_phys; i++, ioc->sas_index++)
-               mptsas_probe_one_phy(parent, &port_info->phy_info[i],
+               mptsas_probe_one_phy(parent_dev, &port_info->phy_info[i],
                    ioc->sas_index, 0);
-
-       return 0;
-
- out_free_port_info:
-       if (ex) {
-               kfree(ex->phy_info);
-               kfree(ex);
-       }
- out:
-       return error;
 }
 
-/*
- * mptsas_delete_expander_phys
- *
- *
- * This will traverse topology, and remove expanders
- * that are no longer present
- */
 static void
-mptsas_delete_expander_phys(MPT_ADAPTER *ioc)
+mptsas_expander_event_add(MPT_ADAPTER *ioc,
+    MpiEventDataSasExpanderStatusChange_t *expander_data)
 {
-       struct mptsas_portinfo buffer;
-       struct mptsas_portinfo *port_info, *n, *parent;
-       struct mptsas_phyinfo *phy_info;
-       struct sas_port * port;
+       struct mptsas_portinfo *port_info;
        int i;
-       u64     expander_sas_address;
+       __le64 sas_address;
+
+       port_info = kzalloc(sizeof(struct mptsas_portinfo), GFP_KERNEL);
+       if (!port_info)
+               BUG();
+       port_info->num_phys = (expander_data->NumPhys) ?
+           expander_data->NumPhys : 1;
+       port_info->phy_info = kcalloc(port_info->num_phys,
+           sizeof(struct mptsas_phyinfo), GFP_KERNEL);
+       if (!port_info->phy_info)
+               BUG();
+       memcpy(&sas_address, &expander_data->SASAddress, sizeof(__le64));
+       for (i = 0; i < port_info->num_phys; i++) {
+               port_info->phy_info[i].portinfo = port_info;
+               port_info->phy_info[i].handle =
+                   le16_to_cpu(expander_data->DevHandle);
+               port_info->phy_info[i].identify.sas_address =
+                   le64_to_cpu(sas_address);
+               port_info->phy_info[i].identify.handle_parent =
+                   le16_to_cpu(expander_data->ParentDevHandle);
+       }
 
        mutex_lock(&ioc->sas_topology_mutex);
-       list_for_each_entry_safe(port_info, n, &ioc->sas_topology, list) {
+       list_add_tail(&port_info->list, &ioc->sas_topology);
+       mutex_unlock(&ioc->sas_topology_mutex);
 
-               if (!(port_info->phy_info[0].identify.device_info &
-                   MPI_SAS_DEVICE_INFO_SMP_TARGET))
-                       continue;
+       printk(MYIOC_s_INFO_FMT "add expander: num_phys %d, "
+           "sas_addr (0x%llx)\n", ioc->name, port_info->num_phys,
+           (unsigned long long)sas_address);
 
-               if (mptsas_sas_expander_pg0(ioc, &buffer,
-                    (MPI_SAS_EXPAND_PGAD_FORM_HANDLE <<
-                    MPI_SAS_EXPAND_PGAD_FORM_SHIFT),
-                    port_info->phy_info[0].handle)) {
+       mptsas_expander_refresh(ioc, port_info);
+}
 
-                       /*
-                        * Obtain the port_info instance to the parent port
-                        */
-                       parent = mptsas_find_portinfo_by_handle(ioc,
-                           port_info->phy_info[0].identify.handle_parent);
-
-                       if (!parent)
-                               goto next_port;
+/**
+ * mptsas_delete_expander_siblings - remove siblings attached to expander
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @parent: the parent port_info object
+ * @expander: the expander port_info object
+ **/
+static void
+mptsas_delete_expander_siblings(MPT_ADAPTER *ioc, struct mptsas_portinfo
+    *parent, struct mptsas_portinfo *expander)
+{
+       struct mptsas_phyinfo *phy_info;
+       struct mptsas_portinfo *port_info;
+       struct sas_rphy *rphy;
+       int i;
 
-                       expander_sas_address =
-                               port_info->phy_info[0].identify.sas_address;
+       phy_info = expander->phy_info;
+       for (i = 0; i < expander->num_phys; i++, phy_info++) {
+               rphy = mptsas_get_rphy(phy_info);
+               if (!rphy)
+                       continue;
+               if (rphy->identify.device_type == SAS_END_DEVICE)
+                       mptsas_del_end_device(ioc, phy_info);
+       }
 
+       phy_info = expander->phy_info;
+       for (i = 0; i < expander->num_phys; i++, phy_info++) {
+               rphy = mptsas_get_rphy(phy_info);
+               if (!rphy)
+                       continue;
+               if (rphy->identify.device_type ==
+                   MPI_SAS_DEVICE_INFO_EDGE_EXPANDER ||
+                   rphy->identify.device_type ==
+                   MPI_SAS_DEVICE_INFO_FANOUT_EXPANDER) {
+                       port_info = mptsas_find_portinfo_by_sas_address(ioc,
+                           rphy->identify.sas_address);
+                       if (!port_info)
+                               continue;
+                       if (port_info == parent) /* backlink rphy */
+                               continue;
                        /*
-                        * Delete rphys in the parent that point
-                        * to this expander.  The transport layer will
-                        * cleanup all the children.
-                        */
-                       phy_info = parent->phy_info;
-                       for (i = 0; i < parent->num_phys; i++, phy_info++) {
-                               port = mptsas_get_port(phy_info);
-                               if (!port)
-                                       continue;
-                               if (phy_info->attached.sas_address !=
-                                       expander_sas_address)
-                                       continue;
-                               dsaswideprintk(ioc,
-                                   dev_printk(KERN_DEBUG, &port->dev,
-                                   MYIOC_s_FMT "delete port (%d)\n", ioc->name,
-                                   port->port_identifier));
-                               sas_port_delete(port);
-                               mptsas_port_delete(ioc, phy_info->port_details);
-                       }
- next_port:
+                       Delete this expander even if the expdevpage is exists
+                       because the parent expander is already deleted
+                       */
+                       mptsas_expander_delete(ioc, port_info, 1);
+               }
+       }
+}
 
-                       phy_info = port_info->phy_info;
-                       for (i = 0; i < port_info->num_phys; i++, phy_info++)
-                               mptsas_port_delete(ioc, phy_info->port_details);
 
-                       list_del(&port_info->list);
-                       kfree(port_info->phy_info);
-                       kfree(port_info);
-               }
-               /*
-               * Free this memory allocated from inside
-               * mptsas_sas_expander_pg0
-               */
+/**
+ *     mptsas_expander_delete - remove this expander
+ *     @ioc: Pointer to MPT_ADAPTER structure
+ *     @port_info: expander port_info struct
+ *     @force: Flag to forcefully delete the expander
+ *
+ **/
+
+static void mptsas_expander_delete(MPT_ADAPTER *ioc,
+               struct mptsas_portinfo *port_info, u8 force)
+{
+
+       struct mptsas_portinfo *parent;
+       int             i;
+       u64             expander_sas_address;
+       struct mptsas_phyinfo *phy_info;
+       struct mptsas_portinfo buffer;
+       struct mptsas_portinfo_details *port_details;
+       struct sas_port *port;
+
+       if (!port_info)
+               return;
+
+       /* see if expander is still there before deleting */
+       mptsas_sas_expander_pg0(ioc, &buffer,
+           (MPI_SAS_EXPAND_PGAD_FORM_HANDLE <<
+           MPI_SAS_EXPAND_PGAD_FORM_SHIFT),
+           port_info->phy_info[0].identify.handle);
+
+       if (buffer.num_phys) {
                kfree(buffer.phy_info);
+               if (!force)
+                       return;
        }
-       mutex_unlock(&ioc->sas_topology_mutex);
+
+
+       /*
+        * Obtain the port_info instance to the parent port
+        */
+       port_details = NULL;
+       expander_sas_address =
+           port_info->phy_info[0].identify.sas_address;
+       parent = mptsas_find_portinfo_by_handle(ioc,
+           port_info->phy_info[0].identify.handle_parent);
+       mptsas_delete_expander_siblings(ioc, parent, port_info);
+       if (!parent)
+               goto out;
+
+       /*
+        * Delete rphys in the parent that point
+        * to this expander.
+        */
+       phy_info = parent->phy_info;
+       port = NULL;
+       for (i = 0; i < parent->num_phys; i++, phy_info++) {
+               if (!phy_info->phy)
+                       continue;
+               if (phy_info->attached.sas_address !=
+                   expander_sas_address)
+                       continue;
+               if (!port) {
+                       port = mptsas_get_port(phy_info);
+                       port_details = phy_info->port_details;
+               }
+               dev_printk(KERN_DEBUG, &phy_info->phy->dev,
+                   MYIOC_s_FMT "delete phy %d, phy-obj (0x%p)\n", ioc->name,
+                   phy_info->phy_id, phy_info->phy);
+               sas_port_delete_phy(port, phy_info->phy);
+       }
+       if (port) {
+               dev_printk(KERN_DEBUG, &port->dev,
+                   MYIOC_s_FMT "delete port %d, sas_addr (0x%llx)\n",
+                   ioc->name, port->port_identifier,
+                   (unsigned long long)expander_sas_address);
+               sas_port_delete(port);
+               mptsas_port_delete(ioc, port_details);
+       }
+ out:
+
+       printk(MYIOC_s_INFO_FMT "delete expander: num_phys %d, "
+           "sas_addr (0x%llx)\n",  ioc->name, port_info->num_phys,
+           (unsigned long long)expander_sas_address);
+
+       /*
+        * free link
+        */
+       list_del(&port_info->list);
+       kfree(port_info->phy_info);
+       kfree(port_info);
 }
 
-/*
- * Start of day discovery
+
+/**
+ * mptsas_send_expander_event - expanders events
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @expander_data: event data
+ *
+ *
+ * This function handles adding, removing, and refreshing
+ * device handles within the expander objects.
  */
 static void
+mptsas_send_expander_event(struct fw_event_work *fw_event)
+{
+       MPT_ADAPTER *ioc;
+       MpiEventDataSasExpanderStatusChange_t *expander_data;
+       struct mptsas_portinfo *port_info;
+       __le64 sas_address;
+       int i;
+
+       ioc = fw_event->ioc;
+       expander_data = (MpiEventDataSasExpanderStatusChange_t *)
+           fw_event->event_data;
+       memcpy(&sas_address, &expander_data->SASAddress, sizeof(__le64));
+       port_info = mptsas_find_portinfo_by_sas_address(ioc, sas_address);
+
+       if (expander_data->ReasonCode == MPI_EVENT_SAS_EXP_RC_ADDED) {
+               if (port_info) {
+                       for (i = 0; i < port_info->num_phys; i++) {
+                               port_info->phy_info[i].portinfo = port_info;
+                               port_info->phy_info[i].handle =
+                                   le16_to_cpu(expander_data->DevHandle);
+                               port_info->phy_info[i].identify.sas_address =
+                                   le64_to_cpu(sas_address);
+                               port_info->phy_info[i].identify.handle_parent =
+                                   le16_to_cpu(expander_data->ParentDevHandle);
+                       }
+                       mptsas_expander_refresh(ioc, port_info);
+               } else if (!port_info && expander_data->NumPhys)
+                       mptsas_expander_event_add(ioc, expander_data);
+       } else if (expander_data->ReasonCode ==
+           MPI_EVENT_SAS_EXP_RC_NOT_RESPONDING)
+               mptsas_expander_delete(ioc, port_info, 0);
+
+       mptsas_free_fw_event(ioc, fw_event);
+}
+
+
+/**
+ * mptsas_expander_add -
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @handle:
+ *
+ */
+struct mptsas_portinfo *
+mptsas_expander_add(MPT_ADAPTER *ioc, u16 handle)
+{
+       struct mptsas_portinfo buffer, *port_info;
+       int i;
+
+       if ((mptsas_sas_expander_pg0(ioc, &buffer,
+           (MPI_SAS_EXPAND_PGAD_FORM_HANDLE <<
+           MPI_SAS_EXPAND_PGAD_FORM_SHIFT), handle)))
+               return NULL;
+
+       port_info = kzalloc(sizeof(struct mptsas_portinfo), GFP_ATOMIC);
+       if (!port_info) {
+               dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+               "%s: exit at line=%d\n", ioc->name,
+               __func__, __LINE__));
+               return NULL;
+       }
+       port_info->num_phys = buffer.num_phys;
+       port_info->phy_info = buffer.phy_info;
+       for (i = 0; i < port_info->num_phys; i++)
+               port_info->phy_info[i].portinfo = port_info;
+       mutex_lock(&ioc->sas_topology_mutex);
+       list_add_tail(&port_info->list, &ioc->sas_topology);
+       mutex_unlock(&ioc->sas_topology_mutex);
+       printk(MYIOC_s_INFO_FMT "add expander: num_phys %d, "
+           "sas_addr (0x%llx)\n", ioc->name, port_info->num_phys,
+           (unsigned long long)buffer.phy_info[0].identify.sas_address);
+       mptsas_expander_refresh(ioc, port_info);
+       return port_info;
+}
+
+static void
+mptsas_send_link_status_event(struct fw_event_work *fw_event)
+{
+       MPT_ADAPTER *ioc;
+       MpiEventDataSasPhyLinkStatus_t *link_data;
+       struct mptsas_portinfo *port_info;
+       struct mptsas_phyinfo *phy_info = NULL;
+       __le64 sas_address;
+       u8 phy_num;
+       u8 link_rate;
+
+       ioc = fw_event->ioc;
+       link_data = (MpiEventDataSasPhyLinkStatus_t *)fw_event->event_data;
+
+       memcpy(&sas_address, &link_data->SASAddress, sizeof(__le64));
+       sas_address = le64_to_cpu(sas_address);
+       link_rate = link_data->LinkRates >> 4;
+       phy_num = link_data->PhyNum;
+
+       port_info = mptsas_find_portinfo_by_sas_address(ioc, sas_address);
+       if (port_info) {
+               phy_info = &port_info->phy_info[phy_num];
+               if (phy_info)
+                       phy_info->negotiated_link_rate = link_rate;
+       }
+
+       if (link_rate == MPI_SAS_IOUNIT0_RATE_1_5 ||
+           link_rate == MPI_SAS_IOUNIT0_RATE_3_0) {
+
+               if (!port_info) {
+                       if (ioc->old_sas_discovery_protocal) {
+                               port_info = mptsas_expander_add(ioc,
+                                       le16_to_cpu(link_data->DevHandle));
+                               if (port_info)
+                                       goto out;
+                       }
+                       goto out;
+               }
+
+               if (port_info == ioc->hba_port_info)
+                       mptsas_probe_hba_phys(ioc);
+               else
+                       mptsas_expander_refresh(ioc, port_info);
+       } else if (phy_info && phy_info->phy) {
+               if (link_rate ==  MPI_SAS_IOUNIT0_RATE_PHY_DISABLED)
+                       phy_info->phy->negotiated_linkrate =
+                           SAS_PHY_DISABLED;
+               else if (link_rate ==
+                   MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION)
+                       phy_info->phy->negotiated_linkrate =
+                           SAS_LINK_RATE_FAILED;
+               else
+                       phy_info->phy->negotiated_linkrate =
+                           SAS_LINK_RATE_UNKNOWN;
+       }
+ out:
+       mptsas_free_fw_event(ioc, fw_event);
+}
+
+static void
+mptsas_not_responding_devices(MPT_ADAPTER *ioc)
+{
+       struct mptsas_portinfo buffer, *port_info;
+       struct mptsas_device_info       *sas_info;
+       struct mptsas_devinfo sas_device;
+       u32     handle;
+       VirtTarget *vtarget = NULL;
+       struct mptsas_phyinfo *phy_info;
+       u8 found_expander;
+       int retval, retry_count;
+       unsigned long flags;
+
+       mpt_findImVolumes(ioc);
+
+       spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+       if (ioc->ioc_reset_in_progress) {
+               dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                  "%s: exiting due to a parallel reset \n", ioc->name,
+                   __func__));
+               spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+               return;
+       }
+       spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+
+       /* devices, logical volumes */
+       mutex_lock(&ioc->sas_device_info_mutex);
+ redo_device_scan:
+       list_for_each_entry(sas_info, &ioc->sas_device_info_list, list) {
+               if (sas_info->is_cached)
+                       continue;
+               if (!sas_info->is_logical_volume) {
+                       sas_device.handle = 0;
+                       retry_count = 0;
+retry_page:
+                       retval = mptsas_sas_device_pg0(ioc, &sas_device,
+                               (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID
+                               << MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
+                               (sas_info->fw.channel << 8) +
+                               sas_info->fw.id);
+
+                       if (sas_device.handle)
+                               continue;
+                       if (retval == -EBUSY) {
+                               spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+                               if (ioc->ioc_reset_in_progress) {
+                                       dfailprintk(ioc,
+                                       printk(MYIOC_s_DEBUG_FMT
+                                       "%s: exiting due to reset\n",
+                                       ioc->name, __func__));
+                                       spin_unlock_irqrestore
+                                       (&ioc->taskmgmt_lock, flags);
+                                       mutex_unlock(&ioc->
+                                       sas_device_info_mutex);
+                                       return;
+                               }
+                               spin_unlock_irqrestore(&ioc->taskmgmt_lock,
+                               flags);
+                       }
+
+                       if (retval && (retval != -ENODEV)) {
+                               if (retry_count < 10) {
+                                       retry_count++;
+                                       goto retry_page;
+                               } else {
+                                       devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                                       "%s: Config page retry exceeded retry "
+                                       "count deleting device 0x%llx\n",
+                                       ioc->name, __func__,
+                                       sas_info->sas_address));
+                               }
+                       }
+
+                       /* delete device */
+                       vtarget = mptsas_find_vtarget(ioc,
+                               sas_info->fw.channel, sas_info->fw.id);
+
+                       if (vtarget)
+                               vtarget->deleted = 1;
+
+                       phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
+                                       sas_info->sas_address);
+
+                       if (phy_info) {
+                               mptsas_del_end_device(ioc, phy_info);
+                               goto redo_device_scan;
+                       }
+               } else
+                       mptsas_volume_delete(ioc, sas_info->fw.id);
+       }
+       mutex_lock(&ioc->sas_device_info_mutex);
+
+       /* expanders */
+       mutex_lock(&ioc->sas_topology_mutex);
+ redo_expander_scan:
+       list_for_each_entry(port_info, &ioc->sas_topology, list) {
+
+               if (port_info->phy_info &&
+                   (!(port_info->phy_info[0].identify.device_info &
+                   MPI_SAS_DEVICE_INFO_SMP_TARGET)))
+                       continue;
+               found_expander = 0;
+               handle = 0xFFFF;
+               while (!mptsas_sas_expander_pg0(ioc, &buffer,
+                   (MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE <<
+                    MPI_SAS_EXPAND_PGAD_FORM_SHIFT), handle) &&
+                   !found_expander) {
+
+                       handle = buffer.phy_info[0].handle;
+                       if (buffer.phy_info[0].identify.sas_address ==
+                           port_info->phy_info[0].identify.sas_address) {
+                               found_expander = 1;
+                       }
+                       kfree(buffer.phy_info);
+               }
+
+               if (!found_expander) {
+                       mptsas_expander_delete(ioc, port_info, 0);
+                       goto redo_expander_scan;
+               }
+       }
+       mutex_lock(&ioc->sas_topology_mutex);
+}
+
+/**
+ *     mptsas_probe_expanders - adding expanders
+ *     @ioc: Pointer to MPT_ADAPTER structure
+ *
+ **/
+static void
+mptsas_probe_expanders(MPT_ADAPTER *ioc)
+{
+       struct mptsas_portinfo buffer, *port_info;
+       u32                     handle;
+       int i;
+
+       handle = 0xFFFF;
+       while (!mptsas_sas_expander_pg0(ioc, &buffer,
+           (MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE <<
+            MPI_SAS_EXPAND_PGAD_FORM_SHIFT), handle)) {
+
+               handle = buffer.phy_info[0].handle;
+               port_info = mptsas_find_portinfo_by_sas_address(ioc,
+                   buffer.phy_info[0].identify.sas_address);
+
+               if (port_info) {
+                       /* refreshing handles */
+                       for (i = 0; i < buffer.num_phys; i++) {
+                               port_info->phy_info[i].handle = handle;
+                               port_info->phy_info[i].identify.handle_parent =
+                                   buffer.phy_info[0].identify.handle_parent;
+                       }
+                       mptsas_expander_refresh(ioc, port_info);
+                       kfree(buffer.phy_info);
+                       continue;
+               }
+
+               port_info = kzalloc(sizeof(struct mptsas_portinfo), GFP_KERNEL);
+               if (!port_info) {
+                       dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+                       "%s: exit at line=%d\n", ioc->name,
+                       __func__, __LINE__));
+                       return;
+               }
+               port_info->num_phys = buffer.num_phys;
+               port_info->phy_info = buffer.phy_info;
+               for (i = 0; i < port_info->num_phys; i++)
+                       port_info->phy_info[i].portinfo = port_info;
+               mutex_lock(&ioc->sas_topology_mutex);
+               list_add_tail(&port_info->list, &ioc->sas_topology);
+               mutex_unlock(&ioc->sas_topology_mutex);
+               printk(MYIOC_s_INFO_FMT "add expander: num_phys %d, "
+                   "sas_addr (0x%llx)\n", ioc->name, port_info->num_phys,
+           (unsigned long long)buffer.phy_info[0].identify.sas_address);
+               mptsas_expander_refresh(ioc, port_info);
+       }
+}
+
+static void
+mptsas_probe_devices(MPT_ADAPTER *ioc)
+{
+       u16 handle;
+       struct mptsas_devinfo sas_device;
+       struct mptsas_phyinfo *phy_info;
+
+       handle = 0xFFFF;
+       while (!(mptsas_sas_device_pg0(ioc, &sas_device,
+           MPI_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
+
+               handle = sas_device.handle;
+
+               if ((sas_device.device_info &
+                    (MPI_SAS_DEVICE_INFO_SSP_TARGET |
+                     MPI_SAS_DEVICE_INFO_STP_TARGET |
+                     MPI_SAS_DEVICE_INFO_SATA_DEVICE)) == 0)
+                       continue;
+
+               phy_info = mptsas_refreshing_device_handles(ioc, &sas_device);
+               if (!phy_info)
+                       continue;
+
+               if (mptsas_get_rphy(phy_info))
+                       continue;
+
+               mptsas_add_end_device(ioc, phy_info);
+       }
+}
+
+/**
+ *     mptsas_scan_sas_topology -
+ *     @ioc: Pointer to MPT_ADAPTER structure
+ *     @sas_address:
+ *
+ **/
+static void
 mptsas_scan_sas_topology(MPT_ADAPTER *ioc)
 {
-       u32 handle = 0xFFFF;
+       struct scsi_device *sdev;
        int i;
 
-       mutex_lock(&ioc->sas_discovery_mutex);
        mptsas_probe_hba_phys(ioc);
-       while (!mptsas_probe_expander_phys(ioc, &handle))
-               ;
+       mptsas_probe_expanders(ioc);
+       mptsas_probe_devices(ioc);
+
        /*
          Reporting RAID volumes.
        */
-       if (!ioc->ir_firmware)
-               goto out;
-       if (!ioc->raid_data.pIocPg2)
-               goto out;
-       if (!ioc->raid_data.pIocPg2->NumActiveVolumes)
-               goto out;
+       if (!ioc->ir_firmware || !ioc->raid_data.pIocPg2 ||
+           !ioc->raid_data.pIocPg2->NumActiveVolumes)
+               return;
        for (i = 0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++) {
+               sdev = scsi_device_lookup(ioc->sh, MPTSAS_RAID_CHANNEL,
+                   ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID, 0);
+               if (sdev) {
+                       scsi_device_put(sdev);
+                       continue;
+               }
+               printk(MYIOC_s_INFO_FMT "attaching raid volume, channel %d, "
+                   "id %d\n", ioc->name, MPTSAS_RAID_CHANNEL,
+                   ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID);
                scsi_add_device(ioc->sh, MPTSAS_RAID_CHANNEL,
                    ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID, 0);
        }
- out:
-       mutex_unlock(&ioc->sas_discovery_mutex);
 }
 
-/*
- * Work queue thread to handle Runtime discovery
- * Mere purpose is the hot add/delete of expanders
- *(Mutex UNLOCKED)
- */
+
 static void
-__mptsas_discovery_work(MPT_ADAPTER *ioc)
+mptsas_handle_queue_full_event(struct fw_event_work *fw_event)
 {
-       u32 handle = 0xFFFF;
+       MPT_ADAPTER *ioc;
+       EventDataQueueFull_t *qfull_data;
+       struct mptsas_device_info *sas_info;
+       struct scsi_device      *sdev;
+       int depth;
+       int id = -1;
+       int channel = -1;
+       int fw_id, fw_channel;
+       u16 current_depth;
+
+
+       ioc = fw_event->ioc;
+       qfull_data = (EventDataQueueFull_t *)fw_event->event_data;
+       fw_id = qfull_data->TargetID;
+       fw_channel = qfull_data->Bus;
+       current_depth = le16_to_cpu(qfull_data->CurrentDepth);
+
+       /* if hidden raid component, look for the volume id */
+       mutex_lock(&ioc->sas_device_info_mutex);
+       if (mptscsih_is_phys_disk(ioc, fw_channel, fw_id)) {
+               list_for_each_entry(sas_info, &ioc->sas_device_info_list,
+                   list) {
+                       if (sas_info->is_cached ||
+                           sas_info->is_logical_volume)
+                               continue;
+                       if (sas_info->is_hidden_raid_component &&
+                           (sas_info->fw.channel == fw_channel &&
+                           sas_info->fw.id == fw_id)) {
+                               id = sas_info->volume_id;
+                               channel = MPTSAS_RAID_CHANNEL;
+                               goto out;
+                       }
+               }
+       } else {
+               list_for_each_entry(sas_info, &ioc->sas_device_info_list,
+                   list) {
+                       if (sas_info->is_cached ||
+                           sas_info->is_hidden_raid_component ||
+                           sas_info->is_logical_volume)
+                               continue;
+                       if (sas_info->fw.channel == fw_channel &&
+                           sas_info->fw.id == fw_id) {
+                               id = sas_info->os.id;
+                               channel = sas_info->os.channel;
+                               goto out;
+                       }
+               }
 
-       ioc->sas_discovery_runtime=1;
-       mptsas_delete_expander_phys(ioc);
-       mptsas_probe_hba_phys(ioc);
-       while (!mptsas_probe_expander_phys(ioc, &handle))
-               ;
-       ioc->sas_discovery_runtime=0;
-}
+       }
 
-/*
- * Work queue thread to handle Runtime discovery
- * Mere purpose is the hot add/delete of expanders
- *(Mutex LOCKED)
- */
-static void
-mptsas_discovery_work(struct work_struct *work)
-{
-       struct mptsas_discovery_event *ev =
-               container_of(work, struct mptsas_discovery_event, work);
-       MPT_ADAPTER *ioc = ev->ioc;
+ out:
+       mutex_unlock(&ioc->sas_device_info_mutex);
+
+       if (id != -1) {
+               shost_for_each_device(sdev, ioc->sh) {
+                       if (sdev->id == id && sdev->channel == channel) {
+                               if (current_depth > sdev->queue_depth) {
+                                       sdev_printk(KERN_INFO, sdev,
+                                           "strange observation, the queue "
+                                           "depth is (%d) meanwhile fw queue "
+                                           "depth (%d)\n", sdev->queue_depth,
+                                           current_depth);
+                                       continue;
+                               }
+                               depth = scsi_track_queue_full(sdev,
+                                   current_depth - 1);
+                               if (depth > 0)
+                                       sdev_printk(KERN_INFO, sdev,
+                                       "Queue depth reduced to (%d)\n",
+                                          depth);
+                               else if (depth < 0)
+                                       sdev_printk(KERN_INFO, sdev,
+                                       "Tagged Command Queueing is being "
+                                       "disabled\n");
+                               else if (depth == 0)
+                                       sdev_printk(KERN_INFO, sdev,
+                                       "Queue depth not changed yet\n");
+                       }
+               }
+       }
 
-       mutex_lock(&ioc->sas_discovery_mutex);
-       __mptsas_discovery_work(ioc);
-       mutex_unlock(&ioc->sas_discovery_mutex);
-       kfree(ev);
+       mptsas_free_fw_event(ioc, fw_event);
 }
 
+
 static struct mptsas_phyinfo *
 mptsas_find_phyinfo_by_sas_address(MPT_ADAPTER *ioc, u64 sas_address)
 {
@@ -2429,69 +3786,80 @@ mptsas_find_phyinfo_by_sas_address(MPT_ADAPTER *ioc, u64 sas_address)
        return phy_info;
 }
 
+/**
+ *     mptsas_find_phyinfo_by_phys_disk_num -
+ *     @ioc: Pointer to MPT_ADAPTER structure
+ *     @phys_disk_num:
+ *     @channel:
+ *     @id:
+ *
+ **/
 static struct mptsas_phyinfo *
-mptsas_find_phyinfo_by_target(MPT_ADAPTER *ioc, u8 channel, u8 id)
+mptsas_find_phyinfo_by_phys_disk_num(MPT_ADAPTER *ioc, u8 phys_disk_num,
+       u8 channel, u8 id)
 {
-       struct mptsas_portinfo *port_info;
        struct mptsas_phyinfo *phy_info = NULL;
+       struct mptsas_portinfo *port_info;
+       RaidPhysDiskPage1_t *phys_disk = NULL;
+       int num_paths;
+       u64 sas_address = 0;
        int i;
 
-       mutex_lock(&ioc->sas_topology_mutex);
-       list_for_each_entry(port_info, &ioc->sas_topology, list) {
-               for (i = 0; i < port_info->num_phys; i++) {
-                       if (!mptsas_is_end_device(
-                               &port_info->phy_info[i].attached))
-                               continue;
-                       if (port_info->phy_info[i].attached.id != id)
-                               continue;
-                       if (port_info->phy_info[i].attached.channel != channel)
-                               continue;
-                       phy_info = &port_info->phy_info[i];
-                       break;
+       phy_info = NULL;
+       if (!ioc->raid_data.pIocPg3)
+               return NULL;
+       /* dual port support */
+       num_paths = mpt_raid_phys_disk_get_num_paths(ioc, phys_disk_num);
+       if (!num_paths)
+               goto out;
+       phys_disk = kzalloc(offsetof(RaidPhysDiskPage1_t, Path) +
+          (num_paths * sizeof(RAID_PHYS_DISK1_PATH)), GFP_KERNEL);
+       if (!phys_disk)
+               goto out;
+       mpt_raid_phys_disk_pg1(ioc, phys_disk_num, phys_disk);
+       for (i = 0; i < num_paths; i++) {
+               if ((phys_disk->Path[i].Flags & 1) != 0)
+                       /* entry no longer valid */
+                       continue;
+               if ((id == phys_disk->Path[i].PhysDiskID) &&
+                   (channel == phys_disk->Path[i].PhysDiskBus)) {
+                       memcpy(&sas_address, &phys_disk->Path[i].WWID,
+                               sizeof(u64));
+                       phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
+                                       sas_address);
+                       goto out;
                }
        }
-       mutex_unlock(&ioc->sas_topology_mutex);
-       return phy_info;
-}
 
-static struct mptsas_phyinfo *
-mptsas_find_phyinfo_by_phys_disk_num(MPT_ADAPTER *ioc, u8 channel, u8 id)
-{
-       struct mptsas_portinfo *port_info;
-       struct mptsas_phyinfo *phy_info = NULL;
-       int i;
+ out:
+       kfree(phys_disk);
+       if (phy_info)
+               return phy_info;
 
+       /*
+        * Extra code to handle RAID0 case, where the sas_address is not updated
+        * in phys_disk_page_1 when hotswapped
+        */
        mutex_lock(&ioc->sas_topology_mutex);
        list_for_each_entry(port_info, &ioc->sas_topology, list) {
-               for (i = 0; i < port_info->num_phys; i++) {
+               for (i = 0; i < port_info->num_phys && !phy_info; i++) {
                        if (!mptsas_is_end_device(
                                &port_info->phy_info[i].attached))
                                continue;
                        if (port_info->phy_info[i].attached.phys_disk_num == ~0)
                                continue;
-                       if (port_info->phy_info[i].attached.phys_disk_num != id)
-                               continue;
-                       if (port_info->phy_info[i].attached.channel != channel)
-                               continue;
-                       phy_info = &port_info->phy_info[i];
-                       break;
+                       if ((port_info->phy_info[i].attached.phys_disk_num ==
+                           phys_disk_num) &&
+                           (port_info->phy_info[i].attached.id == id) &&
+                           (port_info->phy_info[i].attached.channel ==
+                            channel))
+                               phy_info = &port_info->phy_info[i];
                }
        }
        mutex_unlock(&ioc->sas_topology_mutex);
        return phy_info;
 }
 
-/*
- * Work queue thread to clear the persitency table
- */
-static void
-mptsas_persist_clear_table(struct work_struct *work)
-{
-       MPT_ADAPTER *ioc = container_of(work, MPT_ADAPTER, sas_persist_task);
-
-       mptbase_sas_persist_operation(ioc, MPI_SAS_OP_CLEAR_NOT_PRESENT);
-}
-
 static void
 mptsas_reprobe_lun(struct scsi_device *sdev, void *data)
 {
@@ -2517,7 +3885,8 @@ mptsas_adding_inactive_raid_components(MPT_ADAPTER *ioc, u8 channel, u8 id)
        pRaidVolumePage0_t              buffer = NULL;
        RaidPhysDiskPage0_t             phys_disk;
        int                             i;
-       struct mptsas_hotplug_event     *ev;
+       struct mptsas_phyinfo   *phy_info;
+       struct mptsas_devinfo           sas_device;
 
        memset(&cfg, 0 , sizeof(CONFIGPARMS));
        memset(&hdr, 0 , sizeof(ConfigPageHeader_t));
@@ -2557,20 +3926,16 @@ mptsas_adding_inactive_raid_components(MPT_ADAPTER *ioc, u8 channel, u8 id)
                    buffer->PhysDisk[i].PhysDiskNum, &phys_disk) != 0)
                        continue;
 
-               ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
-               if (!ev) {
-                       printk(MYIOC_s_WARN_FMT "mptsas: lost hotplug event\n", ioc->name);
-                       goto out;
-               }
+               if (mptsas_sas_device_pg0(ioc, &sas_device,
+                   (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
+                    MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
+                       (phys_disk.PhysDiskBus << 8) +
+                       phys_disk.PhysDiskID))
+                       continue;
 
-               INIT_WORK(&ev->work, mptsas_hotplug_work);
-               ev->ioc = ioc;
-               ev->id = phys_disk.PhysDiskID;
-               ev->channel = phys_disk.PhysDiskBus;
-               ev->phys_disk_num_valid = 1;
-               ev->phys_disk_num = phys_disk.PhysDiskNum;
-               ev->event_type = MPTSAS_ADD_DEVICE;
-               schedule_work(&ev->work);
+               phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
+                   sas_device.sas_address);
+               mptsas_add_end_device(ioc, phy_info);
        }
 
  out:
@@ -2582,417 +3947,386 @@ mptsas_adding_inactive_raid_components(MPT_ADAPTER *ioc, u8 channel, u8 id)
  * Work queue thread to handle SAS hotplug events
  */
 static void
-mptsas_hotplug_work(struct work_struct *work)
+mptsas_hotplug_work(MPT_ADAPTER *ioc, struct fw_event_work *fw_event,
+    struct mptsas_hotplug_event *hot_plug_info)
 {
-       struct mptsas_hotplug_event *ev =
-               container_of(work, struct mptsas_hotplug_event, work);
-
-       MPT_ADAPTER *ioc = ev->ioc;
        struct mptsas_phyinfo *phy_info;
-       struct sas_rphy *rphy;
-       struct sas_port *port;
-       struct scsi_device *sdev;
        struct scsi_target * starget;
-       struct sas_identify identify;
-       char *ds = NULL;
        struct mptsas_devinfo sas_device;
        VirtTarget *vtarget;
-       VirtDevice *vdevice;
+       int i;
+
+       switch (hot_plug_info->event_type) {
+
+       case MPTSAS_ADD_PHYSDISK:
+
+               if (!ioc->raid_data.pIocPg2)
+                       break;
+
+               for (i = 0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++) {
+                       if (ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID ==
+                           hot_plug_info->id) {
+                               printk(MYIOC_s_WARN_FMT "firmware bug: unable "
+                                   "to add hidden disk - target_id matchs "
+                                   "volume_id\n", ioc->name);
+                               mptsas_free_fw_event(ioc, fw_event);
+                               return;
+                       }
+               }
+               mpt_findImVolumes(ioc);
+
+       case MPTSAS_ADD_DEVICE:
+               memset(&sas_device, 0, sizeof(struct mptsas_devinfo));
+               mptsas_sas_device_pg0(ioc, &sas_device,
+                   (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
+                   MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
+                   (hot_plug_info->channel << 8) +
+                   hot_plug_info->id);
+
+               if (!sas_device.handle)
+                       return;
+
+               phy_info = mptsas_refreshing_device_handles(ioc, &sas_device);
+               if (!phy_info)
+                       break;
+
+               if (mptsas_get_rphy(phy_info))
+                       break;
+
+               mptsas_add_end_device(ioc, phy_info);
+               break;
 
-       mutex_lock(&ioc->sas_discovery_mutex);
-       switch (ev->event_type) {
        case MPTSAS_DEL_DEVICE:
+               phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
+                   hot_plug_info->sas_address);
+               mptsas_del_end_device(ioc, phy_info);
+               break;
 
-               phy_info = NULL;
-               if (ev->phys_disk_num_valid) {
-                       if (ev->hidden_raid_component){
-                               if (mptsas_sas_device_pg0(ioc, &sas_device,
-                                   (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
-                                    MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
-                                   (ev->channel << 8) + ev->id)) {
-                                       dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
-                                       "%s: exit at line=%d\n", ioc->name,
-                                               __func__, __LINE__));
-                                       break;
-                               }
-                               phy_info = mptsas_find_phyinfo_by_sas_address(
-                                   ioc, sas_device.sas_address);
-                       }else
-                               phy_info = mptsas_find_phyinfo_by_phys_disk_num(
-                                   ioc, ev->channel, ev->phys_disk_num);
-               }
+       case MPTSAS_DEL_PHYSDISK:
 
-               if (!phy_info)
-                       phy_info = mptsas_find_phyinfo_by_target(ioc,
-                           ev->channel, ev->id);
+               mpt_findImVolumes(ioc);
 
-               /*
-                * Sanity checks, for non-existing phys and remote rphys.
-                */
-               if (!phy_info){
+               phy_info = mptsas_find_phyinfo_by_phys_disk_num(
+                               ioc, hot_plug_info->phys_disk_num,
+                               hot_plug_info->channel,
+                               hot_plug_info->id);
+               mptsas_del_end_device(ioc, phy_info);
+               break;
+
+       case MPTSAS_ADD_PHYSDISK_REPROBE:
+
+               if (mptsas_sas_device_pg0(ioc, &sas_device,
+                   (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
+                    MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
+                   (hot_plug_info->channel << 8) + hot_plug_info->id)) {
                        dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
-                               "%s: exit at line=%d\n", ioc->name,
-                               __func__, __LINE__));
+                       "%s: fw_id=%d exit at line=%d\n", ioc->name,
+                                __func__, hot_plug_info->id, __LINE__));
                        break;
                }
-               if (!phy_info->port_details) {
+
+               phy_info = mptsas_find_phyinfo_by_sas_address(
+                   ioc, sas_device.sas_address);
+
+               if (!phy_info) {
                        dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
-                               "%s: exit at line=%d\n", ioc->name,
-                               __func__, __LINE__));
+                               "%s: fw_id=%d exit at line=%d\n", ioc->name,
+                                __func__, hot_plug_info->id, __LINE__));
                        break;
                }
-               rphy = mptsas_get_rphy(phy_info);
-               if (!rphy) {
+
+               starget = mptsas_get_starget(phy_info);
+               if (!starget) {
                        dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
-                               "%s: exit at line=%d\n", ioc->name,
-                               __func__, __LINE__));
+                               "%s: fw_id=%d exit at line=%d\n", ioc->name,
+                                __func__, hot_plug_info->id, __LINE__));
                        break;
                }
 
-               port = mptsas_get_port(phy_info);
-               if (!port) {
+               vtarget = starget->hostdata;
+               if (!vtarget) {
                        dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
-                               "%s: exit at line=%d\n", ioc->name,
-                               __func__, __LINE__));
+                               "%s: fw_id=%d exit at line=%d\n", ioc->name,
+                                __func__, hot_plug_info->id, __LINE__));
                        break;
                }
 
-               starget = mptsas_get_starget(phy_info);
-               if (starget) {
-                       vtarget = starget->hostdata;
-
-                       if (!vtarget) {
-                               dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
-                                       "%s: exit at line=%d\n", ioc->name,
-                                       __func__, __LINE__));
-                               break;
-                       }
+               mpt_findImVolumes(ioc);
 
-                       /*
-                        * Handling  RAID components
-                        */
-                       if (ev->phys_disk_num_valid &&
-                           ev->hidden_raid_component) {
-                               printk(MYIOC_s_INFO_FMT
-                                   "RAID Hidding: channel=%d, id=%d, "
-                                   "physdsk %d \n", ioc->name, ev->channel,
-                                   ev->id, ev->phys_disk_num);
-                               vtarget->id = ev->phys_disk_num;
-                               vtarget->tflags |=
-                                   MPT_TARGET_FLAGS_RAID_COMPONENT;
-                               mptsas_reprobe_target(starget, 1);
-                               phy_info->attached.phys_disk_num =
-                                   ev->phys_disk_num;
-                       break;
-                       }
-               }
+               starget_printk(KERN_INFO, starget, MYIOC_s_FMT "RAID Hidding: "
+                   "fw_channel=%d, fw_id=%d, physdsk %d, sas_addr 0x%llx\n",
+                   ioc->name, hot_plug_info->channel, hot_plug_info->id,
+                   hot_plug_info->phys_disk_num, (unsigned long long)
+                   sas_device.sas_address);
 
-               if (phy_info->attached.device_info &
-                   MPI_SAS_DEVICE_INFO_SSP_TARGET)
-                       ds = "ssp";
-               if (phy_info->attached.device_info &
-                   MPI_SAS_DEVICE_INFO_STP_TARGET)
-                       ds = "stp";
-               if (phy_info->attached.device_info &
-                   MPI_SAS_DEVICE_INFO_SATA_DEVICE)
-                       ds = "sata";
-
-               printk(MYIOC_s_INFO_FMT
-                      "removing %s device, channel %d, id %d, phy %d\n",
-                      ioc->name, ds, ev->channel, ev->id, phy_info->phy_id);
-               dev_printk(KERN_DEBUG, &port->dev, MYIOC_s_FMT
-                   "delete port (%d)\n", ioc->name, port->port_identifier);
-               sas_port_delete(port);
-               mptsas_port_delete(ioc, phy_info->port_details);
+               vtarget->id = hot_plug_info->phys_disk_num;
+               vtarget->tflags |= MPT_TARGET_FLAGS_RAID_COMPONENT;
+               phy_info->attached.phys_disk_num = hot_plug_info->phys_disk_num;
+               mptsas_reprobe_target(starget, 1);
                break;
-       case MPTSAS_ADD_DEVICE:
 
-               if (ev->phys_disk_num_valid)
-                       mpt_findImVolumes(ioc);
+       case MPTSAS_DEL_PHYSDISK_REPROBE:
 
-               /*
-                * Refresh sas device pg0 data
-                */
                if (mptsas_sas_device_pg0(ioc, &sas_device,
                    (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
                     MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
-                       (ev->channel << 8) + ev->id)) {
+                       (hot_plug_info->channel << 8) + hot_plug_info->id)) {
                                dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
-                                       "%s: exit at line=%d\n", ioc->name,
-                                       __func__, __LINE__));
+                                   "%s: fw_id=%d exit at line=%d\n",
+                                   ioc->name, __func__,
+                                   hot_plug_info->id, __LINE__));
                        break;
                }
 
-               __mptsas_discovery_work(ioc);
-
                phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
                                sas_device.sas_address);
-
-               if (!phy_info || !phy_info->port_details) {
+               if (!phy_info) {
                        dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
-                               "%s: exit at line=%d\n", ioc->name,
-                               __func__, __LINE__));
+                           "%s: fw_id=%d exit at line=%d\n", ioc->name,
+                        __func__, hot_plug_info->id, __LINE__));
                        break;
                }
 
                starget = mptsas_get_starget(phy_info);
-               if (starget && (!ev->hidden_raid_component)){
-
-                       vtarget = starget->hostdata;
-
-                       if (!vtarget) {
-                               dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
-                                   "%s: exit at line=%d\n", ioc->name,
-                                   __func__, __LINE__));
-                               break;
-                       }
-                       /*
-                        * Handling  RAID components
-                        */
-                       if (vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT) {
-                               printk(MYIOC_s_INFO_FMT
-                                   "RAID Exposing: channel=%d, id=%d, "
-                                   "physdsk %d \n", ioc->name, ev->channel,
-                                   ev->id, ev->phys_disk_num);
-                               vtarget->tflags &=
-                                   ~MPT_TARGET_FLAGS_RAID_COMPONENT;
-                               vtarget->id = ev->id;
-                               mptsas_reprobe_target(starget, 0);
-                               phy_info->attached.phys_disk_num = ~0;
-                       }
+               if (!starget) {
+                       dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+                           "%s: fw_id=%d exit at line=%d\n", ioc->name,
+                        __func__, hot_plug_info->id, __LINE__));
                        break;
                }
 
-               if (mptsas_get_rphy(phy_info)) {
+               vtarget = starget->hostdata;
+               if (!vtarget) {
                        dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
-                               "%s: exit at line=%d\n", ioc->name,
-                               __func__, __LINE__));
-                       if (ev->channel) printk("%d\n", __LINE__);
+                           "%s: fw_id=%d exit at line=%d\n", ioc->name,
+                        __func__, hot_plug_info->id, __LINE__));
                        break;
                }
 
-               port = mptsas_get_port(phy_info);
-               if (!port) {
+               if (!(vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT)) {
                        dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
-                               "%s: exit at line=%d\n", ioc->name,
-                               __func__, __LINE__));
+                           "%s: fw_id=%d exit at line=%d\n", ioc->name,
+                        __func__, hot_plug_info->id, __LINE__));
                        break;
                }
-               memcpy(&phy_info->attached, &sas_device,
-                   sizeof(struct mptsas_devinfo));
-
-               if (phy_info->attached.device_info &
-                   MPI_SAS_DEVICE_INFO_SSP_TARGET)
-                       ds = "ssp";
-               if (phy_info->attached.device_info &
-                   MPI_SAS_DEVICE_INFO_STP_TARGET)
-                       ds = "stp";
-               if (phy_info->attached.device_info &
-                   MPI_SAS_DEVICE_INFO_SATA_DEVICE)
-                       ds = "sata";
-
-               printk(MYIOC_s_INFO_FMT
-                      "attaching %s device, channel %d, id %d, phy %d\n",
-                      ioc->name, ds, ev->channel, ev->id, ev->phy_id);
 
-               mptsas_parse_device_info(&identify, &phy_info->attached);
-               rphy = sas_end_device_alloc(port);
-               if (!rphy) {
-                       dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
-                               "%s: exit at line=%d\n", ioc->name,
-                               __func__, __LINE__));
-                       break; /* non-fatal: an rphy can be added later */
-               }
+               mpt_findImVolumes(ioc);
 
-               rphy->identify = identify;
-               if (sas_rphy_add(rphy)) {
-                       dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
-                               "%s: exit at line=%d\n", ioc->name,
-                               __func__, __LINE__));
-                       sas_rphy_free(rphy);
-                       break;
-               }
-               mptsas_set_rphy(ioc, phy_info, rphy);
+               starget_printk(KERN_INFO, starget, MYIOC_s_FMT "RAID Exposing:"
+                   " fw_channel=%d, fw_id=%d, physdsk %d, sas_addr 0x%llx\n",
+                   ioc->name, hot_plug_info->channel, hot_plug_info->id,
+                   hot_plug_info->phys_disk_num, (unsigned long long)
+                   sas_device.sas_address);
+
+               vtarget->tflags &= ~MPT_TARGET_FLAGS_RAID_COMPONENT;
+               vtarget->id = hot_plug_info->id;
+               phy_info->attached.phys_disk_num = ~0;
+               mptsas_reprobe_target(starget, 0);
+               mptsas_add_device_component_by_fw(ioc,
+                   hot_plug_info->channel, hot_plug_info->id);
                break;
+
        case MPTSAS_ADD_RAID:
-               sdev = scsi_device_lookup(ioc->sh, MPTSAS_RAID_CHANNEL,
-                   ev->id, 0);
-               if (sdev) {
-                       scsi_device_put(sdev);
-                       break;
-               }
-               printk(MYIOC_s_INFO_FMT
-                      "attaching raid volume, channel %d, id %d\n",
-                      ioc->name, MPTSAS_RAID_CHANNEL, ev->id);
-               scsi_add_device(ioc->sh, MPTSAS_RAID_CHANNEL, ev->id, 0);
+
                mpt_findImVolumes(ioc);
+               printk(MYIOC_s_INFO_FMT "attaching raid volume, channel %d, "
+                   "id %d\n", ioc->name, MPTSAS_RAID_CHANNEL,
+                   hot_plug_info->id);
+               scsi_add_device(ioc->sh, MPTSAS_RAID_CHANNEL,
+                   hot_plug_info->id, 0);
                break;
+
        case MPTSAS_DEL_RAID:
-               sdev = scsi_device_lookup(ioc->sh, MPTSAS_RAID_CHANNEL,
-                   ev->id, 0);
-               if (!sdev)
-                       break;
-               printk(MYIOC_s_INFO_FMT
-                      "removing raid volume, channel %d, id %d\n",
-                      ioc->name, MPTSAS_RAID_CHANNEL, ev->id);
-               vdevice = sdev->hostdata;
-               scsi_remove_device(sdev);
-               scsi_device_put(sdev);
+
                mpt_findImVolumes(ioc);
+               printk(MYIOC_s_INFO_FMT "removing raid volume, channel %d, "
+                   "id %d\n", ioc->name, MPTSAS_RAID_CHANNEL,
+                   hot_plug_info->id);
+               scsi_remove_device(hot_plug_info->sdev);
+               scsi_device_put(hot_plug_info->sdev);
                break;
+
        case MPTSAS_ADD_INACTIVE_VOLUME:
+
+               mpt_findImVolumes(ioc);
                mptsas_adding_inactive_raid_components(ioc,
-                   ev->channel, ev->id);
+                   hot_plug_info->channel, hot_plug_info->id);
                break;
-       case MPTSAS_IGNORE_EVENT:
+
        default:
                break;
        }
 
-       mutex_unlock(&ioc->sas_discovery_mutex);
-       kfree(ev);
+       mptsas_free_fw_event(ioc, fw_event);
 }
 
 static void
-mptsas_send_sas_event(MPT_ADAPTER *ioc,
-               EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data)
+mptsas_send_sas_event(struct fw_event_work *fw_event)
 {
-       struct mptsas_hotplug_event *ev;
-       u32 device_info = le32_to_cpu(sas_event_data->DeviceInfo);
-       __le64 sas_address;
+       MPT_ADAPTER *ioc;
+       struct mptsas_hotplug_event hot_plug_info;
+       EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data;
+       u32 device_info;
+       u64 sas_address;
+
+       ioc = fw_event->ioc;
+       sas_event_data = (EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *)
+           fw_event->event_data;
+       device_info = le32_to_cpu(sas_event_data->DeviceInfo);
 
        if ((device_info &
-            (MPI_SAS_DEVICE_INFO_SSP_TARGET |
-             MPI_SAS_DEVICE_INFO_STP_TARGET |
-             MPI_SAS_DEVICE_INFO_SATA_DEVICE )) == 0)
+               (MPI_SAS_DEVICE_INFO_SSP_TARGET |
+               MPI_SAS_DEVICE_INFO_STP_TARGET |
+               MPI_SAS_DEVICE_INFO_SATA_DEVICE)) == 0) {
+               mptsas_free_fw_event(ioc, fw_event);
+               return;
+       }
+
+       if (sas_event_data->ReasonCode ==
+               MPI_EVENT_SAS_DEV_STAT_RC_NO_PERSIST_ADDED) {
+               mptbase_sas_persist_operation(ioc,
+               MPI_SAS_OP_CLEAR_NOT_PRESENT);
+               mptsas_free_fw_event(ioc, fw_event);
                return;
+       }
 
        switch (sas_event_data->ReasonCode) {
        case MPI_EVENT_SAS_DEV_STAT_RC_NOT_RESPONDING:
-
-               mptsas_target_reset_queue(ioc, sas_event_data);
-               break;
-
        case MPI_EVENT_SAS_DEV_STAT_RC_ADDED:
-               ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
-               if (!ev) {
-                       printk(MYIOC_s_WARN_FMT "lost hotplug event\n", ioc->name);
-                       break;
-               }
-
-               INIT_WORK(&ev->work, mptsas_hotplug_work);
-               ev->ioc = ioc;
-               ev->handle = le16_to_cpu(sas_event_data->DevHandle);
-               ev->parent_handle =
-                   le16_to_cpu(sas_event_data->ParentDevHandle);
-               ev->channel = sas_event_data->Bus;
-               ev->id = sas_event_data->TargetID;
-               ev->phy_id = sas_event_data->PhyNum;
+               memset(&hot_plug_info, 0, sizeof(struct mptsas_hotplug_event));
+               hot_plug_info.handle = le16_to_cpu(sas_event_data->DevHandle);
+               hot_plug_info.channel = sas_event_data->Bus;
+               hot_plug_info.id = sas_event_data->TargetID;
+               hot_plug_info.phy_id = sas_event_data->PhyNum;
                memcpy(&sas_address, &sas_event_data->SASAddress,
-                   sizeof(__le64));
-               ev->sas_address = le64_to_cpu(sas_address);
-               ev->device_info = device_info;
-
+                   sizeof(u64));
+               hot_plug_info.sas_address = le64_to_cpu(sas_address);
+               hot_plug_info.device_info = device_info;
                if (sas_event_data->ReasonCode &
                    MPI_EVENT_SAS_DEV_STAT_RC_ADDED)
-                       ev->event_type = MPTSAS_ADD_DEVICE;
+                       hot_plug_info.event_type = MPTSAS_ADD_DEVICE;
                else
-                       ev->event_type = MPTSAS_DEL_DEVICE;
-               schedule_work(&ev->work);
+                       hot_plug_info.event_type = MPTSAS_DEL_DEVICE;
+               mptsas_hotplug_work(ioc, fw_event, &hot_plug_info);
                break;
+
        case MPI_EVENT_SAS_DEV_STAT_RC_NO_PERSIST_ADDED:
-       /*
-        * Persistent table is full.
-        */
-               INIT_WORK(&ioc->sas_persist_task,
-                   mptsas_persist_clear_table);
-               schedule_work(&ioc->sas_persist_task);
+               mptbase_sas_persist_operation(ioc,
+                   MPI_SAS_OP_CLEAR_NOT_PRESENT);
+               mptsas_free_fw_event(ioc, fw_event);
                break;
-       /*
-        * TODO, handle other events
-        */
+
        case MPI_EVENT_SAS_DEV_STAT_RC_SMART_DATA:
-       case MPI_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED:
+       /* TODO */
        case MPI_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET:
-       case MPI_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL:
-       case MPI_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL:
-       case MPI_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL:
-       case MPI_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL:
+       /* TODO */
        default:
+               mptsas_free_fw_event(ioc, fw_event);
                break;
        }
 }
+
 static void
-mptsas_send_raid_event(MPT_ADAPTER *ioc,
-               EVENT_DATA_RAID *raid_event_data)
+mptsas_send_raid_event(struct fw_event_work *fw_event)
 {
-       struct mptsas_hotplug_event *ev;
-       int status = le32_to_cpu(raid_event_data->SettingsStatus);
-       int state = (status >> 8) & 0xff;
-
-       if (ioc->bus_type != SAS)
-               return;
-
-       ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
-       if (!ev) {
-               printk(MYIOC_s_WARN_FMT "lost hotplug event\n", ioc->name);
-               return;
+       MPT_ADAPTER *ioc;
+       EVENT_DATA_RAID *raid_event_data;
+       struct mptsas_hotplug_event hot_plug_info;
+       int status;
+       int state;
+       struct scsi_device *sdev = NULL;
+       VirtDevice *vdevice = NULL;
+       RaidPhysDiskPage0_t phys_disk;
+
+       ioc = fw_event->ioc;
+       raid_event_data = (EVENT_DATA_RAID *)fw_event->event_data;
+       status = le32_to_cpu(raid_event_data->SettingsStatus);
+       state = (status >> 8) & 0xff;
+
+       memset(&hot_plug_info, 0, sizeof(struct mptsas_hotplug_event));
+       hot_plug_info.id = raid_event_data->VolumeID;
+       hot_plug_info.channel = raid_event_data->VolumeBus;
+       hot_plug_info.phys_disk_num = raid_event_data->PhysDiskNum;
+
+       if (raid_event_data->ReasonCode == MPI_EVENT_RAID_RC_VOLUME_DELETED ||
+           raid_event_data->ReasonCode == MPI_EVENT_RAID_RC_VOLUME_CREATED ||
+           raid_event_data->ReasonCode ==
+           MPI_EVENT_RAID_RC_VOLUME_STATUS_CHANGED) {
+               sdev = scsi_device_lookup(ioc->sh, MPTSAS_RAID_CHANNEL,
+                   hot_plug_info.id, 0);
+               hot_plug_info.sdev = sdev;
+               if (sdev)
+                       vdevice = sdev->hostdata;
        }
 
-       INIT_WORK(&ev->work, mptsas_hotplug_work);
-       ev->ioc = ioc;
-       ev->id = raid_event_data->VolumeID;
-       ev->channel = raid_event_data->VolumeBus;
-       ev->event_type = MPTSAS_IGNORE_EVENT;
+       devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Entering %s: "
+           "ReasonCode=%02x\n", ioc->name, __func__,
+           raid_event_data->ReasonCode));
 
        switch (raid_event_data->ReasonCode) {
        case MPI_EVENT_RAID_RC_PHYSDISK_DELETED:
-               ev->phys_disk_num_valid = 1;
-               ev->phys_disk_num = raid_event_data->PhysDiskNum;
-               ev->event_type = MPTSAS_ADD_DEVICE;
+               hot_plug_info.event_type = MPTSAS_DEL_PHYSDISK_REPROBE;
                break;
        case MPI_EVENT_RAID_RC_PHYSDISK_CREATED:
-               ev->phys_disk_num_valid = 1;
-               ev->phys_disk_num = raid_event_data->PhysDiskNum;
-               ev->hidden_raid_component = 1;
-               ev->event_type = MPTSAS_DEL_DEVICE;
+               hot_plug_info.event_type = MPTSAS_ADD_PHYSDISK_REPROBE;
                break;
        case MPI_EVENT_RAID_RC_PHYSDISK_STATUS_CHANGED:
                switch (state) {
                case MPI_PD_STATE_ONLINE:
                case MPI_PD_STATE_NOT_COMPATIBLE:
-                       ev->phys_disk_num_valid = 1;
-                       ev->phys_disk_num = raid_event_data->PhysDiskNum;
-                       ev->hidden_raid_component = 1;
-                       ev->event_type = MPTSAS_ADD_DEVICE;
+                       mpt_raid_phys_disk_pg0(ioc,
+                           raid_event_data->PhysDiskNum, &phys_disk);
+                       hot_plug_info.id = phys_disk.PhysDiskID;
+                       hot_plug_info.channel = phys_disk.PhysDiskBus;
+                       hot_plug_info.event_type = MPTSAS_ADD_PHYSDISK;
                        break;
+               case MPI_PD_STATE_FAILED:
                case MPI_PD_STATE_MISSING:
                case MPI_PD_STATE_OFFLINE_AT_HOST_REQUEST:
                case MPI_PD_STATE_FAILED_AT_HOST_REQUEST:
                case MPI_PD_STATE_OFFLINE_FOR_ANOTHER_REASON:
-                       ev->phys_disk_num_valid = 1;
-                       ev->phys_disk_num = raid_event_data->PhysDiskNum;
-                       ev->event_type = MPTSAS_DEL_DEVICE;
+                       hot_plug_info.event_type = MPTSAS_DEL_PHYSDISK;
                        break;
                default:
                        break;
                }
                break;
        case MPI_EVENT_RAID_RC_VOLUME_DELETED:
-               ev->event_type = MPTSAS_DEL_RAID;
+               if (!sdev)
+                       break;
+               vdevice->vtarget->deleted = 1; /* block IO */
+               hot_plug_info.event_type = MPTSAS_DEL_RAID;
                break;
        case MPI_EVENT_RAID_RC_VOLUME_CREATED:
-               ev->event_type = MPTSAS_ADD_RAID;
+               if (sdev) {
+                       scsi_device_put(sdev);
+                       break;
+               }
+               hot_plug_info.event_type = MPTSAS_ADD_RAID;
                break;
        case MPI_EVENT_RAID_RC_VOLUME_STATUS_CHANGED:
+               if (!(status & MPI_RAIDVOL0_STATUS_FLAG_ENABLED)) {
+                       if (!sdev)
+                               break;
+                       vdevice->vtarget->deleted = 1; /* block IO */
+                       hot_plug_info.event_type = MPTSAS_DEL_RAID;
+                       break;
+               }
                switch (state) {
                case MPI_RAIDVOL0_STATUS_STATE_FAILED:
                case MPI_RAIDVOL0_STATUS_STATE_MISSING:
-                       ev->event_type = MPTSAS_DEL_RAID;
+                       if (!sdev)
+                               break;
+                       vdevice->vtarget->deleted = 1; /* block IO */
+                       hot_plug_info.event_type = MPTSAS_DEL_RAID;
                        break;
                case MPI_RAIDVOL0_STATUS_STATE_OPTIMAL:
                case MPI_RAIDVOL0_STATUS_STATE_DEGRADED:
-                       ev->event_type = MPTSAS_ADD_RAID;
+                       if (sdev) {
+                               scsi_device_put(sdev);
+                               break;
+                       }
+                       hot_plug_info.event_type = MPTSAS_ADD_RAID;
                        break;
                default:
                        break;
@@ -3001,32 +4335,188 @@ mptsas_send_raid_event(MPT_ADAPTER *ioc,
        default:
                break;
        }
-       schedule_work(&ev->work);
+
+       if (hot_plug_info.event_type != MPTSAS_IGNORE_EVENT)
+               mptsas_hotplug_work(ioc, fw_event, &hot_plug_info);
+       else
+               mptsas_free_fw_event(ioc, fw_event);
 }
 
-static void
-mptsas_send_discovery_event(MPT_ADAPTER *ioc,
-       EVENT_DATA_SAS_DISCOVERY *discovery_data)
+/**
+ *     mptsas_issue_tm - send mptsas internal tm request
+ *     @ioc: Pointer to MPT_ADAPTER structure
+ *     @type: Task Management type
+ *     @channel: channel number for task management
+ *     @id: Logical Target ID for reset (if appropriate)
+ *     @lun: Logical unit for reset (if appropriate)
+ *     @task_context: Context for the task to be aborted
+ *     @timeout: timeout for task management control
+ *
+ *     return 0 on success and -1 on failure:
+ *
+ */
+static int
+mptsas_issue_tm(MPT_ADAPTER *ioc, u8 type, u8 channel, u8 id, u64 lun,
+       int task_context, ulong timeout, u8 *issue_reset)
 {
-       struct mptsas_discovery_event *ev;
+       MPT_FRAME_HDR   *mf;
+       SCSITaskMgmt_t  *pScsiTm;
+       int              retval;
+       unsigned long    timeleft;
+
+       *issue_reset = 0;
+       mf = mpt_get_msg_frame(mptsasDeviceResetCtx, ioc);
+       if (mf == NULL) {
+               retval = -1; /* return failure */
+               dtmprintk(ioc, printk(MYIOC_s_WARN_FMT "TaskMgmt request: no "
+                   "msg frames!!\n", ioc->name));
+               goto out;
+       }
 
-       /*
-        * DiscoveryStatus
-        *
-        * This flag will be non-zero when firmware
-        * kicks off discovery, and return to zero
-        * once its completed.
-        */
-       if (discovery_data->DiscoveryStatus)
-               return;
+       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt request: mr = %p, "
+           "task_type = 0x%02X,\n\t timeout = %ld, fw_channel = %d, "
+           "fw_id = %d, lun = %lld,\n\t task_context = 0x%x\n", ioc->name, mf,
+            type, timeout, channel, id, (unsigned long long)lun,
+            task_context));
+
+       pScsiTm = (SCSITaskMgmt_t *) mf;
+       memset(pScsiTm, 0, sizeof(SCSITaskMgmt_t));
+       pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
+       pScsiTm->TaskType = type;
+       pScsiTm->MsgFlags = 0;
+       pScsiTm->TargetID = id;
+       pScsiTm->Bus = channel;
+       pScsiTm->ChainOffset = 0;
+       pScsiTm->Reserved = 0;
+       pScsiTm->Reserved1 = 0;
+       pScsiTm->TaskMsgContext = task_context;
+       int_to_scsilun(lun, (struct scsi_lun *)pScsiTm->LUN);
+
+       INITIALIZE_MGMT_STATUS(ioc->taskmgmt_cmds.status)
+       CLEAR_MGMT_STATUS(ioc->internal_cmds.status)
+       retval = 0;
+       mpt_put_msg_frame_hi_pri(mptsasDeviceResetCtx, ioc, mf);
+
+       /* Now wait for the command to complete */
+       timeleft = wait_for_completion_timeout(&ioc->taskmgmt_cmds.done,
+           timeout*HZ);
+       if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+               retval = -1; /* return failure */
+               dtmprintk(ioc, printk(MYIOC_s_ERR_FMT
+                   "TaskMgmt request: TIMED OUT!(mr=%p)\n", ioc->name, mf));
+               mpt_free_msg_frame(ioc, mf);
+               if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET)
+                       goto out;
+               *issue_reset = 1;
+               goto out;
+       }
+
+       if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_RF_VALID)) {
+               retval = -1; /* return failure */
+               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "TaskMgmt request: failed with no reply\n", ioc->name));
+               goto out;
+       }
+
+ out:
+       CLEAR_MGMT_STATUS(ioc->taskmgmt_cmds.status)
+       return retval;
+}
 
-       ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
-       if (!ev)
+/**
+ *     mptsas_broadcast_primative_work - Handle broadcast primitives
+ *     @work: work queue payload containing info describing the event
+ *
+ *     this will be handled in workqueue context.
+ */
+static void
+mptsas_broadcast_primative_work(struct fw_event_work *fw_event)
+{
+       MPT_ADAPTER *ioc = fw_event->ioc;
+       MPT_FRAME_HDR   *mf;
+       VirtDevice      *vdevice;
+       int                     ii;
+       struct scsi_cmnd        *sc;
+       SCSITaskMgmtReply_t     *pScsiTmReply;
+       u8                      issue_reset;
+       int                     task_context;
+       u8                      channel, id;
+       int                      lun;
+       u32                      termination_count;
+       u32                      query_count;
+
+       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+           "%s - enter\n", ioc->name, __func__));
+
+       mutex_lock(&ioc->taskmgmt_cmds.mutex);
+       if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0) {
+               mutex_unlock(&ioc->taskmgmt_cmds.mutex);
+               mptsas_requeue_fw_event(ioc, fw_event, 1000);
                return;
-       INIT_WORK(&ev->work, mptsas_discovery_work);
-       ev->ioc = ioc;
-       schedule_work(&ev->work);
-};
+       }
+
+       issue_reset = 0;
+       termination_count = 0;
+       query_count = 0;
+       mpt_findImVolumes(ioc);
+       pScsiTmReply = (SCSITaskMgmtReply_t *) ioc->taskmgmt_cmds.reply;
+
+       for (ii = 0; ii < ioc->req_depth; ii++) {
+               if (ioc->fw_events_off)
+                       goto out;
+               sc = mptscsih_get_scsi_lookup(ioc, ii);
+               if (!sc)
+                       continue;
+               mf = MPT_INDEX_2_MFPTR(ioc, ii);
+               if (!mf)
+                       continue;
+               task_context = mf->u.frame.hwhdr.msgctxu.MsgContext;
+               vdevice = sc->device->hostdata;
+               if (!vdevice || !vdevice->vtarget)
+                       continue;
+               if (vdevice->vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT)
+                       continue; /* skip hidden raid components */
+               if (vdevice->vtarget->raidVolume)
+                       continue; /* skip hidden raid components */
+               channel = vdevice->vtarget->channel;
+               id = vdevice->vtarget->id;
+               lun = vdevice->lun;
+               if (mptsas_issue_tm(ioc, MPI_SCSITASKMGMT_TASKTYPE_QUERY_TASK,
+                   channel, id, (u64)lun, task_context, 30, &issue_reset))
+                       goto out;
+               query_count++;
+               termination_count +=
+                   le32_to_cpu(pScsiTmReply->TerminationCount);
+               if ((pScsiTmReply->IOCStatus == MPI_IOCSTATUS_SUCCESS) &&
+                   (pScsiTmReply->ResponseCode ==
+                   MPI_SCSITASKMGMT_RSP_TM_SUCCEEDED ||
+                   pScsiTmReply->ResponseCode ==
+                   MPI_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC))
+                       continue;
+               if (mptsas_issue_tm(ioc,
+                   MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET,
+                   channel, id, (u64)lun, 0, 30, &issue_reset))
+                       goto out;
+               termination_count +=
+                   le32_to_cpu(pScsiTmReply->TerminationCount);
+       }
+
+ out:
+       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+           "%s - exit, query_count = %d termination_count = %d\n",
+           ioc->name, __func__, query_count, termination_count));
+
+       ioc->broadcast_aen_busy = 0;
+       mpt_clear_taskmgmt_in_progress_flag(ioc);
+       mutex_unlock(&ioc->taskmgmt_cmds.mutex);
+
+       if (issue_reset) {
+               printk(MYIOC_s_WARN_FMT "Issuing Reset from %s!!\n",
+                   ioc->name, __func__);
+               mpt_HardResetHandler(ioc, CAN_SLEEP);
+       }
+       mptsas_free_fw_event(ioc, fw_event);
+}
 
 /*
  * mptsas_send_ir2_event - handle exposing hidden disk when
@@ -3037,76 +4527,159 @@ mptsas_send_discovery_event(MPT_ADAPTER *ioc,
  *
  */
 static void
-mptsas_send_ir2_event(MPT_ADAPTER *ioc, PTR_MPI_EVENT_DATA_IR2 ir2_data)
+mptsas_send_ir2_event(struct fw_event_work *fw_event)
 {
-       struct mptsas_hotplug_event *ev;
-
-       if (ir2_data->ReasonCode !=
-           MPI_EVENT_IR2_RC_FOREIGN_CFG_DETECTED)
-               return;
-
-       ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
-       if (!ev)
+       MPT_ADAPTER     *ioc;
+       struct mptsas_hotplug_event hot_plug_info;
+       MPI_EVENT_DATA_IR2      *ir2_data;
+       u8 reasonCode;
+       RaidPhysDiskPage0_t phys_disk;
+
+       ioc = fw_event->ioc;
+       ir2_data = (MPI_EVENT_DATA_IR2 *)fw_event->event_data;
+       reasonCode = ir2_data->ReasonCode;
+
+       devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Entering %s: "
+           "ReasonCode=%02x\n", ioc->name, __func__, reasonCode));
+
+       memset(&hot_plug_info, 0, sizeof(struct mptsas_hotplug_event));
+       hot_plug_info.id = ir2_data->TargetID;
+       hot_plug_info.channel = ir2_data->Bus;
+       switch (reasonCode) {
+       case MPI_EVENT_IR2_RC_FOREIGN_CFG_DETECTED:
+               hot_plug_info.event_type = MPTSAS_ADD_INACTIVE_VOLUME;
+               break;
+       case MPI_EVENT_IR2_RC_DUAL_PORT_REMOVED:
+               hot_plug_info.phys_disk_num = ir2_data->PhysDiskNum;
+               hot_plug_info.event_type = MPTSAS_DEL_PHYSDISK;
+               break;
+       case MPI_EVENT_IR2_RC_DUAL_PORT_ADDED:
+               hot_plug_info.phys_disk_num = ir2_data->PhysDiskNum;
+               mpt_raid_phys_disk_pg0(ioc,
+                   ir2_data->PhysDiskNum, &phys_disk);
+               hot_plug_info.id = phys_disk.PhysDiskID;
+               hot_plug_info.event_type = MPTSAS_ADD_PHYSDISK;
+               break;
+       default:
+               mptsas_free_fw_event(ioc, fw_event);
                return;
-
-       INIT_WORK(&ev->work, mptsas_hotplug_work);
-       ev->ioc = ioc;
-       ev->id = ir2_data->TargetID;
-       ev->channel = ir2_data->Bus;
-       ev->event_type = MPTSAS_ADD_INACTIVE_VOLUME;
-
-       schedule_work(&ev->work);
-};
+       }
+       mptsas_hotplug_work(ioc, fw_event, &hot_plug_info);
+}
 
 static int
 mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply)
 {
-       int rc=1;
-       u8 event = le32_to_cpu(reply->Event) & 0xFF;
-
-       if (!ioc->sh)
-               goto out;
+       u32 event = le32_to_cpu(reply->Event);
+       int sz, event_data_sz;
+       struct fw_event_work *fw_event;
+       unsigned long delay;
 
-       /*
-        * sas_discovery_ignore_events
-        *
-        * This flag is to prevent anymore processing of
-        * sas events once mptsas_remove function is called.
-        */
-       if (ioc->sas_discovery_ignore_events) {
-               rc = mptscsih_event_process(ioc, reply);
-               goto out;
-       }
+       /* events turned off due to host reset or driver unloading */
+       if (ioc->fw_events_off)
+               return 0;
 
+       delay = msecs_to_jiffies(1);
        switch (event) {
+       case MPI_EVENT_SAS_BROADCAST_PRIMITIVE:
+       {
+               EVENT_DATA_SAS_BROADCAST_PRIMITIVE *broadcast_event_data =
+                   (EVENT_DATA_SAS_BROADCAST_PRIMITIVE *)reply->Data;
+               if (broadcast_event_data->Primitive !=
+                   MPI_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT)
+                       return 0;
+               if (ioc->broadcast_aen_busy)
+                       return 0;
+               ioc->broadcast_aen_busy = 1;
+               break;
+       }
        case MPI_EVENT_SAS_DEVICE_STATUS_CHANGE:
-               mptsas_send_sas_event(ioc,
-                       (EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *)reply->Data);
+       {
+               EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data =
+                   (EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *)reply->Data;
+
+               if (sas_event_data->ReasonCode ==
+                   MPI_EVENT_SAS_DEV_STAT_RC_NOT_RESPONDING) {
+                       mptsas_target_reset_queue(ioc, sas_event_data);
+                       return 0;
+               }
                break;
-       case MPI_EVENT_INTEGRATED_RAID:
-               mptsas_send_raid_event(ioc,
-                       (EVENT_DATA_RAID *)reply->Data);
+       }
+       case MPI_EVENT_SAS_EXPANDER_STATUS_CHANGE:
+       {
+               MpiEventDataSasExpanderStatusChange_t *expander_data =
+                   (MpiEventDataSasExpanderStatusChange_t *)reply->Data;
+
+               if (ioc->old_sas_discovery_protocal)
+                       return 0;
+
+               if (expander_data->ReasonCode ==
+                   MPI_EVENT_SAS_EXP_RC_NOT_RESPONDING &&
+                   ioc->device_missing_delay)
+                       delay = HZ * ioc->device_missing_delay;
                break;
+       }
+       case MPI_EVENT_SAS_DISCOVERY:
+       {
+               u32 discovery_status;
+               EventDataSasDiscovery_t *discovery_data =
+                   (EventDataSasDiscovery_t *)reply->Data;
+
+               discovery_status = le32_to_cpu(discovery_data->DiscoveryStatus);
+               ioc->sas_discovery_quiesce_io = discovery_status ? 1 : 0;
+               if (ioc->old_sas_discovery_protocal && !discovery_status)
+                       mptsas_queue_rescan(ioc);
+               return 0;
+       }
+       case MPI_EVENT_INTEGRATED_RAID:
        case MPI_EVENT_PERSISTENT_TABLE_FULL:
-               INIT_WORK(&ioc->sas_persist_task,
-                   mptsas_persist_clear_table);
-               schedule_work(&ioc->sas_persist_task);
-               break;
-        case MPI_EVENT_SAS_DISCOVERY:
-               mptsas_send_discovery_event(ioc,
-                       (EVENT_DATA_SAS_DISCOVERY *)reply->Data);
-               break;
        case MPI_EVENT_IR2:
-               mptsas_send_ir2_event(ioc,
-                   (PTR_MPI_EVENT_DATA_IR2)reply->Data);
+       case MPI_EVENT_SAS_PHY_LINK_STATUS:
+       case MPI_EVENT_QUEUE_FULL:
                break;
        default:
-               rc = mptscsih_event_process(ioc, reply);
-               break;
+               return 0;
        }
- out:
 
-       return rc;
+       event_data_sz = ((reply->MsgLength * 4) -
+           offsetof(EventNotificationReply_t, Data));
+       sz = offsetof(struct fw_event_work, event_data) + event_data_sz;
+       fw_event = kzalloc(sz, GFP_ATOMIC);
+       if (!fw_event) {
+               printk(MYIOC_s_WARN_FMT "%s: failed at (line=%d)\n", ioc->name,
+                __func__, __LINE__);
+               return 0;
+       }
+       memcpy(fw_event->event_data, reply->Data, event_data_sz);
+       fw_event->event = event;
+       fw_event->ioc = ioc;
+       mptsas_add_fw_event(ioc, fw_event, delay);
+       return 0;
+}
+
+/* Delete a volume when no longer listed in ioc pg2
+ */
+static void mptsas_volume_delete(MPT_ADAPTER *ioc, u8 id)
+{
+       struct scsi_device *sdev;
+       int i;
+
+       sdev = scsi_device_lookup(ioc->sh, MPTSAS_RAID_CHANNEL, id, 0);
+       if (!sdev)
+               return;
+       if (!ioc->raid_data.pIocPg2)
+               goto out;
+       if (!ioc->raid_data.pIocPg2->NumActiveVolumes)
+               goto out;
+       for (i = 0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++)
+               if (ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID == id)
+                       goto release_sdev;
+ out:
+       printk(MYIOC_s_INFO_FMT "removing raid volume, channel %d, "
+           "id %d\n", ioc->name, MPTSAS_RAID_CHANNEL, id);
+       scsi_remove_device(sdev);
+ release_sdev:
+       scsi_device_put(sdev);
 }
 
 static int
@@ -3128,6 +4701,7 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                return r;
 
        ioc = pci_get_drvdata(pdev);
+       mptsas_fw_event_off(ioc);
        ioc->DoneCtx = mptsasDoneCtx;
        ioc->TaskCtx = mptsasTaskCtx;
        ioc->InternalCtx = mptsasInternalCtx;
@@ -3211,17 +4785,15 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
         * A slightly different algorithm is required for
         * 64bit SGEs.
         */
-       scale = ioc->req_sz/(sizeof(dma_addr_t) + sizeof(u32));
-       if (sizeof(dma_addr_t) == sizeof(u64)) {
+       scale = ioc->req_sz/ioc->SGE_size;
+       if (ioc->sg_addr_size == sizeof(u64)) {
                numSGE = (scale - 1) *
                  (ioc->facts.MaxChainDepth-1) + scale +
-                 (ioc->req_sz - 60) / (sizeof(dma_addr_t) +
-                 sizeof(u32));
+                 (ioc->req_sz - 60) / ioc->SGE_size;
        } else {
                numSGE = 1 + (scale - 1) *
                  (ioc->facts.MaxChainDepth-1) + scale +
-                 (ioc->req_sz - 64) / (sizeof(dma_addr_t) +
-                 sizeof(u32));
+                 (ioc->req_sz - 64) / ioc->SGE_size;
        }
 
        if (numSGE < sh->sg_tablesize) {
@@ -3251,9 +4823,6 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
        /* Clear the TM flags
         */
-       hd->tmPending = 0;
-       hd->tmState = TM_STATE_NONE;
-       hd->resetPending = 0;
        hd->abortSCpnt = NULL;
 
        /* Clear the pointer used to store
@@ -3273,10 +4842,11 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
        ioc->sas_data.ptClear = mpt_pt_clear;
 
-       init_waitqueue_head(&hd->scandv_waitq);
-       hd->scandv_wait_done = 0;
        hd->last_queue_full = 0;
        INIT_LIST_HEAD(&hd->target_reset_list);
+       INIT_LIST_HEAD(&ioc->sas_device_info_list);
+       mutex_init(&ioc->sas_device_info_mutex);
+
        spin_unlock_irqrestore(&ioc->FreeQlock, flags);
 
        if (ioc->sas_data.ptClear==1) {
@@ -3291,8 +4861,11 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                goto out_mptsas_probe;
        }
 
+       /* older firmware doesn't support expander events */
+       if ((ioc->facts.HeaderVersion >> 8) < 0xE)
+               ioc->old_sas_discovery_protocal = 1;
        mptsas_scan_sas_topology(ioc);
-
+       mptsas_fw_event_on(ioc);
        return 0;
 
  out_mptsas_probe:
@@ -3301,12 +4874,25 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        return error;
 }
 
+void
+mptsas_shutdown(struct pci_dev *pdev)
+{
+       MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
+
+       mptsas_fw_event_off(ioc);
+       mptsas_cleanup_fw_event_q(ioc);
+}
+
 static void __devexit mptsas_remove(struct pci_dev *pdev)
 {
        MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
        struct mptsas_portinfo *p, *n;
        int i;
 
+       mptsas_shutdown(pdev);
+
+       mptsas_del_device_components(ioc);
+
        ioc->sas_discovery_ignore_events = 1;
        sas_remove_host(ioc->sh);
 
@@ -3315,11 +4901,12 @@ static void __devexit mptsas_remove(struct pci_dev *pdev)
                list_del(&p->list);
                for (i = 0 ; i < p->num_phys ; i++)
                        mptsas_port_delete(ioc, p->phy_info[i].port_details);
+
                kfree(p->phy_info);
                kfree(p);
        }
        mutex_unlock(&ioc->sas_topology_mutex);
-
+       ioc->hba_port_info = NULL;
        mptscsih_remove(pdev);
 }
 
@@ -3344,7 +4931,7 @@ static struct pci_driver mptsas_driver = {
        .id_table       = mptsas_pci_table,
        .probe          = mptsas_probe,
        .remove         = __devexit_p(mptsas_remove),
-       .shutdown       = mptscsih_shutdown,
+       .shutdown       = mptsas_shutdown,
 #ifdef CONFIG_PM
        .suspend        = mptscsih_suspend,
        .resume         = mptscsih_resume,
@@ -3364,10 +4951,12 @@ mptsas_init(void)
                return -ENODEV;
 
        mptsasDoneCtx = mpt_register(mptscsih_io_done, MPTSAS_DRIVER);
-       mptsasTaskCtx = mpt_register(mptsas_taskmgmt_complete, MPTSAS_DRIVER);
+       mptsasTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTSAS_DRIVER);
        mptsasInternalCtx =
                mpt_register(mptscsih_scandv_complete, MPTSAS_DRIVER);
        mptsasMgmtCtx = mpt_register(mptsas_mgmt_done, MPTSAS_DRIVER);
+       mptsasDeviceResetCtx =
+               mpt_register(mptsas_taskmgmt_complete, MPTSAS_DRIVER);
 
        mpt_event_register(mptsasDoneCtx, mptsas_event_process);
        mpt_reset_register(mptsasDoneCtx, mptsas_ioc_reset);
@@ -3392,6 +4981,7 @@ mptsas_exit(void)
        mpt_deregister(mptsasInternalCtx);
        mpt_deregister(mptsasTaskCtx);
        mpt_deregister(mptsasDoneCtx);
+       mpt_deregister(mptsasDeviceResetCtx);
 }
 
 module_init(mptsas_init);
index 2b544e0877e6f0f3dd1ccc686edcfe2cd3e9a744..953c2bfcf6aaa470f05a0e3be18c966c05f3dc6b 100644 (file)
@@ -53,6 +53,7 @@ struct mptsas_target_reset_event {
        struct list_head        list;
        EVENT_DATA_SAS_DEVICE_STATUS_CHANGE sas_event_data;
        u8      target_reset_issued;
+       unsigned long    time_count;
 };
 
 enum mptsas_hotplug_action {
@@ -60,12 +61,37 @@ enum mptsas_hotplug_action {
        MPTSAS_DEL_DEVICE,
        MPTSAS_ADD_RAID,
        MPTSAS_DEL_RAID,
+       MPTSAS_ADD_PHYSDISK,
+       MPTSAS_ADD_PHYSDISK_REPROBE,
+       MPTSAS_DEL_PHYSDISK,
+       MPTSAS_DEL_PHYSDISK_REPROBE,
        MPTSAS_ADD_INACTIVE_VOLUME,
        MPTSAS_IGNORE_EVENT,
 };
 
+struct mptsas_mapping{
+       u8                      id;
+       u8                      channel;
+};
+
+struct mptsas_device_info {
+       struct list_head        list;
+       struct mptsas_mapping   os;     /* operating system mapping*/
+       struct mptsas_mapping   fw;     /* firmware mapping */
+       u64                     sas_address;
+       u32                     device_info; /* specific bits for devices */
+       u16                     slot;           /* enclosure slot id */
+       u64                     enclosure_logical_id; /*enclosure address */
+       u8                      is_logical_volume; /* is this logical volume */
+       /* this belongs to volume */
+       u8                      is_hidden_raid_component;
+       /* this valid when is_hidden_raid_component set */
+       u8                      volume_id;
+       /* cached data for a removed device */
+       u8                      is_cached;
+};
+
 struct mptsas_hotplug_event {
-       struct work_struct      work;
        MPT_ADAPTER             *ioc;
        enum mptsas_hotplug_action event_type;
        u64                     sas_address;
@@ -73,11 +99,18 @@ struct mptsas_hotplug_event {
        u8                      id;
        u32                     device_info;
        u16                     handle;
-       u16                     parent_handle;
        u8                      phy_id;
-       u8                      phys_disk_num_valid;    /* hrc (hidden raid component) */
        u8                      phys_disk_num;          /* hrc - unique index*/
-       u8                      hidden_raid_component;  /* hrc - don't expose*/
+       struct scsi_device      *sdev;
+};
+
+struct fw_event_work {
+       struct list_head        list;
+       struct delayed_work      work;
+       MPT_ADAPTER     *ioc;
+       u32                     event;
+       u8                      retries;
+       u8                      event_data[1];
 };
 
 struct mptsas_discovery_event {
index e62c6bc4ad33ec83407a002a1549a5edd49cdc90..024e8305bcf20b8842defd7decd3f423effc3c3c 100644 (file)
@@ -80,7 +80,7 @@ MODULE_VERSION(my_VERSION);
 /*
  *  Other private/forward protos...
  */
-static struct scsi_cmnd * mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i);
+struct scsi_cmnd       *mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i);
 static struct scsi_cmnd * mptscsih_getclear_scsi_lookup(MPT_ADAPTER *ioc, int i);
 static void    mptscsih_set_scsi_lookup(MPT_ADAPTER *ioc, int i, struct scsi_cmnd *scmd);
 static int     SCPNT_TO_LOOKUP_IDX(MPT_ADAPTER *ioc, struct scsi_cmnd *scmd);
@@ -92,18 +92,24 @@ static int  mptscsih_AddSGE(MPT_ADAPTER *ioc, struct scsi_cmnd *SCpnt,
                                 SCSIIORequest_t *pReq, int req_idx);
 static void    mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx);
 static void    mptscsih_copy_sense_data(struct scsi_cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply);
-static int     mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd);
-static int     mptscsih_tm_wait_for_completion(MPT_SCSI_HOST * hd, ulong timeout );
 
-static int     mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, int ctx2abort, ulong timeout);
+int    mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id,
+               int lun, int ctx2abort, ulong timeout);
 
 int            mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset);
 int            mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply);
 
+void
+mptscsih_taskmgmt_response_code(MPT_ADAPTER *ioc, u8 response_code);
+static int     mptscsih_get_completion_code(MPT_ADAPTER *ioc,
+               MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply);
 int            mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
 static int     mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *iocmd);
 static void    mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, VirtDevice *vdevice);
 
+static int
+mptscsih_taskmgmt_reply(MPT_ADAPTER *ioc, u8 type,
+                               SCSITaskMgmtReply_t *pScsiTmReply);
 void           mptscsih_remove(struct pci_dev *);
 void           mptscsih_shutdown(struct pci_dev *);
 #ifdef CONFIG_PM
@@ -113,69 +119,6 @@ int                mptscsih_resume(struct pci_dev *pdev);
 
 #define SNS_LEN(scp)   SCSI_SENSE_BUFFERSIZE
 
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- *     mptscsih_add_sge - Place a simple SGE at address pAddr.
- *     @pAddr: virtual address for SGE
- *     @flagslength: SGE flags and data transfer length
- *     @dma_addr: Physical address
- *
- *     This routine places a MPT request frame back on the MPT adapter's
- *     FreeQ.
- */
-static inline void
-mptscsih_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr)
-{
-       if (sizeof(dma_addr_t) == sizeof(u64)) {
-               SGESimple64_t *pSge = (SGESimple64_t *) pAddr;
-               u32 tmp = dma_addr & 0xFFFFFFFF;
-
-               pSge->FlagsLength = cpu_to_le32(flagslength);
-               pSge->Address.Low = cpu_to_le32(tmp);
-               tmp = (u32) ((u64)dma_addr >> 32);
-               pSge->Address.High = cpu_to_le32(tmp);
-
-       } else {
-               SGESimple32_t *pSge = (SGESimple32_t *) pAddr;
-               pSge->FlagsLength = cpu_to_le32(flagslength);
-               pSge->Address = cpu_to_le32(dma_addr);
-       }
-} /* mptscsih_add_sge() */
-
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- *     mptscsih_add_chain - Place a chain SGE at address pAddr.
- *     @pAddr: virtual address for SGE
- *     @next: nextChainOffset value (u32's)
- *     @length: length of next SGL segment
- *     @dma_addr: Physical address
- *
- *     This routine places a MPT request frame back on the MPT adapter's
- *     FreeQ.
- */
-static inline void
-mptscsih_add_chain(char *pAddr, u8 next, u16 length, dma_addr_t dma_addr)
-{
-       if (sizeof(dma_addr_t) == sizeof(u64)) {
-               SGEChain64_t *pChain = (SGEChain64_t *) pAddr;
-               u32 tmp = dma_addr & 0xFFFFFFFF;
-
-               pChain->Length = cpu_to_le16(length);
-               pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();
-
-               pChain->NextChainOffset = next;
-
-               pChain->Address.Low = cpu_to_le32(tmp);
-               tmp = (u32) ((u64)dma_addr >> 32);
-               pChain->Address.High = cpu_to_le32(tmp);
-       } else {
-               SGEChain32_t *pChain = (SGEChain32_t *) pAddr;
-               pChain->Length = cpu_to_le16(length);
-               pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();
-               pChain->NextChainOffset = next;
-               pChain->Address = cpu_to_le32(dma_addr);
-       }
-} /* mptscsih_add_chain() */
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /*
@@ -281,10 +224,10 @@ mptscsih_AddSGE(MPT_ADAPTER *ioc, struct scsi_cmnd *SCpnt,
         */
 
 nextSGEset:
-       numSgeSlots = ((frm_sz - sgeOffset) / (sizeof(u32) + sizeof(dma_addr_t)) );
+       numSgeSlots = ((frm_sz - sgeOffset) / ioc->SGE_size);
        numSgeThisFrame = (sges_left < numSgeSlots) ? sges_left : numSgeSlots;
 
-       sgflags = MPT_SGE_FLAGS_SIMPLE_ELEMENT | MPT_SGE_FLAGS_ADDRESSING | sgdir;
+       sgflags = MPT_SGE_FLAGS_SIMPLE_ELEMENT | sgdir;
 
        /* Get first (num - 1) SG elements
         * Skip any SG entries with a length of 0
@@ -293,17 +236,19 @@ nextSGEset:
        for (ii=0; ii < (numSgeThisFrame-1); ii++) {
                thisxfer = sg_dma_len(sg);
                if (thisxfer == 0) {
-                       sg = sg_next(sg); /* Get next SG element from the OS */
+                       /* Get next SG element from the OS */
+                       sg = sg_next(sg);
                        sg_done++;
                        continue;
                }
 
                v2 = sg_dma_address(sg);
-               mptscsih_add_sge(psge, sgflags | thisxfer, v2);
+               ioc->add_sge(psge, sgflags | thisxfer, v2);
 
-               sg = sg_next(sg);       /* Get next SG element from the OS */
-               psge += (sizeof(u32) + sizeof(dma_addr_t));
-               sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
+               /* Get next SG element from the OS */
+               sg = sg_next(sg);
+               psge += ioc->SGE_size;
+               sgeOffset += ioc->SGE_size;
                sg_done++;
        }
 
@@ -320,12 +265,8 @@ nextSGEset:
                thisxfer = sg_dma_len(sg);
 
                v2 = sg_dma_address(sg);
-               mptscsih_add_sge(psge, sgflags | thisxfer, v2);
-               /*
-               sg = sg_next(sg);
-               psge += (sizeof(u32) + sizeof(dma_addr_t));
-               */
-               sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
+               ioc->add_sge(psge, sgflags | thisxfer, v2);
+               sgeOffset += ioc->SGE_size;
                sg_done++;
 
                if (chainSge) {
@@ -334,7 +275,8 @@ nextSGEset:
                         * Update the chain element
                         * Offset and Length fields.
                         */
-                       mptscsih_add_chain((char *)chainSge, 0, sgeOffset, ioc->ChainBufferDMA + chain_dma_off);
+                       ioc->add_chain((char *)chainSge, 0, sgeOffset,
+                               ioc->ChainBufferDMA + chain_dma_off);
                } else {
                        /* The current buffer is the original MF
                         * and there is no Chain buffer.
@@ -367,7 +309,7 @@ nextSGEset:
                 * set properly).
                 */
                if (sg_done) {
-                       u32 *ptmp = (u32 *) (psge - (sizeof(u32) + sizeof(dma_addr_t)));
+                       u32 *ptmp = (u32 *) (psge - ioc->SGE_size);
                        sgflags = le32_to_cpu(*ptmp);
                        sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT;
                        *ptmp = cpu_to_le32(sgflags);
@@ -381,8 +323,9 @@ nextSGEset:
                         * Old chain element is now complete.
                         */
                        u8 nextChain = (u8) (sgeOffset >> 2);
-                       sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
-                       mptscsih_add_chain((char *)chainSge, nextChain, sgeOffset, ioc->ChainBufferDMA + chain_dma_off);
+                       sgeOffset += ioc->SGE_size;
+                       ioc->add_chain((char *)chainSge, nextChain, sgeOffset,
+                                        ioc->ChainBufferDMA + chain_dma_off);
                } else {
                        /* The original MF buffer requires a chain buffer -
                         * set the offset.
@@ -592,14 +535,15 @@ mptscsih_info_scsiio(MPT_ADAPTER *ioc, struct scsi_cmnd *sc, SCSIIOReply_t * pSc
        }
 
        scsi_print_command(sc);
-       printk(MYIOC_s_DEBUG_FMT "\tfw_channel = %d, fw_id = %d\n",
-           ioc->name, pScsiReply->Bus, pScsiReply->TargetID);
+       printk(MYIOC_s_DEBUG_FMT "\tfw_channel = %d, fw_id = %d, lun = %d\n",
+           ioc->name, pScsiReply->Bus, pScsiReply->TargetID, sc->device->lun);
        printk(MYIOC_s_DEBUG_FMT "\trequest_len = %d, underflow = %d, "
            "resid = %d\n", ioc->name, scsi_bufflen(sc), sc->underflow,
            scsi_get_resid(sc));
        printk(MYIOC_s_DEBUG_FMT "\ttag = %d, transfer_count = %d, "
            "sc->result = %08X\n", ioc->name, le16_to_cpu(pScsiReply->TaskTag),
            le32_to_cpu(pScsiReply->TransferCount), sc->result);
+
        printk(MYIOC_s_DEBUG_FMT "\tiocstatus = %s (0x%04x), "
            "scsi_status = %s (0x%02x), scsi_state = (0x%02x)\n",
            ioc->name, desc, ioc_status, desc1, pScsiReply->SCSIStatus,
@@ -654,16 +598,14 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
        req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
        req_idx_MR = (mr != NULL) ?
            le16_to_cpu(mr->u.frame.hwhdr.msgctxu.fld.req_idx) : req_idx;
+
+       /* Special case, where already freed message frame is received from
+        * Firmware. It happens with Resetting IOC.
+        * Return immediately. Do not care
+        */
        if ((req_idx != req_idx_MR) ||
-           (mf->u.frame.linkage.arg1 == 0xdeadbeaf)) {
-               printk(MYIOC_s_ERR_FMT "Received a mf that was already freed\n",
-                   ioc->name);
-               printk (MYIOC_s_ERR_FMT
-                   "req_idx=%x req_idx_MR=%x mf=%p mr=%p sc=%p\n",
-                   ioc->name, req_idx, req_idx_MR, mf, mr,
-                   mptscsih_get_scsi_lookup(ioc, req_idx_MR));
+           (le32_to_cpu(mf->u.frame.linkage.arg1) == 0xdeadbeaf))
                return 0;
-       }
 
        sc = mptscsih_getclear_scsi_lookup(ioc, req_idx);
        if (sc == NULL) {
@@ -810,12 +752,16 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
                         */
 
                case MPI_IOCSTATUS_SCSI_TASK_TERMINATED:        /* 0x0048 */
-               case MPI_IOCSTATUS_SCSI_EXT_TERMINATED:         /* 0x004C */
                        /* Linux handles an unsolicited DID_RESET better
                         * than an unsolicited DID_ABORT.
                         */
                        sc->result = DID_RESET << 16;
 
+               case MPI_IOCSTATUS_SCSI_EXT_TERMINATED:         /* 0x004C */
+                       if (ioc->bus_type == FC)
+                               sc->result = DID_ERROR << 16;
+                       else
+                               sc->result = DID_RESET << 16;
                        break;
 
                case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:      /* 0x0049 */
@@ -992,9 +938,9 @@ mptscsih_flush_running_cmds(MPT_SCSI_HOST *hd)
                scsi_dma_unmap(sc);
                sc->result = DID_RESET << 16;
                sc->host_scribble = NULL;
-               sdev_printk(KERN_INFO, sc->device, MYIOC_s_FMT
-                   "completing cmds: fw_channel %d, fw_id %d, sc=%p,"
-                   " mf = %p, idx=%x\n", ioc->name, channel, id, sc, mf, ii);
+               dtmprintk(ioc, sdev_printk(KERN_INFO, sc->device, MYIOC_s_FMT
+                   "completing cmds: fw_channel %d, fw_id %d, sc=%p, mf = %p, "
+                   "idx=%x\n", ioc->name, channel, id, sc, mf, ii));
                sc->scsi_done(sc);
        }
 }
@@ -1053,9 +999,11 @@ mptscsih_search_running_cmds(MPT_SCSI_HOST *hd, VirtDevice *vdevice)
                        scsi_dma_unmap(sc);
                        sc->host_scribble = NULL;
                        sc->result = DID_NO_CONNECT << 16;
-                       sdev_printk(KERN_INFO, sc->device, MYIOC_s_FMT "completing cmds: fw_channel %d,"
-                          "fw_id %d, sc=%p, mf = %p, idx=%x\n", ioc->name, vdevice->vtarget->channel,
-                          vdevice->vtarget->id, sc, mf, ii);
+                       dtmprintk(ioc, sdev_printk(KERN_INFO, sc->device,
+                          MYIOC_s_FMT "completing cmds: fw_channel %d, "
+                          "fw_id %d, sc=%p, mf = %p, idx=%x\n", ioc->name,
+                          vdevice->vtarget->channel, vdevice->vtarget->id,
+                          sc, mf, ii));
                        sc->scsi_done(sc);
                        spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
                }
@@ -1346,7 +1294,6 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
        MPT_FRAME_HDR           *mf;
        SCSIIORequest_t         *pScsiReq;
        VirtDevice              *vdevice = SCpnt->device->hostdata;
-       int      lun;
        u32      datalen;
        u32      scsictl;
        u32      scsidir;
@@ -1357,13 +1304,12 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
 
        hd = shost_priv(SCpnt->device->host);
        ioc = hd->ioc;
-       lun = SCpnt->device->lun;
        SCpnt->scsi_done = done;
 
        dmfprintk(ioc, printk(MYIOC_s_DEBUG_FMT "qcmd: SCpnt=%p, done()=%p\n",
                ioc->name, SCpnt, done));
 
-       if (hd->resetPending) {
+       if (ioc->taskmgmt_quiesce_io) {
                dtmprintk(ioc, printk(MYIOC_s_WARN_FMT "qcmd: SCpnt=%p timeout + 60HZ\n",
                        ioc->name, SCpnt));
                return SCSI_MLQUEUE_HOST_BUSY;
@@ -1422,7 +1368,7 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
        pScsiReq->CDBLength = SCpnt->cmd_len;
        pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE;
        pScsiReq->Reserved = 0;
-       pScsiReq->MsgFlags = mpt_msg_flags();
+       pScsiReq->MsgFlags = mpt_msg_flags(ioc);
        int_to_scsilun(SCpnt->device->lun, (struct scsi_lun *)pScsiReq->LUN);
        pScsiReq->Control = cpu_to_le32(scsictl);
 
@@ -1448,7 +1394,8 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
         */
        if (datalen == 0) {
                /* Add a NULL SGE */
-               mptscsih_add_sge((char *)&pScsiReq->SGL, MPT_SGE_FLAGS_SSIMPLE_READ | 0,
+               ioc->add_sge((char *)&pScsiReq->SGL,
+                       MPT_SGE_FLAGS_SSIMPLE_READ | 0,
                        (dma_addr_t) -1);
        } else {
                /* Add a 32 or 64 bit SGE */
@@ -1528,8 +1475,8 @@ mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx)
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /**
- *     mptscsih_TMHandler - Generic handler for SCSI Task Management.
- *     @hd: Pointer to MPT SCSI HOST structure
+ *     mptscsih_IssueTaskMgmt - Generic send Task Management function.
+ *     @hd: Pointer to MPT_SCSI_HOST structure
  *     @type: Task Management type
  *     @channel: channel number for task management
  *     @id: Logical Target ID for reset (if appropriate)
@@ -1537,145 +1484,68 @@ mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx)
  *     @ctx2abort: Context for the task to be aborted (if appropriate)
  *     @timeout: timeout for task management control
  *
- *     Fall through to mpt_HardResetHandler if: not operational, too many
- *     failed TM requests or handshake failure.
+ *     Remark: _HardResetHandler can be invoked from an interrupt thread (timer)
+ *     or a non-interrupt thread.  In the former, must not call schedule().
  *
- *     Remark: Currently invoked from a non-interrupt thread (_bh).
+ *     Not all fields are meaningfull for all task types.
  *
- *     Note: With old EH code, at most 1 SCSI TaskMgmt function per IOC
- *     will be active.
+ *     Returns 0 for SUCCESS, or FAILED.
  *
- *     Returns 0 for SUCCESS, or %FAILED.
  **/
 int
-mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, int ctx2abort, ulong timeout)
+mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun,
+       int ctx2abort, ulong timeout)
 {
-       MPT_ADAPTER     *ioc;
-       int              rc = -1;
+       MPT_FRAME_HDR   *mf;
+       SCSITaskMgmt_t  *pScsiTm;
+       int              ii;
+       int              retval;
+       MPT_ADAPTER     *ioc = hd->ioc;
+       unsigned long    timeleft;
+       u8               issue_hard_reset;
        u32              ioc_raw_state;
-       unsigned long    flags;
-
-       ioc = hd->ioc;
-       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TMHandler Entered!\n", ioc->name));
-
-       // SJR - CHECKME - Can we avoid this here?
-       // (mpt_HardResetHandler has this check...)
-       spin_lock_irqsave(&ioc->diagLock, flags);
-       if ((ioc->diagPending) || (ioc->alt_ioc && ioc->alt_ioc->diagPending)) {
-               spin_unlock_irqrestore(&ioc->diagLock, flags);
-               return FAILED;
-       }
-       spin_unlock_irqrestore(&ioc->diagLock, flags);
-
-       /*  Wait a fixed amount of time for the TM pending flag to be cleared.
-        *  If we time out and not bus reset, then we return a FAILED status
-        *  to the caller.
-        *  The call to mptscsih_tm_pending_wait() will set the pending flag
-        *  if we are
-        *  successful. Otherwise, reload the FW.
-        */
-       if (mptscsih_tm_pending_wait(hd) == FAILED) {
-               if (type == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
-                       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TMHandler abort: "
-                          "Timed out waiting for last TM (%d) to complete! \n",
-                          ioc->name, hd->tmPending));
-                       return FAILED;
-               } else if (type == MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET) {
-                       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TMHandler target "
-                               "reset: Timed out waiting for last TM (%d) "
-                               "to complete! \n", ioc->name,
-                               hd->tmPending));
-                       return FAILED;
-               } else if (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) {
-                       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TMHandler bus reset: "
-                          "Timed out waiting for last TM (%d) to complete! \n",
-                         ioc->name, hd->tmPending));
-                       return FAILED;
-               }
-       } else {
-               spin_lock_irqsave(&ioc->FreeQlock, flags);
-               hd->tmPending |=  (1 << type);
-               spin_unlock_irqrestore(&ioc->FreeQlock, flags);
-       }
+       unsigned long    time_count;
 
+       issue_hard_reset = 0;
        ioc_raw_state = mpt_GetIocState(ioc, 0);
 
        if ((ioc_raw_state & MPI_IOC_STATE_MASK) != MPI_IOC_STATE_OPERATIONAL) {
                printk(MYIOC_s_WARN_FMT
-                       "TM Handler for type=%x: IOC Not operational (0x%x)!\n",
+                       "TaskMgmt type=%x: IOC Not operational (0x%x)!\n",
                        ioc->name, type, ioc_raw_state);
-               printk(MYIOC_s_WARN_FMT " Issuing HardReset!!\n", ioc->name);
+               printk(MYIOC_s_WARN_FMT "Issuing HardReset from %s!!\n",
+                   ioc->name, __func__);
                if (mpt_HardResetHandler(ioc, CAN_SLEEP) < 0)
-                       printk(MYIOC_s_WARN_FMT "TMHandler: HardReset "
+                       printk(MYIOC_s_WARN_FMT "TaskMgmt HardReset "
                            "FAILED!!\n", ioc->name);
-               return FAILED;
+               return 0;
        }
 
        if (ioc_raw_state & MPI_DOORBELL_ACTIVE) {
                printk(MYIOC_s_WARN_FMT
-                       "TM Handler for type=%x: ioc_state: "
+                       "TaskMgmt type=%x: ioc_state: "
                        "DOORBELL_ACTIVE (0x%x)!\n",
                        ioc->name, type, ioc_raw_state);
                return FAILED;
        }
 
-       /* Isse the Task Mgmt request.
-        */
-       if (hd->hard_resets < -1)
-               hd->hard_resets++;
-
-       rc = mptscsih_IssueTaskMgmt(hd, type, channel, id, lun,
-           ctx2abort, timeout);
-       if (rc)
-               printk(MYIOC_s_INFO_FMT "Issue of TaskMgmt failed!\n",
-                      ioc->name);
-       else
-               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Issue of TaskMgmt Successful!\n",
-                          ioc->name));
-
-       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
-                       "TMHandler rc = %d!\n", ioc->name, rc));
-
-       return rc;
-}
-
-
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- *     mptscsih_IssueTaskMgmt - Generic send Task Management function.
- *     @hd: Pointer to MPT_SCSI_HOST structure
- *     @type: Task Management type
- *     @channel: channel number for task management
- *     @id: Logical Target ID for reset (if appropriate)
- *     @lun: Logical Unit for reset (if appropriate)
- *     @ctx2abort: Context for the task to be aborted (if appropriate)
- *     @timeout: timeout for task management control
- *
- *     Remark: _HardResetHandler can be invoked from an interrupt thread (timer)
- *     or a non-interrupt thread.  In the former, must not call schedule().
- *
- *     Not all fields are meaningfull for all task types.
- *
- *     Returns 0 for SUCCESS, or FAILED.
- *
- **/
-static int
-mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, int ctx2abort, ulong timeout)
-{
-       MPT_FRAME_HDR   *mf;
-       SCSITaskMgmt_t  *pScsiTm;
-       int              ii;
-       int              retval;
-       MPT_ADAPTER     *ioc = hd->ioc;
+       mutex_lock(&ioc->taskmgmt_cmds.mutex);
+       if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0) {
+               mf = NULL;
+               retval = FAILED;
+               goto out;
+       }
 
        /* Return Fail to calling function if no message frames available.
         */
        if ((mf = mpt_get_msg_frame(ioc->TaskCtx, ioc)) == NULL) {
-               dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "IssueTaskMgmt, no msg frames!!\n",
-                               ioc->name));
-               return FAILED;
+               dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+                       "TaskMgmt no msg frames!!\n", ioc->name));
+               retval = FAILED;
+               mpt_clear_taskmgmt_in_progress_flag(ioc);
+               goto out;
        }
-       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "IssueTaskMgmt request @ %p\n",
+       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt request (mf=%p)\n",
                        ioc->name, mf));
 
        /* Format the Request
@@ -1699,11 +1569,14 @@ mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, i
 
        pScsiTm->TaskMsgContext = ctx2abort;
 
-       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "IssueTaskMgmt: ctx2abort (0x%08x) "
-               "type=%d\n", ioc->name, ctx2abort, type));
+       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt: ctx2abort (0x%08x) "
+               "task_type = 0x%02X, timeout = %ld\n", ioc->name, ctx2abort,
+               type, timeout));
 
        DBG_DUMP_TM_REQUEST_FRAME(ioc, (u32 *)pScsiTm);
 
+       INITIALIZE_MGMT_STATUS(ioc->taskmgmt_cmds.status)
+       time_count = jiffies;
        if ((ioc->facts.IOCCapabilities & MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q) &&
            (ioc->facts.MsgVersion >= MPI_VERSION_01_05))
                mpt_put_msg_frame_hi_pri(ioc->TaskCtx, ioc, mf);
@@ -1711,47 +1584,50 @@ mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, i
                retval = mpt_send_handshake_request(ioc->TaskCtx, ioc,
                        sizeof(SCSITaskMgmt_t), (u32*)pScsiTm, CAN_SLEEP);
                if (retval) {
-                       dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "send_handshake FAILED!"
-                       " (hd %p, ioc %p, mf %p, rc=%d) \n", ioc->name, hd,
-                       ioc, mf, retval));
-                       goto fail_out;
+                       dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
+                               "TaskMgmt handshake FAILED!(mf=%p, rc=%d) \n",
+                               ioc->name, mf, retval));
+                       mpt_free_msg_frame(ioc, mf);
+                       mpt_clear_taskmgmt_in_progress_flag(ioc);
+                       goto out;
                }
        }
 
-       if(mptscsih_tm_wait_for_completion(hd, timeout) == FAILED) {
-               dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "task management request TIMED OUT!"
-                       " (hd %p, ioc %p, mf %p) \n", ioc->name, hd,
-                       ioc, mf));
-               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Calling HardReset! \n",
-                        ioc->name));
-               retval = mpt_HardResetHandler(ioc, CAN_SLEEP);
-               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "rc=%d \n",
-                        ioc->name, retval));
-               goto fail_out;
+       timeleft = wait_for_completion_timeout(&ioc->taskmgmt_cmds.done,
+               timeout*HZ);
+       if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+               retval = FAILED;
+               dtmprintk(ioc, printk(MYIOC_s_ERR_FMT
+                   "TaskMgmt TIMED OUT!(mf=%p)\n", ioc->name, mf));
+               mpt_clear_taskmgmt_in_progress_flag(ioc);
+               if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET)
+                       goto out;
+               issue_hard_reset = 1;
+               goto out;
        }
 
-       /*
-        * Handle success case, see if theres a non-zero ioc_status.
-        */
-       if (hd->tm_iocstatus == MPI_IOCSTATUS_SUCCESS ||
-          hd->tm_iocstatus == MPI_IOCSTATUS_SCSI_TASK_TERMINATED ||
-          hd->tm_iocstatus == MPI_IOCSTATUS_SCSI_IOC_TERMINATED)
-               retval = 0;
-       else
-               retval = FAILED;
+       retval = mptscsih_taskmgmt_reply(ioc, type,
+           (SCSITaskMgmtReply_t *) ioc->taskmgmt_cmds.reply);
 
-       return retval;
+       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+           "TaskMgmt completed (%d seconds)\n",
+           ioc->name, jiffies_to_msecs(jiffies - time_count)/1000));
 
fail_out:
+ out:
 
-       /*
-        * Free task management mf, and corresponding tm flags
-        */
-       mpt_free_msg_frame(ioc, mf);
-       hd->tmPending = 0;
-       hd->tmState = TM_STATE_NONE;
-       return FAILED;
+       CLEAR_MGMT_STATUS(ioc->taskmgmt_cmds.status)
+       if (issue_hard_reset) {
+               printk(MYIOC_s_WARN_FMT "Issuing Reset from %s!!\n",
+                       ioc->name, __func__);
+               retval = mpt_HardResetHandler(ioc, CAN_SLEEP);
+               mpt_free_msg_frame(ioc, mf);
+       }
+
+       retval = (retval == 0) ? 0 : FAILED;
+       mutex_unlock(&ioc->taskmgmt_cmds.mutex);
+       return retval;
 }
+EXPORT_SYMBOL(mptscsih_IssueTaskMgmt);
 
 static int
 mptscsih_get_tm_timeout(MPT_ADAPTER *ioc)
@@ -1838,13 +1714,8 @@ mptscsih_abort(struct scsi_cmnd * SCpnt)
                goto out;
        }
 
-       if (hd->resetPending) {
-               retval = FAILED;
-               goto out;
-       }
-
-       if (hd->timeouts < -1)
-               hd->timeouts++;
+       if (ioc->timeouts < -1)
+               ioc->timeouts++;
 
        if (mpt_fwfault_debug)
                mpt_halt_firmware(ioc);
@@ -1861,22 +1732,30 @@ mptscsih_abort(struct scsi_cmnd * SCpnt)
 
        hd->abortSCpnt = SCpnt;
 
-       retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
-           vdevice->vtarget->channel, vdevice->vtarget->id, vdevice->lun,
-           ctx2abort, mptscsih_get_tm_timeout(ioc));
+       retval = mptscsih_IssueTaskMgmt(hd,
+                        MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
+                        vdevice->vtarget->channel,
+                        vdevice->vtarget->id, vdevice->lun,
+                        ctx2abort, mptscsih_get_tm_timeout(ioc));
 
        if (SCPNT_TO_LOOKUP_IDX(ioc, SCpnt) == scpnt_idx &&
-           SCpnt->serial_number == sn)
+           SCpnt->serial_number == sn) {
+               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "task abort: command still in active list! (sc=%p)\n",
+                   ioc->name, SCpnt));
                retval = FAILED;
+       } else {
+               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "task abort: command cleared from active list! (sc=%p)\n",
+                   ioc->name, SCpnt));
+               retval = SUCCESS;
+       }
 
  out:
        printk(MYIOC_s_INFO_FMT "task abort: %s (sc=%p)\n",
-           ioc->name, ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt);
+           ioc->name, ((retval == SUCCESS) ? "SUCCESS" : "FAILED"), SCpnt);
 
-       if (retval == 0)
-               return SUCCESS;
-       else
-               return FAILED;
+       return retval;
 }
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -1909,14 +1788,9 @@ mptscsih_dev_reset(struct scsi_cmnd * SCpnt)
               ioc->name, SCpnt);
        scsi_print_command(SCpnt);
 
-       if (hd->resetPending) {
-               retval = FAILED;
-               goto out;
-       }
-
        vdevice = SCpnt->device->hostdata;
        if (!vdevice || !vdevice->vtarget) {
-               retval = 0;
+               retval = SUCCESS;
                goto out;
        }
 
@@ -1927,9 +1801,11 @@ mptscsih_dev_reset(struct scsi_cmnd * SCpnt)
                goto out;
        }
 
-       retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET,
-           vdevice->vtarget->channel, vdevice->vtarget->id, 0, 0,
-           mptscsih_get_tm_timeout(ioc));
+       retval = mptscsih_IssueTaskMgmt(hd,
+                               MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET,
+                               vdevice->vtarget->channel,
+                               vdevice->vtarget->id, 0, 0,
+                               mptscsih_get_tm_timeout(ioc));
 
  out:
        printk (MYIOC_s_INFO_FMT "target reset: %s (sc=%p)\n",
@@ -1972,12 +1848,16 @@ mptscsih_bus_reset(struct scsi_cmnd * SCpnt)
               ioc->name, SCpnt);
        scsi_print_command(SCpnt);
 
-       if (hd->timeouts < -1)
-               hd->timeouts++;
+       if (ioc->timeouts < -1)
+               ioc->timeouts++;
 
        vdevice = SCpnt->device->hostdata;
-       retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS,
-           vdevice->vtarget->channel, 0, 0, 0, mptscsih_get_tm_timeout(ioc));
+       if (!vdevice || !vdevice->vtarget)
+               return SUCCESS;
+       retval = mptscsih_IssueTaskMgmt(hd,
+                                       MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS,
+                                       vdevice->vtarget->channel, 0, 0, 0,
+                                       mptscsih_get_tm_timeout(ioc));
 
        printk(MYIOC_s_INFO_FMT "bus reset: %s (sc=%p)\n",
            ioc->name, ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt);
@@ -2001,8 +1881,9 @@ int
 mptscsih_host_reset(struct scsi_cmnd *SCpnt)
 {
        MPT_SCSI_HOST *  hd;
-       int              retval;
+       int              status = SUCCESS;
        MPT_ADAPTER     *ioc;
+       int             retval;
 
        /*  If we can't locate the host to reset, then we failed. */
        if ((hd = shost_priv(SCpnt->device->host)) == NULL){
@@ -2021,86 +1902,71 @@ mptscsih_host_reset(struct scsi_cmnd *SCpnt)
        /*  If our attempts to reset the host failed, then return a failed
         *  status.  The host will be taken off line by the SCSI mid-layer.
         */
-       if (mpt_HardResetHandler(ioc, CAN_SLEEP) < 0) {
-               retval = FAILED;
-       } else {
-               /*  Make sure TM pending is cleared and TM state is set to
-                *  NONE.
-                */
-               retval = 0;
-               hd->tmPending = 0;
-               hd->tmState = TM_STATE_NONE;
-       }
+    retval = mpt_HardResetHandler(ioc, CAN_SLEEP);
+       if (retval < 0)
+               status = FAILED;
+       else
+               status = SUCCESS;
 
        printk(MYIOC_s_INFO_FMT "host reset: %s (sc=%p)\n",
            ioc->name, ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt);
 
-       return retval;
+       return status;
 }
 
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- *     mptscsih_tm_pending_wait - wait for pending task management request to complete
- *     @hd: Pointer to MPT host structure.
- *
- *     Returns {SUCCESS,FAILED}.
- */
 static int
-mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd)
+mptscsih_taskmgmt_reply(MPT_ADAPTER *ioc, u8 type,
+       SCSITaskMgmtReply_t *pScsiTmReply)
 {
-       unsigned long  flags;
-       int            loop_count = 4 * 10;  /* Wait 10 seconds */
-       int            status = FAILED;
-       MPT_ADAPTER     *ioc = hd->ioc;
+       u16                      iocstatus;
+       u32                      termination_count;
+       int                      retval;
 
-       do {
-               spin_lock_irqsave(&ioc->FreeQlock, flags);
-               if (hd->tmState == TM_STATE_NONE) {
-                       hd->tmState = TM_STATE_IN_PROGRESS;
-                       hd->tmPending = 1;
-                       spin_unlock_irqrestore(&ioc->FreeQlock, flags);
-                       status = SUCCESS;
-                       break;
-               }
-               spin_unlock_irqrestore(&ioc->FreeQlock, flags);
-               msleep(250);
-       } while (--loop_count);
+       if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_RF_VALID)) {
+               retval = FAILED;
+               goto out;
+       }
 
-       return status;
-}
+       DBG_DUMP_TM_REPLY_FRAME(ioc, (u32 *)pScsiTmReply);
 
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- *     mptscsih_tm_wait_for_completion - wait for completion of TM task
- *     @hd: Pointer to MPT host structure.
- *     @timeout: timeout value
- *
- *     Returns {SUCCESS,FAILED}.
- */
-static int
-mptscsih_tm_wait_for_completion(MPT_SCSI_HOST * hd, ulong timeout )
-{
-       unsigned long  flags;
-       int            loop_count = 4 * timeout;
-       int            status = FAILED;
-       MPT_ADAPTER     *ioc = hd->ioc;
+       iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK;
+       termination_count = le32_to_cpu(pScsiTmReply->TerminationCount);
 
-       do {
-               spin_lock_irqsave(&ioc->FreeQlock, flags);
-               if(hd->tmPending == 0) {
-                       status = SUCCESS;
-                       spin_unlock_irqrestore(&ioc->FreeQlock, flags);
-                       break;
-               }
-               spin_unlock_irqrestore(&ioc->FreeQlock, flags);
-               msleep(250);
-       } while (--loop_count);
+       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+           "TaskMgmt fw_channel = %d, fw_id = %d, task_type = 0x%02X,\n"
+           "\tiocstatus = 0x%04X, loginfo = 0x%08X, response_code = 0x%02X,\n"
+           "\tterm_cmnds = %d\n", ioc->name, pScsiTmReply->Bus,
+           pScsiTmReply->TargetID, type, le16_to_cpu(pScsiTmReply->IOCStatus),
+           le32_to_cpu(pScsiTmReply->IOCLogInfo), pScsiTmReply->ResponseCode,
+           termination_count));
 
-       return status;
+       if (ioc->facts.MsgVersion >= MPI_VERSION_01_05 &&
+           pScsiTmReply->ResponseCode)
+               mptscsih_taskmgmt_response_code(ioc,
+                   pScsiTmReply->ResponseCode);
+
+       if (iocstatus == MPI_IOCSTATUS_SUCCESS) {
+               retval = 0;
+               goto out;
+       }
+
+       retval = FAILED;
+       if (type == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
+               if (termination_count == 1)
+                       retval = 0;
+               goto out;
+       }
+
+       if (iocstatus == MPI_IOCSTATUS_SCSI_TASK_TERMINATED ||
+          iocstatus == MPI_IOCSTATUS_SCSI_IOC_TERMINATED)
+               retval = 0;
+
+ out:
+       return retval;
 }
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-static void
+void
 mptscsih_taskmgmt_response_code(MPT_ADAPTER *ioc, u8 response_code)
 {
        char *desc;
@@ -2134,6 +2000,7 @@ mptscsih_taskmgmt_response_code(MPT_ADAPTER *ioc, u8 response_code)
        printk(MYIOC_s_INFO_FMT "Response Code(0x%08x): F/W: %s\n",
                ioc->name, response_code, desc);
 }
+EXPORT_SYMBOL(mptscsih_taskmgmt_response_code);
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /**
@@ -2150,97 +2017,28 @@ mptscsih_taskmgmt_response_code(MPT_ADAPTER *ioc, u8 response_code)
  *     Returns 1 indicating alloc'd request frame ptr should be freed.
  **/
 int
-mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
+mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf,
+       MPT_FRAME_HDR *mr)
 {
-       SCSITaskMgmtReply_t     *pScsiTmReply;
-       SCSITaskMgmt_t          *pScsiTmReq;
-       MPT_SCSI_HOST           *hd;
-       unsigned long            flags;
-       u16                      iocstatus;
-       u8                       tmType;
-       u32                      termination_count;
-
-       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt completed (mf=%p,mr=%p)\n",
-           ioc->name, mf, mr));
-       if (!ioc->sh) {
-               dtmprintk(ioc, printk(MYIOC_s_WARN_FMT
-                   "TaskMgmt Complete: NULL Scsi Host Ptr\n", ioc->name));
-               return 1;
-       }
-
-       if (mr == NULL) {
-               dtmprintk(ioc, printk(MYIOC_s_WARN_FMT
-                   "ERROR! TaskMgmt Reply: NULL Request %p\n", ioc->name, mf));
-               return 1;
-       }
-
-       hd = shost_priv(ioc->sh);
-       pScsiTmReply = (SCSITaskMgmtReply_t*)mr;
-       pScsiTmReq = (SCSITaskMgmt_t*)mf;
-       tmType = pScsiTmReq->TaskType;
-       iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK;
-       termination_count = le32_to_cpu(pScsiTmReply->TerminationCount);
+       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+               "TaskMgmt completed (mf=%p, mr=%p)\n", ioc->name, mf, mr));
 
-       if (ioc->facts.MsgVersion >= MPI_VERSION_01_05 &&
-           pScsiTmReply->ResponseCode)
-               mptscsih_taskmgmt_response_code(ioc,
-                   pScsiTmReply->ResponseCode);
-       DBG_DUMP_TM_REPLY_FRAME(ioc, (u32 *)pScsiTmReply);
+       ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_COMMAND_GOOD;
 
-#ifdef CONFIG_FUSION_LOGGING
-       if ((ioc->debug_level & MPT_DEBUG_REPLY) ||
-                               (ioc->debug_level & MPT_DEBUG_TM ))
-               printk("%s: ha=%d [%d:%d:0] task_type=0x%02X "
-                       "iocstatus=0x%04X\n\tloginfo=0x%08X response_code=0x%02X "
-                       "term_cmnds=%d\n", __func__, ioc->id, pScsiTmReply->Bus,
-                        pScsiTmReply->TargetID, pScsiTmReq->TaskType,
-                       le16_to_cpu(pScsiTmReply->IOCStatus),
-                       le32_to_cpu(pScsiTmReply->IOCLogInfo),pScsiTmReply->ResponseCode,
-                       le32_to_cpu(pScsiTmReply->TerminationCount));
-#endif
-       if (!iocstatus) {
-               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT " TaskMgmt SUCCESS\n", ioc->name));
-                       hd->abortSCpnt = NULL;
+       if (!mr)
                goto out;
-       }
-
-       /* Error?  (anything non-zero?) */
-
-       /* clear flags and continue.
-        */
-       switch (tmType) {
-
-       case MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK:
-               if (termination_count == 1)
-                       iocstatus = MPI_IOCSTATUS_SCSI_TASK_TERMINATED;
-               hd->abortSCpnt = NULL;
-               break;
-
-       case MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS:
-
-               /* If an internal command is present
-                * or the TM failed - reload the FW.
-                * FC FW may respond FAILED to an ABORT
-                */
-               if (iocstatus == MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED ||
-                   hd->cmdPtr)
-                       if (mpt_HardResetHandler(ioc, NO_SLEEP) < 0)
-                               printk(MYIOC_s_WARN_FMT " Firmware Reload FAILED!!\n", ioc->name);
-               break;
-
-       case MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET:
-       default:
-               break;
-       }
 
+       ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_RF_VALID;
+       memcpy(ioc->taskmgmt_cmds.reply, mr,
+           min(MPT_DEFAULT_FRAME_SIZE, 4 * mr->u.reply.MsgLength));
  out:
-       spin_lock_irqsave(&ioc->FreeQlock, flags);
-       hd->tmPending = 0;
-       hd->tmState = TM_STATE_NONE;
-       hd->tm_iocstatus = iocstatus;
-       spin_unlock_irqrestore(&ioc->FreeQlock, flags);
-
-       return 1;
+       if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_PENDING) {
+               mpt_clear_taskmgmt_in_progress_flag(ioc);
+               ioc->taskmgmt_cmds.status &= ~MPT_MGMT_STATUS_PENDING;
+               complete(&ioc->taskmgmt_cmds.done);
+               return 1;
+       }
+       return 0;
 }
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -2290,8 +2088,10 @@ int
 mptscsih_is_phys_disk(MPT_ADAPTER *ioc, u8 channel, u8 id)
 {
        struct inactive_raid_component_info *component_info;
-       int i;
+       int i, j;
+       RaidPhysDiskPage1_t *phys_disk;
        int rc = 0;
+       int num_paths;
 
        if (!ioc->raid_data.pIocPg3)
                goto out;
@@ -2303,6 +2103,45 @@ mptscsih_is_phys_disk(MPT_ADAPTER *ioc, u8 channel, u8 id)
                }
        }
 
+       if (ioc->bus_type != SAS)
+               goto out;
+
+       /*
+        * Check if dual path
+        */
+       for (i = 0; i < ioc->raid_data.pIocPg3->NumPhysDisks; i++) {
+               num_paths = mpt_raid_phys_disk_get_num_paths(ioc,
+                   ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskNum);
+               if (num_paths < 2)
+                       continue;
+               phys_disk = kzalloc(offsetof(RaidPhysDiskPage1_t, Path) +
+                  (num_paths * sizeof(RAID_PHYS_DISK1_PATH)), GFP_KERNEL);
+               if (!phys_disk)
+                       continue;
+               if ((mpt_raid_phys_disk_pg1(ioc,
+                   ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskNum,
+                   phys_disk))) {
+                       kfree(phys_disk);
+                       continue;
+               }
+               for (j = 0; j < num_paths; j++) {
+                       if ((phys_disk->Path[j].Flags &
+                           MPI_RAID_PHYSDISK1_FLAG_INVALID))
+                               continue;
+                       if ((phys_disk->Path[j].Flags &
+                           MPI_RAID_PHYSDISK1_FLAG_BROKEN))
+                               continue;
+                       if ((id == phys_disk->Path[j].PhysDiskID) &&
+                           (channel == phys_disk->Path[j].PhysDiskBus)) {
+                               rc = 1;
+                               kfree(phys_disk);
+                               goto out;
+                       }
+               }
+               kfree(phys_disk);
+       }
+
+
        /*
         * Check inactive list for matching phys disks
         */
@@ -2327,8 +2166,10 @@ u8
 mptscsih_raid_id_to_num(MPT_ADAPTER *ioc, u8 channel, u8 id)
 {
        struct inactive_raid_component_info *component_info;
-       int i;
+       int i, j;
+       RaidPhysDiskPage1_t *phys_disk;
        int rc = -ENXIO;
+       int num_paths;
 
        if (!ioc->raid_data.pIocPg3)
                goto out;
@@ -2340,6 +2181,44 @@ mptscsih_raid_id_to_num(MPT_ADAPTER *ioc, u8 channel, u8 id)
                }
        }
 
+       if (ioc->bus_type != SAS)
+               goto out;
+
+       /*
+        * Check if dual path
+        */
+       for (i = 0; i < ioc->raid_data.pIocPg3->NumPhysDisks; i++) {
+               num_paths = mpt_raid_phys_disk_get_num_paths(ioc,
+                   ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskNum);
+               if (num_paths < 2)
+                       continue;
+               phys_disk = kzalloc(offsetof(RaidPhysDiskPage1_t, Path) +
+                  (num_paths * sizeof(RAID_PHYS_DISK1_PATH)), GFP_KERNEL);
+               if (!phys_disk)
+                       continue;
+               if ((mpt_raid_phys_disk_pg1(ioc,
+                   ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskNum,
+                   phys_disk))) {
+                       kfree(phys_disk);
+                       continue;
+               }
+               for (j = 0; j < num_paths; j++) {
+                       if ((phys_disk->Path[j].Flags &
+                           MPI_RAID_PHYSDISK1_FLAG_INVALID))
+                               continue;
+                       if ((phys_disk->Path[j].Flags &
+                           MPI_RAID_PHYSDISK1_FLAG_BROKEN))
+                               continue;
+                       if ((id == phys_disk->Path[j].PhysDiskID) &&
+                           (channel == phys_disk->Path[j].PhysDiskBus)) {
+                               rc = phys_disk->PhysDiskNum;
+                               kfree(phys_disk);
+                               goto out;
+                       }
+               }
+               kfree(phys_disk);
+       }
+
        /*
         * Check inactive list for matching phys disks
         */
@@ -2457,7 +2336,6 @@ mptscsih_slave_configure(struct scsi_device *sdev)
                    sdev->ppr, sdev->inquiry_len));
 
        vdevice->configured_lun = 1;
-       mptscsih_change_queue_depth(sdev, MPT_SCSI_CMD_PER_DEV_HIGH);
 
        dsprintk(ioc, printk(MYIOC_s_DEBUG_FMT
                "Queue depth=%d, tflags=%x\n",
@@ -2469,6 +2347,7 @@ mptscsih_slave_configure(struct scsi_device *sdev)
                    ioc->name, vtarget->negoFlags, vtarget->maxOffset,
                    vtarget->minSyncFactor));
 
+       mptscsih_change_queue_depth(sdev, MPT_SCSI_CMD_PER_DEV_HIGH);
        dsprintk(ioc, printk(MYIOC_s_DEBUG_FMT
                "tagged %d, simple %d, ordered %d\n",
                ioc->name,sdev->tagged_supported, sdev->simple_tags,
@@ -2542,15 +2421,13 @@ mptscsih_copy_sense_data(struct scsi_cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR
 }
 
 /**
- * mptscsih_get_scsi_lookup
+ * mptscsih_get_scsi_lookup - retrieves scmd entry
  * @ioc: Pointer to MPT_ADAPTER structure
  * @i: index into the array
  *
- * retrieves scmd entry from ScsiLookup[] array list
- *
  * Returns the scsi_cmd pointer
- **/
-static struct scsi_cmnd *
+ */
+struct scsi_cmnd *
 mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i)
 {
        unsigned long   flags;
@@ -2562,15 +2439,15 @@ mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i)
 
        return scmd;
 }
+EXPORT_SYMBOL(mptscsih_get_scsi_lookup);
 
 /**
- * mptscsih_getclear_scsi_lookup
+ * mptscsih_getclear_scsi_lookup -  retrieves and clears scmd entry from ScsiLookup[] array list
  * @ioc: Pointer to MPT_ADAPTER structure
  * @i: index into the array
  *
- * retrieves and clears scmd entry from ScsiLookup[] array list
- *
  * Returns the scsi_cmd pointer
+ *
  **/
 static struct scsi_cmnd *
 mptscsih_getclear_scsi_lookup(MPT_ADAPTER *ioc, int i)
@@ -2635,94 +2512,33 @@ int
 mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
 {
        MPT_SCSI_HOST   *hd;
-       unsigned long    flags;
 
-       dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
-           ": IOC %s_reset routed to SCSI host driver!\n",
-           ioc->name, reset_phase==MPT_IOC_SETUP_RESET ? "setup" : (
-           reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post")));
-
-       /* If a FW reload request arrives after base installed but
-        * before all scsi hosts have been attached, then an alt_ioc
-        * may have a NULL sh pointer.
-        */
        if (ioc->sh == NULL || shost_priv(ioc->sh) == NULL)
                return 0;
-       else
-               hd = shost_priv(ioc->sh);
-
-       if (reset_phase == MPT_IOC_SETUP_RESET) {
-               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Setup-Diag Reset\n", ioc->name));
-
-               /* Clean Up:
-                * 1. Set Hard Reset Pending Flag
-                * All new commands go to doneQ
-                */
-               hd->resetPending = 1;
-
-       } else if (reset_phase == MPT_IOC_PRE_RESET) {
-               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Pre-Diag Reset\n", ioc->name));
 
-               /* 2. Flush running commands
-                *      Clean ScsiLookup (and associated memory)
-                *      AND clean mytaskQ
-                */
-
-               /* 2b. Reply to OS all known outstanding I/O commands.
-                */
+       hd = shost_priv(ioc->sh);
+       switch (reset_phase) {
+       case MPT_IOC_SETUP_RESET:
+               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "%s: MPT_IOC_SETUP_RESET\n", ioc->name, __func__));
+               break;
+       case MPT_IOC_PRE_RESET:
+               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "%s: MPT_IOC_PRE_RESET\n", ioc->name, __func__));
                mptscsih_flush_running_cmds(hd);
-
-               /* 2c. If there was an internal command that
-                * has not completed, configuration or io request,
-                * free these resources.
-                */
-               if (hd->cmdPtr) {
-                       del_timer(&hd->timer);
-                       mpt_free_msg_frame(ioc, hd->cmdPtr);
-               }
-
-               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Pre-Reset complete.\n", ioc->name));
-
-       } else {
-               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Post-Diag Reset\n", ioc->name));
-
-               /* Once a FW reload begins, all new OS commands are
-                * redirected to the doneQ w/ a reset status.
-                * Init all control structures.
-                */
-
-               /* 2. Chain Buffer initialization
-                */
-
-               /* 4. Renegotiate to all devices, if SPI
-                */
-
-               /* 5. Enable new commands to be posted
-                */
-               spin_lock_irqsave(&ioc->FreeQlock, flags);
-               hd->tmPending = 0;
-               spin_unlock_irqrestore(&ioc->FreeQlock, flags);
-               hd->resetPending = 0;
-               hd->tmState = TM_STATE_NONE;
-
-               /* 6. If there was an internal command,
-                * wake this process up.
-                */
-               if (hd->cmdPtr) {
-                       /*
-                        * Wake up the original calling thread
-                        */
-                       hd->pLocal = &hd->localReply;
-                       hd->pLocal->completion = MPT_SCANDV_DID_RESET;
-                       hd->scandv_wait_done = 1;
-                       wake_up(&hd->scandv_waitq);
-                       hd->cmdPtr = NULL;
+               break;
+       case MPT_IOC_POST_RESET:
+               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "%s: MPT_IOC_POST_RESET\n", ioc->name, __func__));
+               if (ioc->internal_cmds.status & MPT_MGMT_STATUS_PENDING) {
+                       ioc->internal_cmds.status |=
+                               MPT_MGMT_STATUS_DID_IOCRESET;
+                       complete(&ioc->internal_cmds.done);
                }
-
-               dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Post-Reset complete.\n", ioc->name));
-
+               break;
+       default:
+               break;
        }
-
        return 1;               /* currently means nothing really */
 }
 
@@ -2730,55 +2546,16 @@ mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
 int
 mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply)
 {
-       MPT_SCSI_HOST *hd;
        u8 event = le32_to_cpu(pEvReply->Event) & 0xFF;
 
-       devtverboseprintk(ioc, printk(MYIOC_s_DEBUG_FMT "MPT event (=%02Xh) routed to SCSI host driver!\n",
-                       ioc->name, event));
-
-       if (ioc->sh == NULL ||
-               ((hd = shost_priv(ioc->sh)) == NULL))
-               return 1;
-
-       switch (event) {
-       case MPI_EVENT_UNIT_ATTENTION:                  /* 03 */
-               /* FIXME! */
-               break;
-       case MPI_EVENT_IOC_BUS_RESET:                   /* 04 */
-       case MPI_EVENT_EXT_BUS_RESET:                   /* 05 */
-               if (hd && (ioc->bus_type == SPI) && (hd->soft_resets < -1))
-                       hd->soft_resets++;
-               break;
-       case MPI_EVENT_LOGOUT:                          /* 09 */
-               /* FIXME! */
-               break;
-
-       case MPI_EVENT_RESCAN:                          /* 06 */
-               break;
-
-               /*
-                *  CHECKME! Don't think we need to do
-                *  anything for these, but...
-                */
-       case MPI_EVENT_LINK_STATUS_CHANGE:              /* 07 */
-       case MPI_EVENT_LOOP_STATE_CHANGE:               /* 08 */
-               /*
-                *  CHECKME!  Falling thru...
-                */
-               break;
-
-       case MPI_EVENT_INTEGRATED_RAID:                 /* 0B */
-               break;
+       devtverboseprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+               "MPT event (=%02Xh) routed to SCSI host driver!\n",
+               ioc->name, event));
 
-       case MPI_EVENT_NONE:                            /* 00 */
-       case MPI_EVENT_LOG_DATA:                        /* 01 */
-       case MPI_EVENT_STATE_CHANGE:                    /* 02 */
-       case MPI_EVENT_EVENT_CHANGE:                    /* 0A */
-       default:
-               dprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": Ignoring event (=%02Xh)\n",
-                   ioc->name, event));
-               break;
-       }
+       if ((event == MPI_EVENT_IOC_BUS_RESET ||
+           event == MPI_EVENT_EXT_BUS_RESET) &&
+           (ioc->bus_type == SPI) && (ioc->soft_resets < -1))
+                       ioc->soft_resets++;
 
        return 1;               /* currently means nothing really */
 }
@@ -2809,153 +2586,44 @@ mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply)
  *     Used ONLY for DV and other internal commands.
  */
 int
-mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
+mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req,
+                               MPT_FRAME_HDR *reply)
 {
-       MPT_SCSI_HOST   *hd;
        SCSIIORequest_t *pReq;
-       int              completionCode;
+       SCSIIOReply_t   *pReply;
+       u8               cmd;
        u16              req_idx;
+       u8      *sense_data;
+       int              sz;
 
-       hd = shost_priv(ioc->sh);
-
-       if ((mf == NULL) ||
-           (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) {
-               printk(MYIOC_s_ERR_FMT
-                       "ScanDvComplete, %s req frame ptr! (=%p)\n",
-                               ioc->name, mf?"BAD":"NULL", (void *) mf);
-               goto wakeup;
-       }
-
-       del_timer(&hd->timer);
-       req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
-       mptscsih_set_scsi_lookup(ioc, req_idx, NULL);
-       pReq = (SCSIIORequest_t *) mf;
+       ioc->internal_cmds.status |= MPT_MGMT_STATUS_COMMAND_GOOD;
+       ioc->internal_cmds.completion_code = MPT_SCANDV_GOOD;
+       if (!reply)
+               goto out;
 
-       if (mf != hd->cmdPtr) {
-               printk(MYIOC_s_WARN_FMT "ScanDvComplete (mf=%p, cmdPtr=%p, idx=%d)\n",
-                               ioc->name, (void *)mf, (void *) hd->cmdPtr, req_idx);
+       pReply = (SCSIIOReply_t *) reply;
+       pReq = (SCSIIORequest_t *) req;
+       ioc->internal_cmds.completion_code =
+           mptscsih_get_completion_code(ioc, req, reply);
+       ioc->internal_cmds.status |= MPT_MGMT_STATUS_RF_VALID;
+       memcpy(ioc->internal_cmds.reply, reply,
+           min(MPT_DEFAULT_FRAME_SIZE, 4 * reply->u.reply.MsgLength));
+       cmd = reply->u.hdr.Function;
+       if (((cmd == MPI_FUNCTION_SCSI_IO_REQUEST) ||
+           (cmd == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) &&
+           (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID)) {
+               req_idx = le16_to_cpu(req->u.frame.hwhdr.msgctxu.fld.req_idx);
+               sense_data = ((u8 *)ioc->sense_buf_pool +
+                   (req_idx * MPT_SENSE_BUFFER_ALLOC));
+               sz = min_t(int, pReq->SenseBufferLength,
+                   MPT_SENSE_BUFFER_ALLOC);
+               memcpy(ioc->internal_cmds.sense, sense_data, sz);
        }
-       hd->cmdPtr = NULL;
-
-       ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ScanDvComplete (mf=%p,mr=%p,idx=%d)\n",
-                       ioc->name, mf, mr, req_idx));
-
-       hd->pLocal = &hd->localReply;
-       hd->pLocal->scsiStatus = 0;
-
-       /* If target struct exists, clear sense valid flag.
-        */
-       if (mr == NULL) {
-               completionCode = MPT_SCANDV_GOOD;
-       } else {
-               SCSIIOReply_t   *pReply;
-               u16              status;
-               u8               scsi_status;
-
-               pReply = (SCSIIOReply_t *) mr;
-
-               status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK;
-               scsi_status = pReply->SCSIStatus;
-
-
-               switch(status) {
-
-               case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE:       /* 0x0043 */
-                       completionCode = MPT_SCANDV_SELECTION_TIMEOUT;
-                       break;
-
-               case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR:          /* 0x0046 */
-               case MPI_IOCSTATUS_SCSI_TASK_TERMINATED:        /* 0x0048 */
-               case MPI_IOCSTATUS_SCSI_IOC_TERMINATED:         /* 0x004B */
-               case MPI_IOCSTATUS_SCSI_EXT_TERMINATED:         /* 0x004C */
-                       completionCode = MPT_SCANDV_DID_RESET;
-                       break;
-
-               case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN:          /* 0x0045 */
-               case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR:        /* 0x0040 */
-               case MPI_IOCSTATUS_SUCCESS:                     /* 0x0000 */
-                       if (pReply->Function == MPI_FUNCTION_CONFIG) {
-                               ConfigReply_t *pr = (ConfigReply_t *)mr;
-                               completionCode = MPT_SCANDV_GOOD;
-                               hd->pLocal->header.PageVersion = pr->Header.PageVersion;
-                               hd->pLocal->header.PageLength = pr->Header.PageLength;
-                               hd->pLocal->header.PageNumber = pr->Header.PageNumber;
-                               hd->pLocal->header.PageType = pr->Header.PageType;
-
-                       } else if (pReply->Function == MPI_FUNCTION_RAID_ACTION) {
-                               /* If the RAID Volume request is successful,
-                                * return GOOD, else indicate that
-                                * some type of error occurred.
-                                */
-                               MpiRaidActionReply_t    *pr = (MpiRaidActionReply_t *)mr;
-                               if (le16_to_cpu(pr->ActionStatus) == MPI_RAID_ACTION_ASTATUS_SUCCESS)
-                                       completionCode = MPT_SCANDV_GOOD;
-                               else
-                                       completionCode = MPT_SCANDV_SOME_ERROR;
-                               memcpy(hd->pLocal->sense, pr, sizeof(hd->pLocal->sense));
-
-                       } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) {
-                               u8              *sense_data;
-                               int              sz;
-
-                               /* save sense data in global structure
-                                */
-                               completionCode = MPT_SCANDV_SENSE;
-                               hd->pLocal->scsiStatus = scsi_status;
-                               sense_data = ((u8 *)ioc->sense_buf_pool +
-                                       (req_idx * MPT_SENSE_BUFFER_ALLOC));
-
-                               sz = min_t(int, pReq->SenseBufferLength,
-                                                       SCSI_STD_SENSE_BYTES);
-                               memcpy(hd->pLocal->sense, sense_data, sz);
-
-                               ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT "  Check Condition, sense ptr %p\n",
-                                   ioc->name, sense_data));
-                       } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_FAILED) {
-                               if (pReq->CDB[0] == INQUIRY)
-                                       completionCode = MPT_SCANDV_ISSUE_SENSE;
-                               else
-                                       completionCode = MPT_SCANDV_DID_RESET;
-                       }
-                       else if (pReply->SCSIState & MPI_SCSI_STATE_NO_SCSI_STATUS)
-                               completionCode = MPT_SCANDV_DID_RESET;
-                       else if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
-                               completionCode = MPT_SCANDV_DID_RESET;
-                       else {
-                               completionCode = MPT_SCANDV_GOOD;
-                               hd->pLocal->scsiStatus = scsi_status;
-                       }
-                       break;
-
-               case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR:         /* 0x0047 */
-                       if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
-                               completionCode = MPT_SCANDV_DID_RESET;
-                       else
-                               completionCode = MPT_SCANDV_SOME_ERROR;
-                       break;
-
-               default:
-                       completionCode = MPT_SCANDV_SOME_ERROR;
-                       break;
-
-               }       /* switch(status) */
-
-       } /* end of address reply case */
-
-       hd->pLocal->completion = completionCode;
-
-       /* MF and RF are freed in mpt_interrupt
-        */
-wakeup:
-       /* Free Chain buffers (will never chain) in scan or dv */
-       //mptscsih_freeChainBuffers(ioc, req_idx);
-
-       /*
-        * Wake up the original calling thread
-        */
-       hd->scandv_wait_done = 1;
-       wake_up(&hd->scandv_waitq);
-
+ out:
+       if (!(ioc->internal_cmds.status & MPT_MGMT_STATUS_PENDING))
+               return 0;
+       ioc->internal_cmds.status &= ~MPT_MGMT_STATUS_PENDING;
+       complete(&ioc->internal_cmds.done);
        return 1;
 }
 
@@ -3004,6 +2672,95 @@ mptscsih_timer_expired(unsigned long data)
        return;
 }
 
+/**
+ *     mptscsih_get_completion_code -
+ *     @ioc: Pointer to MPT_ADAPTER structure
+ *     @reply:
+ *     @cmd:
+ *
+ **/
+static int
+mptscsih_get_completion_code(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req,
+                               MPT_FRAME_HDR *reply)
+{
+       SCSIIOReply_t   *pReply;
+       MpiRaidActionReply_t *pr;
+       u8               scsi_status;
+       u16              status;
+       int              completion_code;
+
+       pReply = (SCSIIOReply_t *)reply;
+       status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK;
+       scsi_status = pReply->SCSIStatus;
+
+       devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+           "IOCStatus=%04xh, SCSIState=%02xh, SCSIStatus=%02xh,"
+           "IOCLogInfo=%08xh\n", ioc->name, status, pReply->SCSIState,
+           scsi_status, le32_to_cpu(pReply->IOCLogInfo)));
+
+       switch (status) {
+
+       case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE:       /* 0x0043 */
+               completion_code = MPT_SCANDV_SELECTION_TIMEOUT;
+               break;
+
+       case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR:          /* 0x0046 */
+       case MPI_IOCSTATUS_SCSI_TASK_TERMINATED:        /* 0x0048 */
+       case MPI_IOCSTATUS_SCSI_IOC_TERMINATED:         /* 0x004B */
+       case MPI_IOCSTATUS_SCSI_EXT_TERMINATED:         /* 0x004C */
+               completion_code = MPT_SCANDV_DID_RESET;
+               break;
+
+       case MPI_IOCSTATUS_BUSY:
+       case MPI_IOCSTATUS_INSUFFICIENT_RESOURCES:
+               completion_code = MPT_SCANDV_BUSY;
+               break;
+
+       case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN:          /* 0x0045 */
+       case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR:        /* 0x0040 */
+       case MPI_IOCSTATUS_SUCCESS:                     /* 0x0000 */
+               if (pReply->Function == MPI_FUNCTION_CONFIG) {
+                       completion_code = MPT_SCANDV_GOOD;
+               } else if (pReply->Function == MPI_FUNCTION_RAID_ACTION) {
+                       pr = (MpiRaidActionReply_t *)reply;
+                       if (le16_to_cpu(pr->ActionStatus) ==
+                               MPI_RAID_ACTION_ASTATUS_SUCCESS)
+                               completion_code = MPT_SCANDV_GOOD;
+                       else
+                               completion_code = MPT_SCANDV_SOME_ERROR;
+               } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID)
+                       completion_code = MPT_SCANDV_SENSE;
+               else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_FAILED) {
+                       if (req->u.scsireq.CDB[0] == INQUIRY)
+                               completion_code = MPT_SCANDV_ISSUE_SENSE;
+                       else
+                               completion_code = MPT_SCANDV_DID_RESET;
+               } else if (pReply->SCSIState & MPI_SCSI_STATE_NO_SCSI_STATUS)
+                       completion_code = MPT_SCANDV_DID_RESET;
+               else if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
+                       completion_code = MPT_SCANDV_DID_RESET;
+               else if (scsi_status == MPI_SCSI_STATUS_BUSY)
+                       completion_code = MPT_SCANDV_BUSY;
+               else
+                       completion_code = MPT_SCANDV_GOOD;
+               break;
+
+       case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR:         /* 0x0047 */
+               if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
+                       completion_code = MPT_SCANDV_DID_RESET;
+               else
+                       completion_code = MPT_SCANDV_SOME_ERROR;
+               break;
+       default:
+               completion_code = MPT_SCANDV_SOME_ERROR;
+               break;
+
+       }       /* switch(status) */
+
+       devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+           "  completionCode set to %08xh\n", ioc->name, completion_code));
+       return completion_code;
+}
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /**
@@ -3030,22 +2787,27 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
 {
        MPT_FRAME_HDR   *mf;
        SCSIIORequest_t *pScsiReq;
-       SCSIIORequest_t  ReqCopy;
        int              my_idx, ii, dir;
-       int              rc, cmdTimeout;
-       int             in_isr;
+       int              timeout;
        char             cmdLen;
        char             CDB[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
-       char             cmd = io->cmd;
-       MPT_ADAPTER     *ioc = hd->ioc;
+       u8               cmd = io->cmd;
+       MPT_ADAPTER *ioc = hd->ioc;
+       int              ret = 0;
+       unsigned long    timeleft;
+       unsigned long    flags;
 
-       in_isr = in_interrupt();
-       if (in_isr) {
-               dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Internal SCSI IO request not allowed in ISR context!\n",
-                                       ioc->name));
-               return -EPERM;
+       /* don't send internal command during diag reset */
+       spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
+       if (ioc->ioc_reset_in_progress) {
+               spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+               dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                       "%s: busy with host reset\n", ioc->name, __func__));
+               return MPT_SCANDV_BUSY;
        }
+       spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
 
+       mutex_lock(&ioc->internal_cmds.mutex);
 
        /* Set command specific information
         */
@@ -3055,13 +2817,13 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
                dir = MPI_SCSIIO_CONTROL_READ;
                CDB[0] = cmd;
                CDB[4] = io->size;
-               cmdTimeout = 10;
+               timeout = 10;
                break;
 
        case TEST_UNIT_READY:
                cmdLen = 6;
                dir = MPI_SCSIIO_CONTROL_READ;
-               cmdTimeout = 10;
+               timeout = 10;
                break;
 
        case START_STOP:
@@ -3069,7 +2831,7 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
                dir = MPI_SCSIIO_CONTROL_READ;
                CDB[0] = cmd;
                CDB[4] = 1;     /*Spin up the disk */
-               cmdTimeout = 15;
+               timeout = 15;
                break;
 
        case REQUEST_SENSE:
@@ -3077,7 +2839,7 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
                CDB[0] = cmd;
                CDB[4] = io->size;
                dir = MPI_SCSIIO_CONTROL_READ;
-               cmdTimeout = 10;
+               timeout = 10;
                break;
 
        case READ_BUFFER:
@@ -3096,7 +2858,7 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
                CDB[6] = (io->size >> 16) & 0xFF;
                CDB[7] = (io->size >>  8) & 0xFF;
                CDB[8] = io->size & 0xFF;
-               cmdTimeout = 10;
+               timeout = 10;
                break;
 
        case WRITE_BUFFER:
@@ -3111,21 +2873,21 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
                CDB[6] = (io->size >> 16) & 0xFF;
                CDB[7] = (io->size >>  8) & 0xFF;
                CDB[8] = io->size & 0xFF;
-               cmdTimeout = 10;
+               timeout = 10;
                break;
 
        case RESERVE:
                cmdLen = 6;
                dir = MPI_SCSIIO_CONTROL_READ;
                CDB[0] = cmd;
-               cmdTimeout = 10;
+               timeout = 10;
                break;
 
        case RELEASE:
                cmdLen = 6;
                dir = MPI_SCSIIO_CONTROL_READ;
                CDB[0] = cmd;
-               cmdTimeout = 10;
+               timeout = 10;
                break;
 
        case SYNCHRONIZE_CACHE:
@@ -3133,20 +2895,23 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
                dir = MPI_SCSIIO_CONTROL_READ;
                CDB[0] = cmd;
 //             CDB[1] = 0x02;  /* set immediate bit */
-               cmdTimeout = 10;
+               timeout = 10;
                break;
 
        default:
                /* Error Case */
-               return -EFAULT;
+               ret = -EFAULT;
+               goto out;
        }
 
        /* Get and Populate a free Frame
+        * MsgContext set in mpt_get_msg_frame call
         */
        if ((mf = mpt_get_msg_frame(ioc->InternalCtx, ioc)) == NULL) {
-               dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "No msg frames!\n",
-                   ioc->name));
-               return -EBUSY;
+               dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s: No msg frames!\n",
+                   ioc->name, __func__));
+               ret = MPT_SCANDV_BUSY;
+               goto out;
        }
 
        pScsiReq = (SCSIIORequest_t *) mf;
@@ -3172,7 +2937,7 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
 
        pScsiReq->Reserved = 0;
 
-       pScsiReq->MsgFlags = mpt_msg_flags();
+       pScsiReq->MsgFlags = mpt_msg_flags(ioc);
        /* MsgContext set in mpt_get_msg_fram call  */
 
        int_to_scsilun(io->lun, (struct scsi_lun *)pScsiReq->LUN);
@@ -3184,74 +2949,58 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
 
        if (cmd == REQUEST_SENSE) {
                pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED);
-               ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Untagged! 0x%2x\n",
-                       ioc->name, cmd));
+               devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "%s: Untagged! 0x%02x\n", ioc->name, __func__, cmd));
        }
 
-       for (ii=0; ii < 16; ii++)
+       for (ii = 0; ii < 16; ii++)
                pScsiReq->CDB[ii] = CDB[ii];
 
        pScsiReq->DataLength = cpu_to_le32(io->size);
        pScsiReq->SenseBufferLowAddr = cpu_to_le32(ioc->sense_buf_low_dma
                                           + (my_idx * MPT_SENSE_BUFFER_ALLOC));
 
-       ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Sending Command 0x%x for (%d:%d:%d)\n",
-                       ioc->name, cmd, io->channel, io->id, io->lun));
+       devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+           "%s: Sending Command 0x%02x for fw_channel=%d fw_id=%d lun=%d\n",
+           ioc->name, __func__, cmd, io->channel, io->id, io->lun));
 
-       if (dir == MPI_SCSIIO_CONTROL_READ) {
-               mpt_add_sge((char *) &pScsiReq->SGL,
-                       MPT_SGE_FLAGS_SSIMPLE_READ | io->size,
-                       io->data_dma);
-       } else {
-               mpt_add_sge((char *) &pScsiReq->SGL,
-                       MPT_SGE_FLAGS_SSIMPLE_WRITE | io->size,
-                       io->data_dma);
-       }
-
-       /* The ISR will free the request frame, but we need
-        * the information to initialize the target. Duplicate.
-        */
-       memcpy(&ReqCopy, pScsiReq, sizeof(SCSIIORequest_t));
-
-       /* Issue this command after:
-        *      finish init
-        *      add timer
-        * Wait until the reply has been received
-        *  ScsiScanDvCtx callback function will
-        *      set hd->pLocal;
-        *      set scandv_wait_done and call wake_up
-        */
-       hd->pLocal = NULL;
-       hd->timer.expires = jiffies + HZ*cmdTimeout;
-       hd->scandv_wait_done = 0;
-
-       /* Save cmd pointer, for resource free if timeout or
-        * FW reload occurs
-        */
-       hd->cmdPtr = mf;
+       if (dir == MPI_SCSIIO_CONTROL_READ)
+               ioc->add_sge((char *) &pScsiReq->SGL,
+                   MPT_SGE_FLAGS_SSIMPLE_READ | io->size, io->data_dma);
+       else
+               ioc->add_sge((char *) &pScsiReq->SGL,
+                   MPT_SGE_FLAGS_SSIMPLE_WRITE | io->size, io->data_dma);
 
-       add_timer(&hd->timer);
+       INITIALIZE_MGMT_STATUS(ioc->internal_cmds.status)
        mpt_put_msg_frame(ioc->InternalCtx, ioc, mf);
-       wait_event(hd->scandv_waitq, hd->scandv_wait_done);
-
-       if (hd->pLocal) {
-               rc = hd->pLocal->completion;
-               hd->pLocal->skip = 0;
-
-               /* Always set fatal error codes in some cases.
-                */
-               if (rc == MPT_SCANDV_SELECTION_TIMEOUT)
-                       rc = -ENXIO;
-               else if (rc == MPT_SCANDV_SOME_ERROR)
-                       rc =  -rc;
-       } else {
-               rc = -EFAULT;
-               /* This should never happen. */
-               ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT "_do_cmd: Null pLocal!!!\n",
-                               ioc->name));
+       timeleft = wait_for_completion_timeout(&ioc->internal_cmds.done,
+           timeout*HZ);
+       if (!(ioc->internal_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+               ret = MPT_SCANDV_DID_RESET;
+               dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT
+                   "%s: TIMED OUT for cmd=0x%02x\n", ioc->name, __func__,
+                   cmd));
+               if (ioc->internal_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET) {
+                       mpt_free_msg_frame(ioc, mf);
+                       goto out;
+               }
+               if (!timeleft) {
+                       printk(MYIOC_s_WARN_FMT "Issuing Reset from %s!!\n",
+                           ioc->name, __func__);
+                       mpt_HardResetHandler(ioc, CAN_SLEEP);
+                       mpt_free_msg_frame(ioc, mf);
+               }
+               goto out;
        }
 
-       return rc;
+       ret = ioc->internal_cmds.completion_code;
+       devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: success, rc=0x%02x\n",
+                       ioc->name, __func__, ret));
+
+ out:
+       CLEAR_MGMT_STATUS(ioc->internal_cmds.status)
+       mutex_unlock(&ioc->internal_cmds.mutex);
+       return ret;
 }
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -3491,6 +3240,7 @@ struct device_attribute *mptscsih_host_attrs[] = {
        &dev_attr_debug_level,
        NULL,
 };
+
 EXPORT_SYMBOL(mptscsih_host_attrs);
 
 EXPORT_SYMBOL(mptscsih_remove);
@@ -3516,6 +3266,5 @@ EXPORT_SYMBOL(mptscsih_event_process);
 EXPORT_SYMBOL(mptscsih_ioc_reset);
 EXPORT_SYMBOL(mptscsih_change_queue_depth);
 EXPORT_SYMBOL(mptscsih_timer_expired);
-EXPORT_SYMBOL(mptscsih_TMHandler);
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
index 319aa3033371999e8c75b2f151a84942744d04fb..eb3f677528ac703b1dac56f6795441fc155108df 100644 (file)
@@ -60,6 +60,7 @@
 #define MPT_SCANDV_SELECTION_TIMEOUT   (0x00000008)
 #define MPT_SCANDV_ISSUE_SENSE         (0x00000010)
 #define MPT_SCANDV_FALLBACK            (0x00000020)
+#define MPT_SCANDV_BUSY                        (0x00000040)
 
 #define MPT_SCANDV_MAX_RETRIES         (10)
 
@@ -89,6 +90,7 @@
 
 #endif
 
+
 typedef struct _internal_cmd {
        char            *data;          /* data pointer */
        dma_addr_t      data_dma;       /* data dma address */
@@ -112,6 +114,8 @@ extern int mptscsih_resume(struct pci_dev *pdev);
 extern int mptscsih_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, int length, int func);
 extern const char * mptscsih_info(struct Scsi_Host *SChost);
 extern int mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *));
+extern int mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel,
+       u8 id, int lun, int ctx2abort, ulong timeout);
 extern void mptscsih_slave_destroy(struct scsi_device *device);
 extern int mptscsih_slave_configure(struct scsi_device *device);
 extern int mptscsih_abort(struct scsi_cmnd * SCpnt);
@@ -126,7 +130,8 @@ extern int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pE
 extern int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset);
 extern int mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth);
 extern void mptscsih_timer_expired(unsigned long data);
-extern int mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, int ctx2abort, ulong timeout);
 extern u8 mptscsih_raid_id_to_num(MPT_ADAPTER *ioc, u8 channel, u8 id);
 extern int mptscsih_is_phys_disk(MPT_ADAPTER *ioc, u8 channel, u8 id);
 extern struct device_attribute *mptscsih_host_attrs[];
+extern struct scsi_cmnd        *mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i);
+extern void mptscsih_taskmgmt_response_code(MPT_ADAPTER *ioc, u8 response_code);
index 61620144e49c32c82a834e9911035dc675691f1e..c5b808fd55ba6fc2b523939836abe347e85aeeea 100644 (file)
@@ -300,7 +300,7 @@ mptspi_writeIOCPage4(MPT_SCSI_HOST *hd, u8 channel , u8 id)
        flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE |
                (IOCPage4Ptr->Header.PageLength + ii) * 4;
 
-       mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, dataDma);
+       ioc->add_sge((char *)&pReq->PageBufferSGE, flagsLength, dataDma);
 
        ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT
                "writeIOCPage4: MaxSEP=%d ActiveSEP=%d id=%d bus=%d\n",
@@ -614,19 +614,24 @@ static void mptspi_read_parameters(struct scsi_target *starget)
        spi_width(starget) = (nego & MPI_SCSIDEVPAGE0_NP_WIDE) ? 1 : 0;
 }
 
-static int
+int
 mptscsih_quiesce_raid(MPT_SCSI_HOST *hd, int quiesce, u8 channel, u8 id)
 {
+       MPT_ADAPTER     *ioc = hd->ioc;
        MpiRaidActionRequest_t  *pReq;
        MPT_FRAME_HDR           *mf;
-       MPT_ADAPTER *ioc = hd->ioc;
+       int                     ret;
+       unsigned long           timeleft;
+
+       mutex_lock(&ioc->internal_cmds.mutex);
 
        /* Get and Populate a free Frame
         */
        if ((mf = mpt_get_msg_frame(ioc->InternalCtx, ioc)) == NULL) {
-               ddvprintk(ioc, printk(MYIOC_s_WARN_FMT "_do_raid: no msg frames!\n",
-                                       ioc->name));
-               return -EAGAIN;
+               dfailprintk(hd->ioc, printk(MYIOC_s_WARN_FMT
+                       "%s: no msg frames!\n", ioc->name, __func__));
+               ret = -EAGAIN;
+               goto out;
        }
        pReq = (MpiRaidActionRequest_t *)mf;
        if (quiesce)
@@ -643,29 +648,36 @@ mptscsih_quiesce_raid(MPT_SCSI_HOST *hd, int quiesce, u8 channel, u8 id)
        pReq->Reserved2 = 0;
        pReq->ActionDataWord = 0; /* Reserved for this action */
 
-       mpt_add_sge((char *)&pReq->ActionDataSGE,
+       ioc->add_sge((char *)&pReq->ActionDataSGE,
                MPT_SGE_FLAGS_SSIMPLE_READ | 0, (dma_addr_t) -1);
 
        ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT "RAID Volume action=%x channel=%d id=%d\n",
                        ioc->name, pReq->Action, channel, id));
 
-       hd->pLocal = NULL;
-       hd->timer.expires = jiffies + HZ*10; /* 10 second timeout */
-       hd->scandv_wait_done = 0;
-
-       /* Save cmd pointer, for resource free if timeout or
-        * FW reload occurs
-        */
-       hd->cmdPtr = mf;
-
-       add_timer(&hd->timer);
+       INITIALIZE_MGMT_STATUS(ioc->internal_cmds.status)
        mpt_put_msg_frame(ioc->InternalCtx, ioc, mf);
-       wait_event(hd->scandv_waitq, hd->scandv_wait_done);
+       timeleft = wait_for_completion_timeout(&ioc->internal_cmds.done, 10*HZ);
+       if (!(ioc->internal_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
+               ret = -ETIME;
+               dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: TIMED OUT!\n",
+                   ioc->name, __func__));
+               if (ioc->internal_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET)
+                       goto out;
+               if (!timeleft) {
+                       printk(MYIOC_s_WARN_FMT "Issuing Reset from %s!!\n",
+                           ioc->name, __func__);
+                       mpt_HardResetHandler(ioc, CAN_SLEEP);
+                       mpt_free_msg_frame(ioc, mf);
+               }
+               goto out;
+       }
 
-       if ((hd->pLocal == NULL) || (hd->pLocal->completion != 0))
-               return -1;
+       ret = ioc->internal_cmds.completion_code;
 
-       return 0;
+ out:
+       CLEAR_MGMT_STATUS(ioc->internal_cmds.status)
+       mutex_unlock(&ioc->internal_cmds.mutex);
+       return ret;
 }
 
 static void mptspi_dv_device(struct _MPT_SCSI_HOST *hd,
@@ -1423,17 +1435,15 @@ mptspi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
         * A slightly different algorithm is required for
         * 64bit SGEs.
         */
-       scale = ioc->req_sz/(sizeof(dma_addr_t) + sizeof(u32));
-       if (sizeof(dma_addr_t) == sizeof(u64)) {
+       scale = ioc->req_sz/ioc->SGE_size;
+       if (ioc->sg_addr_size == sizeof(u64)) {
                numSGE = (scale - 1) *
                  (ioc->facts.MaxChainDepth-1) + scale +
-                 (ioc->req_sz - 60) / (sizeof(dma_addr_t) +
-                 sizeof(u32));
+                 (ioc->req_sz - 60) / ioc->SGE_size;
        } else {
                numSGE = 1 + (scale - 1) *
                  (ioc->facts.MaxChainDepth-1) + scale +
-                 (ioc->req_sz - 64) / (sizeof(dma_addr_t) +
-                 sizeof(u32));
+                 (ioc->req_sz - 64) / ioc->SGE_size;
        }
 
        if (numSGE < sh->sg_tablesize) {
@@ -1464,9 +1474,6 @@ mptspi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
        /* Clear the TM flags
         */
-       hd->tmPending = 0;
-       hd->tmState = TM_STATE_NONE;
-       hd->resetPending = 0;
        hd->abortSCpnt = NULL;
 
        /* Clear the pointer used to store
@@ -1493,8 +1500,6 @@ mptspi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                mpt_saf_te));
        ioc->spi_data.noQas = 0;
 
-       init_waitqueue_head(&hd->scandv_waitq);
-       hd->scandv_wait_done = 0;
        hd->last_queue_full = 0;
        hd->spi_pending = 0;
 
@@ -1514,7 +1519,7 @@ mptspi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
         * issue internal bus reset
         */
        if (ioc->spi_data.bus_reset)
-               mptscsih_TMHandler(hd,
+               mptscsih_IssueTaskMgmt(hd,
                    MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS,
                    0, 0, 0, 0, 5);
 
index 214a92d1ef75938fd58f9adea4289074f5e11121..f3c4a3b910bb0d1c16620695662b86136a6cd061 100644 (file)
@@ -2264,6 +2264,17 @@ config BNX2
          To compile this driver as a module, choose M here: the module
          will be called bnx2.  This is recommended.
 
+config CNIC
+       tristate "Broadcom CNIC support"
+       depends on BNX2
+       depends on UIO
+       help
+         This driver supports offload features of Broadcom NetXtremeII
+         gigabit Ethernet cards.
+
+         To compile this driver as a module, choose M here: the module
+         will be called cnic.  This is recommended.
+
 config SPIDER_NET
        tristate "Spider Gigabit Ethernet driver"
        depends on PCI && (PPC_IBM_CELL_BLADE || PPC_CELLEB)
index a1c25cb4669fb8b9a95b7a1410355423d46b5599..db30ebd7b262336bb3949da5db58e7173d4bfa64 100644 (file)
@@ -73,6 +73,7 @@ obj-$(CONFIG_STNIC) += stnic.o 8390.o
 obj-$(CONFIG_FEALNX) += fealnx.o
 obj-$(CONFIG_TIGON3) += tg3.o
 obj-$(CONFIG_BNX2) += bnx2.o
+obj-$(CONFIG_CNIC) += cnic.o
 obj-$(CONFIG_BNX2X) += bnx2x.o
 bnx2x-objs := bnx2x_main.o bnx2x_link.o
 spidernet-y += spider_net.o spider_net_ethtool.o
index b0cb29d4cc01e735b7855e99f7e3a7c81650d3db..3f5fcb0156a180a49977f200b7a16fe5e19f6a38 100644 (file)
 #include <linux/firmware.h>
 #include <linux/log2.h>
 
+#if defined(CONFIG_CNIC) || defined(CONFIG_CNIC_MODULE)
+#define BCM_CNIC 1
+#include "cnic_if.h"
+#endif
 #include "bnx2.h"
 #include "bnx2_fw.h"
 
@@ -315,6 +319,158 @@ bnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val)
        spin_unlock_bh(&bp->indirect_lock);
 }
 
+#ifdef BCM_CNIC
+static int
+bnx2_drv_ctl(struct net_device *dev, struct drv_ctl_info *info)
+{
+       struct bnx2 *bp = netdev_priv(dev);
+       struct drv_ctl_io *io = &info->data.io;
+
+       switch (info->cmd) {
+       case DRV_CTL_IO_WR_CMD:
+               bnx2_reg_wr_ind(bp, io->offset, io->data);
+               break;
+       case DRV_CTL_IO_RD_CMD:
+               io->data = bnx2_reg_rd_ind(bp, io->offset);
+               break;
+       case DRV_CTL_CTX_WR_CMD:
+               bnx2_ctx_wr(bp, io->cid_addr, io->offset, io->data);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static void bnx2_setup_cnic_irq_info(struct bnx2 *bp)
+{
+       struct cnic_eth_dev *cp = &bp->cnic_eth_dev;
+       struct bnx2_napi *bnapi = &bp->bnx2_napi[0];
+       int sb_id;
+
+       if (bp->flags & BNX2_FLAG_USING_MSIX) {
+               cp->drv_state |= CNIC_DRV_STATE_USING_MSIX;
+               bnapi->cnic_present = 0;
+               sb_id = bp->irq_nvecs;
+               cp->irq_arr[0].irq_flags |= CNIC_IRQ_FL_MSIX;
+       } else {
+               cp->drv_state &= ~CNIC_DRV_STATE_USING_MSIX;
+               bnapi->cnic_tag = bnapi->last_status_idx;
+               bnapi->cnic_present = 1;
+               sb_id = 0;
+               cp->irq_arr[0].irq_flags &= ~CNIC_IRQ_FL_MSIX;
+       }
+
+       cp->irq_arr[0].vector = bp->irq_tbl[sb_id].vector;
+       cp->irq_arr[0].status_blk = (void *)
+               ((unsigned long) bnapi->status_blk.msi +
+               (BNX2_SBLK_MSIX_ALIGN_SIZE * sb_id));
+       cp->irq_arr[0].status_blk_num = sb_id;
+       cp->num_irq = 1;
+}
+
+static int bnx2_register_cnic(struct net_device *dev, struct cnic_ops *ops,
+                             void *data)
+{
+       struct bnx2 *bp = netdev_priv(dev);
+       struct cnic_eth_dev *cp = &bp->cnic_eth_dev;
+
+       if (ops == NULL)
+               return -EINVAL;
+
+       if (cp->drv_state & CNIC_DRV_STATE_REGD)
+               return -EBUSY;
+
+       bp->cnic_data = data;
+       rcu_assign_pointer(bp->cnic_ops, ops);
+
+       cp->num_irq = 0;
+       cp->drv_state = CNIC_DRV_STATE_REGD;
+
+       bnx2_setup_cnic_irq_info(bp);
+
+       return 0;
+}
+
+static int bnx2_unregister_cnic(struct net_device *dev)
+{
+       struct bnx2 *bp = netdev_priv(dev);
+       struct bnx2_napi *bnapi = &bp->bnx2_napi[0];
+       struct cnic_eth_dev *cp = &bp->cnic_eth_dev;
+
+       cp->drv_state = 0;
+       bnapi->cnic_present = 0;
+       rcu_assign_pointer(bp->cnic_ops, NULL);
+       synchronize_rcu();
+       return 0;
+}
+
+struct cnic_eth_dev *bnx2_cnic_probe(struct net_device *dev)
+{
+       struct bnx2 *bp = netdev_priv(dev);
+       struct cnic_eth_dev *cp = &bp->cnic_eth_dev;
+
+       cp->drv_owner = THIS_MODULE;
+       cp->chip_id = bp->chip_id;
+       cp->pdev = bp->pdev;
+       cp->io_base = bp->regview;
+       cp->drv_ctl = bnx2_drv_ctl;
+       cp->drv_register_cnic = bnx2_register_cnic;
+       cp->drv_unregister_cnic = bnx2_unregister_cnic;
+
+       return cp;
+}
+EXPORT_SYMBOL(bnx2_cnic_probe);
+
+static void
+bnx2_cnic_stop(struct bnx2 *bp)
+{
+       struct cnic_ops *c_ops;
+       struct cnic_ctl_info info;
+
+       rcu_read_lock();
+       c_ops = rcu_dereference(bp->cnic_ops);
+       if (c_ops) {
+               info.cmd = CNIC_CTL_STOP_CMD;
+               c_ops->cnic_ctl(bp->cnic_data, &info);
+       }
+       rcu_read_unlock();
+}
+
+static void
+bnx2_cnic_start(struct bnx2 *bp)
+{
+       struct cnic_ops *c_ops;
+       struct cnic_ctl_info info;
+
+       rcu_read_lock();
+       c_ops = rcu_dereference(bp->cnic_ops);
+       if (c_ops) {
+               if (!(bp->flags & BNX2_FLAG_USING_MSIX)) {
+                       struct bnx2_napi *bnapi = &bp->bnx2_napi[0];
+
+                       bnapi->cnic_tag = bnapi->last_status_idx;
+               }
+               info.cmd = CNIC_CTL_START_CMD;
+               c_ops->cnic_ctl(bp->cnic_data, &info);
+       }
+       rcu_read_unlock();
+}
+
+#else
+
+static void
+bnx2_cnic_stop(struct bnx2 *bp)
+{
+}
+
+static void
+bnx2_cnic_start(struct bnx2 *bp)
+{
+}
+
+#endif
+
 static int
 bnx2_read_phy(struct bnx2 *bp, u32 reg, u32 *val)
 {
@@ -488,6 +644,7 @@ bnx2_napi_enable(struct bnx2 *bp)
 static void
 bnx2_netif_stop(struct bnx2 *bp)
 {
+       bnx2_cnic_stop(bp);
        bnx2_disable_int_sync(bp);
        if (netif_running(bp->dev)) {
                bnx2_napi_disable(bp);
@@ -504,6 +661,7 @@ bnx2_netif_start(struct bnx2 *bp)
                        netif_tx_wake_all_queues(bp->dev);
                        bnx2_napi_enable(bp);
                        bnx2_enable_int(bp);
+                       bnx2_cnic_start(bp);
                }
        }
 }
@@ -3164,6 +3322,11 @@ bnx2_has_work(struct bnx2_napi *bnapi)
        if (bnx2_has_fast_work(bnapi))
                return 1;
 
+#ifdef BCM_CNIC
+       if (bnapi->cnic_present && (bnapi->cnic_tag != sblk->status_idx))
+               return 1;
+#endif
+
        if ((sblk->status_attn_bits & STATUS_ATTN_EVENTS) !=
            (sblk->status_attn_bits_ack & STATUS_ATTN_EVENTS))
                return 1;
@@ -3193,6 +3356,23 @@ bnx2_chk_missed_msi(struct bnx2 *bp)
        bp->idle_chk_status_idx = bnapi->last_status_idx;
 }
 
+#ifdef BCM_CNIC
+static void bnx2_poll_cnic(struct bnx2 *bp, struct bnx2_napi *bnapi)
+{
+       struct cnic_ops *c_ops;
+
+       if (!bnapi->cnic_present)
+               return;
+
+       rcu_read_lock();
+       c_ops = rcu_dereference(bp->cnic_ops);
+       if (c_ops)
+               bnapi->cnic_tag = c_ops->cnic_handler(bp->cnic_data,
+                                                     bnapi->status_blk.msi);
+       rcu_read_unlock();
+}
+#endif
+
 static void bnx2_poll_link(struct bnx2 *bp, struct bnx2_napi *bnapi)
 {
        struct status_block *sblk = bnapi->status_blk.msi;
@@ -3267,6 +3447,10 @@ static int bnx2_poll(struct napi_struct *napi, int budget)
 
                work_done = bnx2_poll_work(bp, bnapi, work_done, budget);
 
+#ifdef BCM_CNIC
+               bnx2_poll_cnic(bp, bnapi);
+#endif
+
                /* bnapi->last_status_idx is used below to tell the hw how
                 * much work has been processed, so we must read it before
                 * checking for more work.
@@ -4632,8 +4816,11 @@ bnx2_init_chip(struct bnx2 *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;
-       if (CHIP_ID(bp) == CHIP_ID_5709_A0 || CHIP_ID(bp) == CHIP_ID_5709_A1)
-               val |= BNX2_MQ_CONFIG_HALT_DIS;
+       if (CHIP_NUM(bp) == CHIP_NUM_5709) {
+               val |= BNX2_MQ_CONFIG_BIN_MQ_MODE;
+               if (CHIP_REV(bp) == CHIP_REV_Ax)
+                       val |= BNX2_MQ_CONFIG_HALT_DIS;
+       }
 
        REG_WR(bp, BNX2_MQ_CONFIG, val);
 
@@ -7471,7 +7658,7 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev)
        INIT_WORK(&bp->reset_task, bnx2_reset_task);
 
        dev->base_addr = dev->mem_start = pci_resource_start(pdev, 0);
-       mem_len = MB_GET_CID_ADDR(TX_TSS_CID + TX_MAX_TSS_RINGS);
+       mem_len = MB_GET_CID_ADDR(TX_TSS_CID + TX_MAX_TSS_RINGS + 1);
        dev->mem_end = dev->mem_start + mem_len;
        dev->irq = pdev->irq;
 
index 5b570e17c839a2d35db6fba9f27da71a1efaf41e..a1ff739bc9b5e5709f6e3093bf516ace7d148515 100644 (file)
@@ -361,6 +361,9 @@ struct l2_fhdr {
 #define BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE_VALUE       (1<<28)
 
 #define BNX2_L2CTX_HOST_BDIDX                          0x00000004
+#define BNX2_L2CTX_STATUSB_NUM_SHIFT                    16
+#define BNX2_L2CTX_STATUSB_NUM(sb_id)                   \
+       (((sb_id) > 0) ? (((sb_id) + 7) << BNX2_L2CTX_STATUSB_NUM_SHIFT) : 0)
 #define BNX2_L2CTX_HOST_BSEQ                           0x00000008
 #define BNX2_L2CTX_NX_BSEQ                             0x0000000c
 #define BNX2_L2CTX_NX_BDHADDR_HI                       0x00000010
@@ -5900,6 +5903,7 @@ struct l2_fhdr {
 #define BNX2_RXP_FTQ_CTL_CUR_DEPTH                      (0x3ffL<<22)
 
 #define BNX2_RXP_SCRATCH                               0x000e0000
+#define BNX2_RXP_SCRATCH_RXP_FLOOD                      0x000e0024
 #define BNX2_RXP_SCRATCH_RSS_TBL_SZ                     0x000e0038
 #define BNX2_RXP_SCRATCH_RSS_TBL                        0x000e003c
 #define BNX2_RXP_SCRATCH_RSS_TBL_MAX_ENTRIES            128
@@ -6678,6 +6682,11 @@ struct bnx2_napi {
        u32                     last_status_idx;
        u32                     int_num;
 
+#ifdef BCM_CNIC
+       u32                     cnic_tag;
+       int                     cnic_present;
+#endif
+
        struct bnx2_rx_ring_info        rx_ring;
        struct bnx2_tx_ring_info        tx_ring;
 };
@@ -6727,6 +6736,11 @@ struct bnx2 {
        int             tx_ring_size;
        u32             tx_wake_thresh;
 
+#ifdef BCM_CNIC
+       struct cnic_ops         *cnic_ops;
+       void                    *cnic_data;
+#endif
+
        /* End of fields used in the performance code paths. */
 
        unsigned int            current_interval;
@@ -6885,6 +6899,10 @@ struct bnx2 {
 
        u32                     idle_chk_status_idx;
 
+#ifdef BCM_CNIC
+       struct cnic_eth_dev     cnic_eth_dev;
+#endif
+
        const struct firmware   *mips_firmware;
        const struct firmware   *rv2p_firmware;
 };
diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c
new file mode 100644 (file)
index 0000000..8d74037
--- /dev/null
@@ -0,0 +1,2711 @@
+/* cnic.c: Broadcom CNIC core network driver.
+ *
+ * Copyright (c) 2006-2009 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.
+ *
+ * Original skeleton written by: John(Zongxi) Chen (zongxi@broadcom.com)
+ * Modified and maintained by: Michael Chan <mchan@broadcom.com>
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/uio_driver.h>
+#include <linux/in.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+#define BCM_VLAN 1
+#endif
+#include <net/ip.h>
+#include <net/tcp.h>
+#include <net/route.h>
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+#include <scsi/iscsi_if.h>
+
+#include "cnic_if.h"
+#include "bnx2.h"
+#include "cnic.h"
+#include "cnic_defs.h"
+
+#define DRV_MODULE_NAME                "cnic"
+#define PFX DRV_MODULE_NAME    ": "
+
+static char version[] __devinitdata =
+       "Broadcom NetXtreme II CNIC Driver " DRV_MODULE_NAME " v" CNIC_MODULE_VERSION " (" CNIC_MODULE_RELDATE ")\n";
+
+MODULE_AUTHOR("Michael Chan <mchan@broadcom.com> and John(Zongxi) "
+             "Chen (zongxi@broadcom.com");
+MODULE_DESCRIPTION("Broadcom NetXtreme II CNIC Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CNIC_MODULE_VERSION);
+
+static LIST_HEAD(cnic_dev_list);
+static DEFINE_RWLOCK(cnic_dev_lock);
+static DEFINE_MUTEX(cnic_lock);
+
+static struct cnic_ulp_ops *cnic_ulp_tbl[MAX_CNIC_ULP_TYPE];
+
+static int cnic_service_bnx2(void *, void *);
+static int cnic_ctl(void *, struct cnic_ctl_info *);
+
+static struct cnic_ops cnic_bnx2_ops = {
+       .cnic_owner     = THIS_MODULE,
+       .cnic_handler   = cnic_service_bnx2,
+       .cnic_ctl       = cnic_ctl,
+};
+
+static void cnic_shutdown_bnx2_rx_ring(struct cnic_dev *);
+static void cnic_init_bnx2_tx_ring(struct cnic_dev *);
+static void cnic_init_bnx2_rx_ring(struct cnic_dev *);
+static int cnic_cm_set_pg(struct cnic_sock *);
+
+static int cnic_uio_open(struct uio_info *uinfo, struct inode *inode)
+{
+       struct cnic_dev *dev = uinfo->priv;
+       struct cnic_local *cp = dev->cnic_priv;
+
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       if (cp->uio_dev != -1)
+               return -EBUSY;
+
+       cp->uio_dev = iminor(inode);
+
+       cnic_shutdown_bnx2_rx_ring(dev);
+
+       cnic_init_bnx2_tx_ring(dev);
+       cnic_init_bnx2_rx_ring(dev);
+
+       return 0;
+}
+
+static int cnic_uio_close(struct uio_info *uinfo, struct inode *inode)
+{
+       struct cnic_dev *dev = uinfo->priv;
+       struct cnic_local *cp = dev->cnic_priv;
+
+       cp->uio_dev = -1;
+       return 0;
+}
+
+static inline void cnic_hold(struct cnic_dev *dev)
+{
+       atomic_inc(&dev->ref_count);
+}
+
+static inline void cnic_put(struct cnic_dev *dev)
+{
+       atomic_dec(&dev->ref_count);
+}
+
+static inline void csk_hold(struct cnic_sock *csk)
+{
+       atomic_inc(&csk->ref_count);
+}
+
+static inline void csk_put(struct cnic_sock *csk)
+{
+       atomic_dec(&csk->ref_count);
+}
+
+static struct cnic_dev *cnic_from_netdev(struct net_device *netdev)
+{
+       struct cnic_dev *cdev;
+
+       read_lock(&cnic_dev_lock);
+       list_for_each_entry(cdev, &cnic_dev_list, list) {
+               if (netdev == cdev->netdev) {
+                       cnic_hold(cdev);
+                       read_unlock(&cnic_dev_lock);
+                       return cdev;
+               }
+       }
+       read_unlock(&cnic_dev_lock);
+       return NULL;
+}
+
+static void cnic_ctx_wr(struct cnic_dev *dev, u32 cid_addr, u32 off, u32 val)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       struct cnic_eth_dev *ethdev = cp->ethdev;
+       struct drv_ctl_info info;
+       struct drv_ctl_io *io = &info.data.io;
+
+       info.cmd = DRV_CTL_CTX_WR_CMD;
+       io->cid_addr = cid_addr;
+       io->offset = off;
+       io->data = val;
+       ethdev->drv_ctl(dev->netdev, &info);
+}
+
+static void cnic_reg_wr_ind(struct cnic_dev *dev, u32 off, u32 val)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       struct cnic_eth_dev *ethdev = cp->ethdev;
+       struct drv_ctl_info info;
+       struct drv_ctl_io *io = &info.data.io;
+
+       info.cmd = DRV_CTL_IO_WR_CMD;
+       io->offset = off;
+       io->data = val;
+       ethdev->drv_ctl(dev->netdev, &info);
+}
+
+static u32 cnic_reg_rd_ind(struct cnic_dev *dev, u32 off)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       struct cnic_eth_dev *ethdev = cp->ethdev;
+       struct drv_ctl_info info;
+       struct drv_ctl_io *io = &info.data.io;
+
+       info.cmd = DRV_CTL_IO_RD_CMD;
+       io->offset = off;
+       ethdev->drv_ctl(dev->netdev, &info);
+       return io->data;
+}
+
+static int cnic_in_use(struct cnic_sock *csk)
+{
+       return test_bit(SK_F_INUSE, &csk->flags);
+}
+
+static void cnic_kwq_completion(struct cnic_dev *dev, u32 count)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       struct cnic_eth_dev *ethdev = cp->ethdev;
+       struct drv_ctl_info info;
+
+       info.cmd = DRV_CTL_COMPLETION_CMD;
+       info.data.comp.comp_count = count;
+       ethdev->drv_ctl(dev->netdev, &info);
+}
+
+static int cnic_send_nlmsg(struct cnic_local *cp, u32 type,
+                          struct cnic_sock *csk)
+{
+       struct iscsi_path path_req;
+       char *buf = NULL;
+       u16 len = 0;
+       u32 msg_type = ISCSI_KEVENT_IF_DOWN;
+       struct cnic_ulp_ops *ulp_ops;
+
+       if (cp->uio_dev == -1)
+               return -ENODEV;
+
+       if (csk) {
+               len = sizeof(path_req);
+               buf = (char *) &path_req;
+               memset(&path_req, 0, len);
+
+               msg_type = ISCSI_KEVENT_PATH_REQ;
+               path_req.handle = (u64) csk->l5_cid;
+               if (test_bit(SK_F_IPV6, &csk->flags)) {
+                       memcpy(&path_req.dst.v6_addr, &csk->dst_ip[0],
+                              sizeof(struct in6_addr));
+                       path_req.ip_addr_len = 16;
+               } else {
+                       memcpy(&path_req.dst.v4_addr, &csk->dst_ip[0],
+                              sizeof(struct in_addr));
+                       path_req.ip_addr_len = 4;
+               }
+               path_req.vlan_id = csk->vlan_id;
+               path_req.pmtu = csk->mtu;
+       }
+
+       rcu_read_lock();
+       ulp_ops = rcu_dereference(cp->ulp_ops[CNIC_ULP_ISCSI]);
+       if (ulp_ops)
+               ulp_ops->iscsi_nl_send_msg(cp->dev, msg_type, buf, len);
+       rcu_read_unlock();
+       return 0;
+}
+
+static int cnic_iscsi_nl_msg_recv(struct cnic_dev *dev, u32 msg_type,
+                                 char *buf, u16 len)
+{
+       int rc = -EINVAL;
+
+       switch (msg_type) {
+       case ISCSI_UEVENT_PATH_UPDATE: {
+               struct cnic_local *cp;
+               u32 l5_cid;
+               struct cnic_sock *csk;
+               struct iscsi_path *path_resp;
+
+               if (len < sizeof(*path_resp))
+                       break;
+
+               path_resp = (struct iscsi_path *) buf;
+               cp = dev->cnic_priv;
+               l5_cid = (u32) path_resp->handle;
+               if (l5_cid >= MAX_CM_SK_TBL_SZ)
+                       break;
+
+               csk = &cp->csk_tbl[l5_cid];
+               csk_hold(csk);
+               if (cnic_in_use(csk)) {
+                       memcpy(csk->ha, path_resp->mac_addr, 6);
+                       if (test_bit(SK_F_IPV6, &csk->flags))
+                               memcpy(&csk->src_ip[0], &path_resp->src.v6_addr,
+                                      sizeof(struct in6_addr));
+                       else
+                               memcpy(&csk->src_ip[0], &path_resp->src.v4_addr,
+                                      sizeof(struct in_addr));
+                       if (is_valid_ether_addr(csk->ha))
+                               cnic_cm_set_pg(csk);
+               }
+               csk_put(csk);
+               rc = 0;
+       }
+       }
+
+       return rc;
+}
+
+static int cnic_offld_prep(struct cnic_sock *csk)
+{
+       if (test_and_set_bit(SK_F_OFFLD_SCHED, &csk->flags))
+               return 0;
+
+       if (!test_bit(SK_F_CONNECT_START, &csk->flags)) {
+               clear_bit(SK_F_OFFLD_SCHED, &csk->flags);
+               return 0;
+       }
+
+       return 1;
+}
+
+static int cnic_close_prep(struct cnic_sock *csk)
+{
+       clear_bit(SK_F_CONNECT_START, &csk->flags);
+       smp_mb__after_clear_bit();
+
+       if (test_and_clear_bit(SK_F_OFFLD_COMPLETE, &csk->flags)) {
+               while (test_and_set_bit(SK_F_OFFLD_SCHED, &csk->flags))
+                       msleep(1);
+
+               return 1;
+       }
+       return 0;
+}
+
+static int cnic_abort_prep(struct cnic_sock *csk)
+{
+       clear_bit(SK_F_CONNECT_START, &csk->flags);
+       smp_mb__after_clear_bit();
+
+       while (test_and_set_bit(SK_F_OFFLD_SCHED, &csk->flags))
+               msleep(1);
+
+       if (test_and_clear_bit(SK_F_OFFLD_COMPLETE, &csk->flags)) {
+               csk->state = L4_KCQE_OPCODE_VALUE_RESET_COMP;
+               return 1;
+       }
+
+       return 0;
+}
+
+int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops)
+{
+       struct cnic_dev *dev;
+
+       if (ulp_type >= MAX_CNIC_ULP_TYPE) {
+               printk(KERN_ERR PFX "cnic_register_driver: Bad type %d\n",
+                      ulp_type);
+               return -EINVAL;
+       }
+       mutex_lock(&cnic_lock);
+       if (cnic_ulp_tbl[ulp_type]) {
+               printk(KERN_ERR PFX "cnic_register_driver: Type %d has already "
+                                   "been registered\n", ulp_type);
+               mutex_unlock(&cnic_lock);
+               return -EBUSY;
+       }
+
+       read_lock(&cnic_dev_lock);
+       list_for_each_entry(dev, &cnic_dev_list, list) {
+               struct cnic_local *cp = dev->cnic_priv;
+
+               clear_bit(ULP_F_INIT, &cp->ulp_flags[ulp_type]);
+       }
+       read_unlock(&cnic_dev_lock);
+
+       rcu_assign_pointer(cnic_ulp_tbl[ulp_type], ulp_ops);
+       mutex_unlock(&cnic_lock);
+
+       /* Prevent race conditions with netdev_event */
+       rtnl_lock();
+       read_lock(&cnic_dev_lock);
+       list_for_each_entry(dev, &cnic_dev_list, list) {
+               struct cnic_local *cp = dev->cnic_priv;
+
+               if (!test_and_set_bit(ULP_F_INIT, &cp->ulp_flags[ulp_type]))
+                       ulp_ops->cnic_init(dev);
+       }
+       read_unlock(&cnic_dev_lock);
+       rtnl_unlock();
+
+       return 0;
+}
+
+int cnic_unregister_driver(int ulp_type)
+{
+       struct cnic_dev *dev;
+
+       if (ulp_type >= MAX_CNIC_ULP_TYPE) {
+               printk(KERN_ERR PFX "cnic_unregister_driver: Bad type %d\n",
+                      ulp_type);
+               return -EINVAL;
+       }
+       mutex_lock(&cnic_lock);
+       if (!cnic_ulp_tbl[ulp_type]) {
+               printk(KERN_ERR PFX "cnic_unregister_driver: Type %d has not "
+                                   "been registered\n", ulp_type);
+               goto out_unlock;
+       }
+       read_lock(&cnic_dev_lock);
+       list_for_each_entry(dev, &cnic_dev_list, list) {
+               struct cnic_local *cp = dev->cnic_priv;
+
+               if (rcu_dereference(cp->ulp_ops[ulp_type])) {
+                       printk(KERN_ERR PFX "cnic_unregister_driver: Type %d "
+                              "still has devices registered\n", ulp_type);
+                       read_unlock(&cnic_dev_lock);
+                       goto out_unlock;
+               }
+       }
+       read_unlock(&cnic_dev_lock);
+
+       rcu_assign_pointer(cnic_ulp_tbl[ulp_type], NULL);
+
+       mutex_unlock(&cnic_lock);
+       synchronize_rcu();
+       return 0;
+
+out_unlock:
+       mutex_unlock(&cnic_lock);
+       return -EINVAL;
+}
+
+static int cnic_start_hw(struct cnic_dev *);
+static void cnic_stop_hw(struct cnic_dev *);
+
+static int cnic_register_device(struct cnic_dev *dev, int ulp_type,
+                               void *ulp_ctx)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       struct cnic_ulp_ops *ulp_ops;
+
+       if (ulp_type >= MAX_CNIC_ULP_TYPE) {
+               printk(KERN_ERR PFX "cnic_register_device: Bad type %d\n",
+                      ulp_type);
+               return -EINVAL;
+       }
+       mutex_lock(&cnic_lock);
+       if (cnic_ulp_tbl[ulp_type] == NULL) {
+               printk(KERN_ERR PFX "cnic_register_device: Driver with type %d "
+                                   "has not been registered\n", ulp_type);
+               mutex_unlock(&cnic_lock);
+               return -EAGAIN;
+       }
+       if (rcu_dereference(cp->ulp_ops[ulp_type])) {
+               printk(KERN_ERR PFX "cnic_register_device: Type %d has already "
+                      "been registered to this device\n", ulp_type);
+               mutex_unlock(&cnic_lock);
+               return -EBUSY;
+       }
+
+       clear_bit(ULP_F_START, &cp->ulp_flags[ulp_type]);
+       cp->ulp_handle[ulp_type] = ulp_ctx;
+       ulp_ops = cnic_ulp_tbl[ulp_type];
+       rcu_assign_pointer(cp->ulp_ops[ulp_type], ulp_ops);
+       cnic_hold(dev);
+
+       if (test_bit(CNIC_F_CNIC_UP, &dev->flags))
+               if (!test_and_set_bit(ULP_F_START, &cp->ulp_flags[ulp_type]))
+                       ulp_ops->cnic_start(cp->ulp_handle[ulp_type]);
+
+       mutex_unlock(&cnic_lock);
+
+       return 0;
+
+}
+EXPORT_SYMBOL(cnic_register_driver);
+
+static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+
+       if (ulp_type >= MAX_CNIC_ULP_TYPE) {
+               printk(KERN_ERR PFX "cnic_unregister_device: Bad type %d\n",
+                      ulp_type);
+               return -EINVAL;
+       }
+       mutex_lock(&cnic_lock);
+       if (rcu_dereference(cp->ulp_ops[ulp_type])) {
+               rcu_assign_pointer(cp->ulp_ops[ulp_type], NULL);
+               cnic_put(dev);
+       } else {
+               printk(KERN_ERR PFX "cnic_unregister_device: device not "
+                      "registered to this ulp type %d\n", ulp_type);
+               mutex_unlock(&cnic_lock);
+               return -EINVAL;
+       }
+       mutex_unlock(&cnic_lock);
+
+       synchronize_rcu();
+
+       return 0;
+}
+EXPORT_SYMBOL(cnic_unregister_driver);
+
+static int cnic_init_id_tbl(struct cnic_id_tbl *id_tbl, u32 size, u32 start_id)
+{
+       id_tbl->start = start_id;
+       id_tbl->max = size;
+       id_tbl->next = 0;
+       spin_lock_init(&id_tbl->lock);
+       id_tbl->table = kzalloc(DIV_ROUND_UP(size, 32) * 4, GFP_KERNEL);
+       if (!id_tbl->table)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void cnic_free_id_tbl(struct cnic_id_tbl *id_tbl)
+{
+       kfree(id_tbl->table);
+       id_tbl->table = NULL;
+}
+
+static int cnic_alloc_id(struct cnic_id_tbl *id_tbl, u32 id)
+{
+       int ret = -1;
+
+       id -= id_tbl->start;
+       if (id >= id_tbl->max)
+               return ret;
+
+       spin_lock(&id_tbl->lock);
+       if (!test_bit(id, id_tbl->table)) {
+               set_bit(id, id_tbl->table);
+               ret = 0;
+       }
+       spin_unlock(&id_tbl->lock);
+       return ret;
+}
+
+/* Returns -1 if not successful */
+static u32 cnic_alloc_new_id(struct cnic_id_tbl *id_tbl)
+{
+       u32 id;
+
+       spin_lock(&id_tbl->lock);
+       id = find_next_zero_bit(id_tbl->table, id_tbl->max, id_tbl->next);
+       if (id >= id_tbl->max) {
+               id = -1;
+               if (id_tbl->next != 0) {
+                       id = find_first_zero_bit(id_tbl->table, id_tbl->next);
+                       if (id >= id_tbl->next)
+                               id = -1;
+               }
+       }
+
+       if (id < id_tbl->max) {
+               set_bit(id, id_tbl->table);
+               id_tbl->next = (id + 1) & (id_tbl->max - 1);
+               id += id_tbl->start;
+       }
+
+       spin_unlock(&id_tbl->lock);
+
+       return id;
+}
+
+static void cnic_free_id(struct cnic_id_tbl *id_tbl, u32 id)
+{
+       if (id == -1)
+               return;
+
+       id -= id_tbl->start;
+       if (id >= id_tbl->max)
+               return;
+
+       clear_bit(id, id_tbl->table);
+}
+
+static void cnic_free_dma(struct cnic_dev *dev, struct cnic_dma *dma)
+{
+       int i;
+
+       if (!dma->pg_arr)
+               return;
+
+       for (i = 0; i < dma->num_pages; i++) {
+               if (dma->pg_arr[i]) {
+                       pci_free_consistent(dev->pcidev, BCM_PAGE_SIZE,
+                                           dma->pg_arr[i], dma->pg_map_arr[i]);
+                       dma->pg_arr[i] = NULL;
+               }
+       }
+       if (dma->pgtbl) {
+               pci_free_consistent(dev->pcidev, dma->pgtbl_size,
+                                   dma->pgtbl, dma->pgtbl_map);
+               dma->pgtbl = NULL;
+       }
+       kfree(dma->pg_arr);
+       dma->pg_arr = NULL;
+       dma->num_pages = 0;
+}
+
+static void cnic_setup_page_tbl(struct cnic_dev *dev, struct cnic_dma *dma)
+{
+       int i;
+       u32 *page_table = dma->pgtbl;
+
+       for (i = 0; i < dma->num_pages; i++) {
+               /* Each entry needs to be in big endian format. */
+               *page_table = (u32) ((u64) dma->pg_map_arr[i] >> 32);
+               page_table++;
+               *page_table = (u32) dma->pg_map_arr[i];
+               page_table++;
+       }
+}
+
+static int cnic_alloc_dma(struct cnic_dev *dev, struct cnic_dma *dma,
+                         int pages, int use_pg_tbl)
+{
+       int i, size;
+       struct cnic_local *cp = dev->cnic_priv;
+
+       size = pages * (sizeof(void *) + sizeof(dma_addr_t));
+       dma->pg_arr = kzalloc(size, GFP_ATOMIC);
+       if (dma->pg_arr == NULL)
+               return -ENOMEM;
+
+       dma->pg_map_arr = (dma_addr_t *) (dma->pg_arr + pages);
+       dma->num_pages = pages;
+
+       for (i = 0; i < pages; i++) {
+               dma->pg_arr[i] = pci_alloc_consistent(dev->pcidev,
+                                                     BCM_PAGE_SIZE,
+                                                     &dma->pg_map_arr[i]);
+               if (dma->pg_arr[i] == NULL)
+                       goto error;
+       }
+       if (!use_pg_tbl)
+               return 0;
+
+       dma->pgtbl_size = ((pages * 8) + BCM_PAGE_SIZE - 1) &
+                         ~(BCM_PAGE_SIZE - 1);
+       dma->pgtbl = pci_alloc_consistent(dev->pcidev, dma->pgtbl_size,
+                                         &dma->pgtbl_map);
+       if (dma->pgtbl == NULL)
+               goto error;
+
+       cp->setup_pgtbl(dev, dma);
+
+       return 0;
+
+error:
+       cnic_free_dma(dev, dma);
+       return -ENOMEM;
+}
+
+static void cnic_free_resc(struct cnic_dev *dev)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       int i = 0;
+
+       if (cp->cnic_uinfo) {
+               cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL);
+               while (cp->uio_dev != -1 && i < 15) {
+                       msleep(100);
+                       i++;
+               }
+               uio_unregister_device(cp->cnic_uinfo);
+               kfree(cp->cnic_uinfo);
+               cp->cnic_uinfo = NULL;
+       }
+
+       if (cp->l2_buf) {
+               pci_free_consistent(dev->pcidev, cp->l2_buf_size,
+                                   cp->l2_buf, cp->l2_buf_map);
+               cp->l2_buf = NULL;
+       }
+
+       if (cp->l2_ring) {
+               pci_free_consistent(dev->pcidev, cp->l2_ring_size,
+                                   cp->l2_ring, cp->l2_ring_map);
+               cp->l2_ring = NULL;
+       }
+
+       for (i = 0; i < cp->ctx_blks; i++) {
+               if (cp->ctx_arr[i].ctx) {
+                       pci_free_consistent(dev->pcidev, cp->ctx_blk_size,
+                                           cp->ctx_arr[i].ctx,
+                                           cp->ctx_arr[i].mapping);
+                       cp->ctx_arr[i].ctx = NULL;
+               }
+       }
+       kfree(cp->ctx_arr);
+       cp->ctx_arr = NULL;
+       cp->ctx_blks = 0;
+
+       cnic_free_dma(dev, &cp->gbl_buf_info);
+       cnic_free_dma(dev, &cp->conn_buf_info);
+       cnic_free_dma(dev, &cp->kwq_info);
+       cnic_free_dma(dev, &cp->kcq_info);
+       kfree(cp->iscsi_tbl);
+       cp->iscsi_tbl = NULL;
+       kfree(cp->ctx_tbl);
+       cp->ctx_tbl = NULL;
+
+       cnic_free_id_tbl(&cp->cid_tbl);
+}
+
+static int cnic_alloc_context(struct cnic_dev *dev)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+
+       if (CHIP_NUM(cp) == CHIP_NUM_5709) {
+               int i, k, arr_size;
+
+               cp->ctx_blk_size = BCM_PAGE_SIZE;
+               cp->cids_per_blk = BCM_PAGE_SIZE / 128;
+               arr_size = BNX2_MAX_CID / cp->cids_per_blk *
+                          sizeof(struct cnic_ctx);
+               cp->ctx_arr = kzalloc(arr_size, GFP_KERNEL);
+               if (cp->ctx_arr == NULL)
+                       return -ENOMEM;
+
+               k = 0;
+               for (i = 0; i < 2; i++) {
+                       u32 j, reg, off, lo, hi;
+
+                       if (i == 0)
+                               off = BNX2_PG_CTX_MAP;
+                       else
+                               off = BNX2_ISCSI_CTX_MAP;
+
+                       reg = cnic_reg_rd_ind(dev, off);
+                       lo = reg >> 16;
+                       hi = reg & 0xffff;
+                       for (j = lo; j < hi; j += cp->cids_per_blk, k++)
+                               cp->ctx_arr[k].cid = j;
+               }
+
+               cp->ctx_blks = k;
+               if (cp->ctx_blks >= (BNX2_MAX_CID / cp->cids_per_blk)) {
+                       cp->ctx_blks = 0;
+                       return -ENOMEM;
+               }
+
+               for (i = 0; i < cp->ctx_blks; i++) {
+                       cp->ctx_arr[i].ctx =
+                               pci_alloc_consistent(dev->pcidev, BCM_PAGE_SIZE,
+                                                    &cp->ctx_arr[i].mapping);
+                       if (cp->ctx_arr[i].ctx == NULL)
+                               return -ENOMEM;
+               }
+       }
+       return 0;
+}
+
+static int cnic_alloc_bnx2_resc(struct cnic_dev *dev)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       struct uio_info *uinfo;
+       int ret;
+
+       ret = cnic_alloc_dma(dev, &cp->kwq_info, KWQ_PAGE_CNT, 1);
+       if (ret)
+               goto error;
+       cp->kwq = (struct kwqe **) cp->kwq_info.pg_arr;
+
+       ret = cnic_alloc_dma(dev, &cp->kcq_info, KCQ_PAGE_CNT, 1);
+       if (ret)
+               goto error;
+       cp->kcq = (struct kcqe **) cp->kcq_info.pg_arr;
+
+       ret = cnic_alloc_context(dev);
+       if (ret)
+               goto error;
+
+       cp->l2_ring_size = 2 * BCM_PAGE_SIZE;
+       cp->l2_ring = pci_alloc_consistent(dev->pcidev, cp->l2_ring_size,
+                                          &cp->l2_ring_map);
+       if (!cp->l2_ring)
+               goto error;
+
+       cp->l2_buf_size = (cp->l2_rx_ring_size + 1) * cp->l2_single_buf_size;
+       cp->l2_buf_size = PAGE_ALIGN(cp->l2_buf_size);
+       cp->l2_buf = pci_alloc_consistent(dev->pcidev, cp->l2_buf_size,
+                                          &cp->l2_buf_map);
+       if (!cp->l2_buf)
+               goto error;
+
+       uinfo = kzalloc(sizeof(*uinfo), GFP_ATOMIC);
+       if (!uinfo)
+               goto error;
+
+       uinfo->mem[0].addr = dev->netdev->base_addr;
+       uinfo->mem[0].internal_addr = dev->regview;
+       uinfo->mem[0].size = dev->netdev->mem_end - dev->netdev->mem_start;
+       uinfo->mem[0].memtype = UIO_MEM_PHYS;
+
+       uinfo->mem[1].addr = (unsigned long) cp->status_blk & PAGE_MASK;
+       if (cp->ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX)
+               uinfo->mem[1].size = BNX2_SBLK_MSIX_ALIGN_SIZE * 9;
+       else
+               uinfo->mem[1].size = BNX2_SBLK_MSIX_ALIGN_SIZE;
+       uinfo->mem[1].memtype = UIO_MEM_LOGICAL;
+
+       uinfo->mem[2].addr = (unsigned long) cp->l2_ring;
+       uinfo->mem[2].size = cp->l2_ring_size;
+       uinfo->mem[2].memtype = UIO_MEM_LOGICAL;
+
+       uinfo->mem[3].addr = (unsigned long) cp->l2_buf;
+       uinfo->mem[3].size = cp->l2_buf_size;
+       uinfo->mem[3].memtype = UIO_MEM_LOGICAL;
+
+       uinfo->name = "bnx2_cnic";
+       uinfo->version = CNIC_MODULE_VERSION;
+       uinfo->irq = UIO_IRQ_CUSTOM;
+
+       uinfo->open = cnic_uio_open;
+       uinfo->release = cnic_uio_close;
+
+       uinfo->priv = dev;
+
+       ret = uio_register_device(&dev->pcidev->dev, uinfo);
+       if (ret) {
+               kfree(uinfo);
+               goto error;
+       }
+
+       cp->cnic_uinfo = uinfo;
+
+       return 0;
+
+error:
+       cnic_free_resc(dev);
+       return ret;
+}
+
+static inline u32 cnic_kwq_avail(struct cnic_local *cp)
+{
+       return cp->max_kwq_idx -
+               ((cp->kwq_prod_idx - cp->kwq_con_idx) & cp->max_kwq_idx);
+}
+
+static int cnic_submit_bnx2_kwqes(struct cnic_dev *dev, struct kwqe *wqes[],
+                                 u32 num_wqes)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       struct kwqe *prod_qe;
+       u16 prod, sw_prod, i;
+
+       if (!test_bit(CNIC_F_CNIC_UP, &dev->flags))
+               return -EAGAIN;         /* bnx2 is down */
+
+       spin_lock_bh(&cp->cnic_ulp_lock);
+       if (num_wqes > cnic_kwq_avail(cp) &&
+           !(cp->cnic_local_flags & CNIC_LCL_FL_KWQ_INIT)) {
+               spin_unlock_bh(&cp->cnic_ulp_lock);
+               return -EAGAIN;
+       }
+
+       cp->cnic_local_flags &= ~CNIC_LCL_FL_KWQ_INIT;
+
+       prod = cp->kwq_prod_idx;
+       sw_prod = prod & MAX_KWQ_IDX;
+       for (i = 0; i < num_wqes; i++) {
+               prod_qe = &cp->kwq[KWQ_PG(sw_prod)][KWQ_IDX(sw_prod)];
+               memcpy(prod_qe, wqes[i], sizeof(struct kwqe));
+               prod++;
+               sw_prod = prod & MAX_KWQ_IDX;
+       }
+       cp->kwq_prod_idx = prod;
+
+       CNIC_WR16(dev, cp->kwq_io_addr, cp->kwq_prod_idx);
+
+       spin_unlock_bh(&cp->cnic_ulp_lock);
+       return 0;
+}
+
+static void service_kcqes(struct cnic_dev *dev, int num_cqes)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       int i, j;
+
+       i = 0;
+       j = 1;
+       while (num_cqes) {
+               struct cnic_ulp_ops *ulp_ops;
+               int ulp_type;
+               u32 kcqe_op_flag = cp->completed_kcq[i]->kcqe_op_flag;
+               u32 kcqe_layer = kcqe_op_flag & KCQE_FLAGS_LAYER_MASK;
+
+               if (unlikely(kcqe_op_flag & KCQE_RAMROD_COMPLETION))
+                       cnic_kwq_completion(dev, 1);
+
+               while (j < num_cqes) {
+                       u32 next_op = cp->completed_kcq[i + j]->kcqe_op_flag;
+
+                       if ((next_op & KCQE_FLAGS_LAYER_MASK) != kcqe_layer)
+                               break;
+
+                       if (unlikely(next_op & KCQE_RAMROD_COMPLETION))
+                               cnic_kwq_completion(dev, 1);
+                       j++;
+               }
+
+               if (kcqe_layer == KCQE_FLAGS_LAYER_MASK_L5_RDMA)
+                       ulp_type = CNIC_ULP_RDMA;
+               else if (kcqe_layer == KCQE_FLAGS_LAYER_MASK_L5_ISCSI)
+                       ulp_type = CNIC_ULP_ISCSI;
+               else if (kcqe_layer == KCQE_FLAGS_LAYER_MASK_L4)
+                       ulp_type = CNIC_ULP_L4;
+               else if (kcqe_layer == KCQE_FLAGS_LAYER_MASK_L2)
+                       goto end;
+               else {
+                       printk(KERN_ERR PFX "%s: Unknown type of KCQE(0x%x)\n",
+                              dev->netdev->name, kcqe_op_flag);
+                       goto end;
+               }
+
+               rcu_read_lock();
+               ulp_ops = rcu_dereference(cp->ulp_ops[ulp_type]);
+               if (likely(ulp_ops)) {
+                       ulp_ops->indicate_kcqes(cp->ulp_handle[ulp_type],
+                                                 cp->completed_kcq + i, j);
+               }
+               rcu_read_unlock();
+end:
+               num_cqes -= j;
+               i += j;
+               j = 1;
+       }
+       return;
+}
+
+static u16 cnic_bnx2_next_idx(u16 idx)
+{
+       return idx + 1;
+}
+
+static u16 cnic_bnx2_hw_idx(u16 idx)
+{
+       return idx;
+}
+
+static int cnic_get_kcqes(struct cnic_dev *dev, u16 hw_prod, u16 *sw_prod)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       u16 i, ri, last;
+       struct kcqe *kcqe;
+       int kcqe_cnt = 0, last_cnt = 0;
+
+       i = ri = last = *sw_prod;
+       ri &= MAX_KCQ_IDX;
+
+       while ((i != hw_prod) && (kcqe_cnt < MAX_COMPLETED_KCQE)) {
+               kcqe = &cp->kcq[KCQ_PG(ri)][KCQ_IDX(ri)];
+               cp->completed_kcq[kcqe_cnt++] = kcqe;
+               i = cp->next_idx(i);
+               ri = i & MAX_KCQ_IDX;
+               if (likely(!(kcqe->kcqe_op_flag & KCQE_FLAGS_NEXT))) {
+                       last_cnt = kcqe_cnt;
+                       last = i;
+               }
+       }
+
+       *sw_prod = last;
+       return last_cnt;
+}
+
+static void cnic_chk_bnx2_pkt_rings(struct cnic_local *cp)
+{
+       u16 rx_cons = *cp->rx_cons_ptr;
+       u16 tx_cons = *cp->tx_cons_ptr;
+
+       if (cp->tx_cons != tx_cons || cp->rx_cons != rx_cons) {
+               cp->tx_cons = tx_cons;
+               cp->rx_cons = rx_cons;
+               uio_event_notify(cp->cnic_uinfo);
+       }
+}
+
+static int cnic_service_bnx2(void *data, void *status_blk)
+{
+       struct cnic_dev *dev = data;
+       struct status_block *sblk = status_blk;
+       struct cnic_local *cp = dev->cnic_priv;
+       u32 status_idx = sblk->status_idx;
+       u16 hw_prod, sw_prod;
+       int kcqe_cnt;
+
+       if (unlikely(!test_bit(CNIC_F_CNIC_UP, &dev->flags)))
+               return status_idx;
+
+       cp->kwq_con_idx = *cp->kwq_con_idx_ptr;
+
+       hw_prod = sblk->status_completion_producer_index;
+       sw_prod = cp->kcq_prod_idx;
+       while (sw_prod != hw_prod) {
+               kcqe_cnt = cnic_get_kcqes(dev, hw_prod, &sw_prod);
+               if (kcqe_cnt == 0)
+                       goto done;
+
+               service_kcqes(dev, kcqe_cnt);
+
+               /* Tell compiler that status_blk fields can change. */
+               barrier();
+               if (status_idx != sblk->status_idx) {
+                       status_idx = sblk->status_idx;
+                       cp->kwq_con_idx = *cp->kwq_con_idx_ptr;
+                       hw_prod = sblk->status_completion_producer_index;
+               } else
+                       break;
+       }
+
+done:
+       CNIC_WR16(dev, cp->kcq_io_addr, sw_prod);
+
+       cp->kcq_prod_idx = sw_prod;
+
+       cnic_chk_bnx2_pkt_rings(cp);
+       return status_idx;
+}
+
+static void cnic_service_bnx2_msix(unsigned long data)
+{
+       struct cnic_dev *dev = (struct cnic_dev *) data;
+       struct cnic_local *cp = dev->cnic_priv;
+       struct status_block_msix *status_blk = cp->bnx2_status_blk;
+       u32 status_idx = status_blk->status_idx;
+       u16 hw_prod, sw_prod;
+       int kcqe_cnt;
+
+       cp->kwq_con_idx = status_blk->status_cmd_consumer_index;
+
+       hw_prod = status_blk->status_completion_producer_index;
+       sw_prod = cp->kcq_prod_idx;
+       while (sw_prod != hw_prod) {
+               kcqe_cnt = cnic_get_kcqes(dev, hw_prod, &sw_prod);
+               if (kcqe_cnt == 0)
+                       goto done;
+
+               service_kcqes(dev, kcqe_cnt);
+
+               /* Tell compiler that status_blk fields can change. */
+               barrier();
+               if (status_idx != status_blk->status_idx) {
+                       status_idx = status_blk->status_idx;
+                       cp->kwq_con_idx = status_blk->status_cmd_consumer_index;
+                       hw_prod = status_blk->status_completion_producer_index;
+               } else
+                       break;
+       }
+
+done:
+       CNIC_WR16(dev, cp->kcq_io_addr, sw_prod);
+       cp->kcq_prod_idx = sw_prod;
+
+       cnic_chk_bnx2_pkt_rings(cp);
+
+       cp->last_status_idx = status_idx;
+       CNIC_WR(dev, BNX2_PCICFG_INT_ACK_CMD, cp->int_num |
+               BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | cp->last_status_idx);
+}
+
+static irqreturn_t cnic_irq(int irq, void *dev_instance)
+{
+       struct cnic_dev *dev = dev_instance;
+       struct cnic_local *cp = dev->cnic_priv;
+       u16 prod = cp->kcq_prod_idx & MAX_KCQ_IDX;
+
+       if (cp->ack_int)
+               cp->ack_int(dev);
+
+       prefetch(cp->status_blk);
+       prefetch(&cp->kcq[KCQ_PG(prod)][KCQ_IDX(prod)]);
+
+       if (likely(test_bit(CNIC_F_CNIC_UP, &dev->flags)))
+               tasklet_schedule(&cp->cnic_irq_task);
+
+       return IRQ_HANDLED;
+}
+
+static void cnic_ulp_stop(struct cnic_dev *dev)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       int if_type;
+
+       rcu_read_lock();
+       for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) {
+               struct cnic_ulp_ops *ulp_ops;
+
+               ulp_ops = rcu_dereference(cp->ulp_ops[if_type]);
+               if (!ulp_ops)
+                       continue;
+
+               if (test_and_clear_bit(ULP_F_START, &cp->ulp_flags[if_type]))
+                       ulp_ops->cnic_stop(cp->ulp_handle[if_type]);
+       }
+       rcu_read_unlock();
+}
+
+static void cnic_ulp_start(struct cnic_dev *dev)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       int if_type;
+
+       rcu_read_lock();
+       for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) {
+               struct cnic_ulp_ops *ulp_ops;
+
+               ulp_ops = rcu_dereference(cp->ulp_ops[if_type]);
+               if (!ulp_ops || !ulp_ops->cnic_start)
+                       continue;
+
+               if (!test_and_set_bit(ULP_F_START, &cp->ulp_flags[if_type]))
+                       ulp_ops->cnic_start(cp->ulp_handle[if_type]);
+       }
+       rcu_read_unlock();
+}
+
+static int cnic_ctl(void *data, struct cnic_ctl_info *info)
+{
+       struct cnic_dev *dev = data;
+
+       switch (info->cmd) {
+       case CNIC_CTL_STOP_CMD:
+               cnic_hold(dev);
+               mutex_lock(&cnic_lock);
+
+               cnic_ulp_stop(dev);
+               cnic_stop_hw(dev);
+
+               mutex_unlock(&cnic_lock);
+               cnic_put(dev);
+               break;
+       case CNIC_CTL_START_CMD:
+               cnic_hold(dev);
+               mutex_lock(&cnic_lock);
+
+               if (!cnic_start_hw(dev))
+                       cnic_ulp_start(dev);
+
+               mutex_unlock(&cnic_lock);
+               cnic_put(dev);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static void cnic_ulp_init(struct cnic_dev *dev)
+{
+       int i;
+       struct cnic_local *cp = dev->cnic_priv;
+
+       rcu_read_lock();
+       for (i = 0; i < MAX_CNIC_ULP_TYPE_EXT; i++) {
+               struct cnic_ulp_ops *ulp_ops;
+
+               ulp_ops = rcu_dereference(cnic_ulp_tbl[i]);
+               if (!ulp_ops || !ulp_ops->cnic_init)
+                       continue;
+
+               if (!test_and_set_bit(ULP_F_INIT, &cp->ulp_flags[i]))
+                       ulp_ops->cnic_init(dev);
+
+       }
+       rcu_read_unlock();
+}
+
+static void cnic_ulp_exit(struct cnic_dev *dev)
+{
+       int i;
+       struct cnic_local *cp = dev->cnic_priv;
+
+       rcu_read_lock();
+       for (i = 0; i < MAX_CNIC_ULP_TYPE_EXT; i++) {
+               struct cnic_ulp_ops *ulp_ops;
+
+               ulp_ops = rcu_dereference(cnic_ulp_tbl[i]);
+               if (!ulp_ops || !ulp_ops->cnic_exit)
+                       continue;
+
+               if (test_and_clear_bit(ULP_F_INIT, &cp->ulp_flags[i]))
+                       ulp_ops->cnic_exit(dev);
+
+       }
+       rcu_read_unlock();
+}
+
+static int cnic_cm_offload_pg(struct cnic_sock *csk)
+{
+       struct cnic_dev *dev = csk->dev;
+       struct l4_kwq_offload_pg *l4kwqe;
+       struct kwqe *wqes[1];
+
+       l4kwqe = (struct l4_kwq_offload_pg *) &csk->kwqe1;
+       memset(l4kwqe, 0, sizeof(*l4kwqe));
+       wqes[0] = (struct kwqe *) l4kwqe;
+
+       l4kwqe->op_code = L4_KWQE_OPCODE_VALUE_OFFLOAD_PG;
+       l4kwqe->flags =
+               L4_LAYER_CODE << L4_KWQ_OFFLOAD_PG_LAYER_CODE_SHIFT;
+       l4kwqe->l2hdr_nbytes = ETH_HLEN;
+
+       l4kwqe->da0 = csk->ha[0];
+       l4kwqe->da1 = csk->ha[1];
+       l4kwqe->da2 = csk->ha[2];
+       l4kwqe->da3 = csk->ha[3];
+       l4kwqe->da4 = csk->ha[4];
+       l4kwqe->da5 = csk->ha[5];
+
+       l4kwqe->sa0 = dev->mac_addr[0];
+       l4kwqe->sa1 = dev->mac_addr[1];
+       l4kwqe->sa2 = dev->mac_addr[2];
+       l4kwqe->sa3 = dev->mac_addr[3];
+       l4kwqe->sa4 = dev->mac_addr[4];
+       l4kwqe->sa5 = dev->mac_addr[5];
+
+       l4kwqe->etype = ETH_P_IP;
+       l4kwqe->ipid_count = DEF_IPID_COUNT;
+       l4kwqe->host_opaque = csk->l5_cid;
+
+       if (csk->vlan_id) {
+               l4kwqe->pg_flags |= L4_KWQ_OFFLOAD_PG_VLAN_TAGGING;
+               l4kwqe->vlan_tag = csk->vlan_id;
+               l4kwqe->l2hdr_nbytes += 4;
+       }
+
+       return dev->submit_kwqes(dev, wqes, 1);
+}
+
+static int cnic_cm_update_pg(struct cnic_sock *csk)
+{
+       struct cnic_dev *dev = csk->dev;
+       struct l4_kwq_update_pg *l4kwqe;
+       struct kwqe *wqes[1];
+
+       l4kwqe = (struct l4_kwq_update_pg *) &csk->kwqe1;
+       memset(l4kwqe, 0, sizeof(*l4kwqe));
+       wqes[0] = (struct kwqe *) l4kwqe;
+
+       l4kwqe->opcode = L4_KWQE_OPCODE_VALUE_UPDATE_PG;
+       l4kwqe->flags =
+               L4_LAYER_CODE << L4_KWQ_UPDATE_PG_LAYER_CODE_SHIFT;
+       l4kwqe->pg_cid = csk->pg_cid;
+
+       l4kwqe->da0 = csk->ha[0];
+       l4kwqe->da1 = csk->ha[1];
+       l4kwqe->da2 = csk->ha[2];
+       l4kwqe->da3 = csk->ha[3];
+       l4kwqe->da4 = csk->ha[4];
+       l4kwqe->da5 = csk->ha[5];
+
+       l4kwqe->pg_host_opaque = csk->l5_cid;
+       l4kwqe->pg_valids = L4_KWQ_UPDATE_PG_VALIDS_DA;
+
+       return dev->submit_kwqes(dev, wqes, 1);
+}
+
+static int cnic_cm_upload_pg(struct cnic_sock *csk)
+{
+       struct cnic_dev *dev = csk->dev;
+       struct l4_kwq_upload *l4kwqe;
+       struct kwqe *wqes[1];
+
+       l4kwqe = (struct l4_kwq_upload *) &csk->kwqe1;
+       memset(l4kwqe, 0, sizeof(*l4kwqe));
+       wqes[0] = (struct kwqe *) l4kwqe;
+
+       l4kwqe->opcode = L4_KWQE_OPCODE_VALUE_UPLOAD_PG;
+       l4kwqe->flags =
+               L4_LAYER_CODE << L4_KWQ_UPLOAD_LAYER_CODE_SHIFT;
+       l4kwqe->cid = csk->pg_cid;
+
+       return dev->submit_kwqes(dev, wqes, 1);
+}
+
+static int cnic_cm_conn_req(struct cnic_sock *csk)
+{
+       struct cnic_dev *dev = csk->dev;
+       struct l4_kwq_connect_req1 *l4kwqe1;
+       struct l4_kwq_connect_req2 *l4kwqe2;
+       struct l4_kwq_connect_req3 *l4kwqe3;
+       struct kwqe *wqes[3];
+       u8 tcp_flags = 0;
+       int num_wqes = 2;
+
+       l4kwqe1 = (struct l4_kwq_connect_req1 *) &csk->kwqe1;
+       l4kwqe2 = (struct l4_kwq_connect_req2 *) &csk->kwqe2;
+       l4kwqe3 = (struct l4_kwq_connect_req3 *) &csk->kwqe3;
+       memset(l4kwqe1, 0, sizeof(*l4kwqe1));
+       memset(l4kwqe2, 0, sizeof(*l4kwqe2));
+       memset(l4kwqe3, 0, sizeof(*l4kwqe3));
+
+       l4kwqe3->op_code = L4_KWQE_OPCODE_VALUE_CONNECT3;
+       l4kwqe3->flags =
+               L4_LAYER_CODE << L4_KWQ_CONNECT_REQ3_LAYER_CODE_SHIFT;
+       l4kwqe3->ka_timeout = csk->ka_timeout;
+       l4kwqe3->ka_interval = csk->ka_interval;
+       l4kwqe3->ka_max_probe_count = csk->ka_max_probe_count;
+       l4kwqe3->tos = csk->tos;
+       l4kwqe3->ttl = csk->ttl;
+       l4kwqe3->snd_seq_scale = csk->snd_seq_scale;
+       l4kwqe3->pmtu = csk->mtu;
+       l4kwqe3->rcv_buf = csk->rcv_buf;
+       l4kwqe3->snd_buf = csk->snd_buf;
+       l4kwqe3->seed = csk->seed;
+
+       wqes[0] = (struct kwqe *) l4kwqe1;
+       if (test_bit(SK_F_IPV6, &csk->flags)) {
+               wqes[1] = (struct kwqe *) l4kwqe2;
+               wqes[2] = (struct kwqe *) l4kwqe3;
+               num_wqes = 3;
+
+               l4kwqe1->conn_flags = L4_KWQ_CONNECT_REQ1_IP_V6;
+               l4kwqe2->op_code = L4_KWQE_OPCODE_VALUE_CONNECT2;
+               l4kwqe2->flags =
+                       L4_KWQ_CONNECT_REQ2_LINKED_WITH_NEXT |
+                       L4_LAYER_CODE << L4_KWQ_CONNECT_REQ2_LAYER_CODE_SHIFT;
+               l4kwqe2->src_ip_v6_2 = be32_to_cpu(csk->src_ip[1]);
+               l4kwqe2->src_ip_v6_3 = be32_to_cpu(csk->src_ip[2]);
+               l4kwqe2->src_ip_v6_4 = be32_to_cpu(csk->src_ip[3]);
+               l4kwqe2->dst_ip_v6_2 = be32_to_cpu(csk->dst_ip[1]);
+               l4kwqe2->dst_ip_v6_3 = be32_to_cpu(csk->dst_ip[2]);
+               l4kwqe2->dst_ip_v6_4 = be32_to_cpu(csk->dst_ip[3]);
+               l4kwqe3->mss = l4kwqe3->pmtu - sizeof(struct ipv6hdr) -
+                              sizeof(struct tcphdr);
+       } else {
+               wqes[1] = (struct kwqe *) l4kwqe3;
+               l4kwqe3->mss = l4kwqe3->pmtu - sizeof(struct iphdr) -
+                              sizeof(struct tcphdr);
+       }
+
+       l4kwqe1->op_code = L4_KWQE_OPCODE_VALUE_CONNECT1;
+       l4kwqe1->flags =
+               (L4_LAYER_CODE << L4_KWQ_CONNECT_REQ1_LAYER_CODE_SHIFT) |
+                L4_KWQ_CONNECT_REQ3_LINKED_WITH_NEXT;
+       l4kwqe1->cid = csk->cid;
+       l4kwqe1->pg_cid = csk->pg_cid;
+       l4kwqe1->src_ip = be32_to_cpu(csk->src_ip[0]);
+       l4kwqe1->dst_ip = be32_to_cpu(csk->dst_ip[0]);
+       l4kwqe1->src_port = be16_to_cpu(csk->src_port);
+       l4kwqe1->dst_port = be16_to_cpu(csk->dst_port);
+       if (csk->tcp_flags & SK_TCP_NO_DELAY_ACK)
+               tcp_flags |= L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK;
+       if (csk->tcp_flags & SK_TCP_KEEP_ALIVE)
+               tcp_flags |= L4_KWQ_CONNECT_REQ1_KEEP_ALIVE;
+       if (csk->tcp_flags & SK_TCP_NAGLE)
+               tcp_flags |= L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE;
+       if (csk->tcp_flags & SK_TCP_TIMESTAMP)
+               tcp_flags |= L4_KWQ_CONNECT_REQ1_TIME_STAMP;
+       if (csk->tcp_flags & SK_TCP_SACK)
+               tcp_flags |= L4_KWQ_CONNECT_REQ1_SACK;
+       if (csk->tcp_flags & SK_TCP_SEG_SCALING)
+               tcp_flags |= L4_KWQ_CONNECT_REQ1_SEG_SCALING;
+
+       l4kwqe1->tcp_flags = tcp_flags;
+
+       return dev->submit_kwqes(dev, wqes, num_wqes);
+}
+
+static int cnic_cm_close_req(struct cnic_sock *csk)
+{
+       struct cnic_dev *dev = csk->dev;
+       struct l4_kwq_close_req *l4kwqe;
+       struct kwqe *wqes[1];
+
+       l4kwqe = (struct l4_kwq_close_req *) &csk->kwqe2;
+       memset(l4kwqe, 0, sizeof(*l4kwqe));
+       wqes[0] = (struct kwqe *) l4kwqe;
+
+       l4kwqe->op_code = L4_KWQE_OPCODE_VALUE_CLOSE;
+       l4kwqe->flags = L4_LAYER_CODE << L4_KWQ_CLOSE_REQ_LAYER_CODE_SHIFT;
+       l4kwqe->cid = csk->cid;
+
+       return dev->submit_kwqes(dev, wqes, 1);
+}
+
+static int cnic_cm_abort_req(struct cnic_sock *csk)
+{
+       struct cnic_dev *dev = csk->dev;
+       struct l4_kwq_reset_req *l4kwqe;
+       struct kwqe *wqes[1];
+
+       l4kwqe = (struct l4_kwq_reset_req *) &csk->kwqe2;
+       memset(l4kwqe, 0, sizeof(*l4kwqe));
+       wqes[0] = (struct kwqe *) l4kwqe;
+
+       l4kwqe->op_code = L4_KWQE_OPCODE_VALUE_RESET;
+       l4kwqe->flags = L4_LAYER_CODE << L4_KWQ_RESET_REQ_LAYER_CODE_SHIFT;
+       l4kwqe->cid = csk->cid;
+
+       return dev->submit_kwqes(dev, wqes, 1);
+}
+
+static int cnic_cm_create(struct cnic_dev *dev, int ulp_type, u32 cid,
+                         u32 l5_cid, struct cnic_sock **csk, void *context)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       struct cnic_sock *csk1;
+
+       if (l5_cid >= MAX_CM_SK_TBL_SZ)
+               return -EINVAL;
+
+       csk1 = &cp->csk_tbl[l5_cid];
+       if (atomic_read(&csk1->ref_count))
+               return -EAGAIN;
+
+       if (test_and_set_bit(SK_F_INUSE, &csk1->flags))
+               return -EBUSY;
+
+       csk1->dev = dev;
+       csk1->cid = cid;
+       csk1->l5_cid = l5_cid;
+       csk1->ulp_type = ulp_type;
+       csk1->context = context;
+
+       csk1->ka_timeout = DEF_KA_TIMEOUT;
+       csk1->ka_interval = DEF_KA_INTERVAL;
+       csk1->ka_max_probe_count = DEF_KA_MAX_PROBE_COUNT;
+       csk1->tos = DEF_TOS;
+       csk1->ttl = DEF_TTL;
+       csk1->snd_seq_scale = DEF_SND_SEQ_SCALE;
+       csk1->rcv_buf = DEF_RCV_BUF;
+       csk1->snd_buf = DEF_SND_BUF;
+       csk1->seed = DEF_SEED;
+
+       *csk = csk1;
+       return 0;
+}
+
+static void cnic_cm_cleanup(struct cnic_sock *csk)
+{
+       if (csk->src_port) {
+               struct cnic_dev *dev = csk->dev;
+               struct cnic_local *cp = dev->cnic_priv;
+
+               cnic_free_id(&cp->csk_port_tbl, csk->src_port);
+               csk->src_port = 0;
+       }
+}
+
+static void cnic_close_conn(struct cnic_sock *csk)
+{
+       if (test_bit(SK_F_PG_OFFLD_COMPLETE, &csk->flags)) {
+               cnic_cm_upload_pg(csk);
+               clear_bit(SK_F_PG_OFFLD_COMPLETE, &csk->flags);
+       }
+       cnic_cm_cleanup(csk);
+}
+
+static int cnic_cm_destroy(struct cnic_sock *csk)
+{
+       if (!cnic_in_use(csk))
+               return -EINVAL;
+
+       csk_hold(csk);
+       clear_bit(SK_F_INUSE, &csk->flags);
+       smp_mb__after_clear_bit();
+       while (atomic_read(&csk->ref_count) != 1)
+               msleep(1);
+       cnic_cm_cleanup(csk);
+
+       csk->flags = 0;
+       csk_put(csk);
+       return 0;
+}
+
+static inline u16 cnic_get_vlan(struct net_device *dev,
+                               struct net_device **vlan_dev)
+{
+       if (dev->priv_flags & IFF_802_1Q_VLAN) {
+               *vlan_dev = vlan_dev_real_dev(dev);
+               return vlan_dev_vlan_id(dev);
+       }
+       *vlan_dev = dev;
+       return 0;
+}
+
+static int cnic_get_v4_route(struct sockaddr_in *dst_addr,
+                            struct dst_entry **dst)
+{
+       struct flowi fl;
+       int err;
+       struct rtable *rt;
+
+       memset(&fl, 0, sizeof(fl));
+       fl.nl_u.ip4_u.daddr = dst_addr->sin_addr.s_addr;
+
+       err = ip_route_output_key(&init_net, &rt, &fl);
+       if (!err)
+               *dst = &rt->u.dst;
+       return err;
+}
+
+static int cnic_get_v6_route(struct sockaddr_in6 *dst_addr,
+                            struct dst_entry **dst)
+{
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       struct flowi fl;
+
+       memset(&fl, 0, sizeof(fl));
+       ipv6_addr_copy(&fl.fl6_dst, &dst_addr->sin6_addr);
+       if (ipv6_addr_type(&fl.fl6_dst) & IPV6_ADDR_LINKLOCAL)
+               fl.oif = dst_addr->sin6_scope_id;
+
+       *dst = ip6_route_output(&init_net, NULL, &fl);
+       if (*dst)
+               return 0;
+#endif
+
+       return -ENETUNREACH;
+}
+
+static struct cnic_dev *cnic_cm_select_dev(struct sockaddr_in *dst_addr,
+                                          int ulp_type)
+{
+       struct cnic_dev *dev = NULL;
+       struct dst_entry *dst;
+       struct net_device *netdev = NULL;
+       int err = -ENETUNREACH;
+
+       if (dst_addr->sin_family == AF_INET)
+               err = cnic_get_v4_route(dst_addr, &dst);
+       else if (dst_addr->sin_family == AF_INET6) {
+               struct sockaddr_in6 *dst_addr6 =
+                       (struct sockaddr_in6 *) dst_addr;
+
+               err = cnic_get_v6_route(dst_addr6, &dst);
+       } else
+               return NULL;
+
+       if (err)
+               return NULL;
+
+       if (!dst->dev)
+               goto done;
+
+       cnic_get_vlan(dst->dev, &netdev);
+
+       dev = cnic_from_netdev(netdev);
+
+done:
+       dst_release(dst);
+       if (dev)
+               cnic_put(dev);
+       return dev;
+}
+
+static int cnic_resolve_addr(struct cnic_sock *csk, struct cnic_sockaddr *saddr)
+{
+       struct cnic_dev *dev = csk->dev;
+       struct cnic_local *cp = dev->cnic_priv;
+
+       return cnic_send_nlmsg(cp, ISCSI_KEVENT_PATH_REQ, csk);
+}
+
+static int cnic_get_route(struct cnic_sock *csk, struct cnic_sockaddr *saddr)
+{
+       struct cnic_dev *dev = csk->dev;
+       struct cnic_local *cp = dev->cnic_priv;
+       int is_v6, err, rc = -ENETUNREACH;
+       struct dst_entry *dst;
+       struct net_device *realdev;
+       u32 local_port;
+
+       if (saddr->local.v6.sin6_family == AF_INET6 &&
+           saddr->remote.v6.sin6_family == AF_INET6)
+               is_v6 = 1;
+       else if (saddr->local.v4.sin_family == AF_INET &&
+                saddr->remote.v4.sin_family == AF_INET)
+               is_v6 = 0;
+       else
+               return -EINVAL;
+
+       clear_bit(SK_F_IPV6, &csk->flags);
+
+       if (is_v6) {
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+               set_bit(SK_F_IPV6, &csk->flags);
+               err = cnic_get_v6_route(&saddr->remote.v6, &dst);
+               if (err)
+                       return err;
+
+               if (!dst || dst->error || !dst->dev)
+                       goto err_out;
+
+               memcpy(&csk->dst_ip[0], &saddr->remote.v6.sin6_addr,
+                      sizeof(struct in6_addr));
+               csk->dst_port = saddr->remote.v6.sin6_port;
+               local_port = saddr->local.v6.sin6_port;
+#else
+               return rc;
+#endif
+
+       } else {
+               err = cnic_get_v4_route(&saddr->remote.v4, &dst);
+               if (err)
+                       return err;
+
+               if (!dst || dst->error || !dst->dev)
+                       goto err_out;
+
+               csk->dst_ip[0] = saddr->remote.v4.sin_addr.s_addr;
+               csk->dst_port = saddr->remote.v4.sin_port;
+               local_port = saddr->local.v4.sin_port;
+       }
+
+       csk->vlan_id = cnic_get_vlan(dst->dev, &realdev);
+       if (realdev != dev->netdev)
+               goto err_out;
+
+       if (local_port >= CNIC_LOCAL_PORT_MIN &&
+           local_port < CNIC_LOCAL_PORT_MAX) {
+               if (cnic_alloc_id(&cp->csk_port_tbl, local_port))
+                       local_port = 0;
+       } else
+               local_port = 0;
+
+       if (!local_port) {
+               local_port = cnic_alloc_new_id(&cp->csk_port_tbl);
+               if (local_port == -1) {
+                       rc = -ENOMEM;
+                       goto err_out;
+               }
+       }
+       csk->src_port = local_port;
+
+       csk->mtu = dst_mtu(dst);
+       rc = 0;
+
+err_out:
+       dst_release(dst);
+       return rc;
+}
+
+static void cnic_init_csk_state(struct cnic_sock *csk)
+{
+       csk->state = 0;
+       clear_bit(SK_F_OFFLD_SCHED, &csk->flags);
+       clear_bit(SK_F_CLOSING, &csk->flags);
+}
+
+static int cnic_cm_connect(struct cnic_sock *csk, struct cnic_sockaddr *saddr)
+{
+       int err = 0;
+
+       if (!cnic_in_use(csk))
+               return -EINVAL;
+
+       if (test_and_set_bit(SK_F_CONNECT_START, &csk->flags))
+               return -EINVAL;
+
+       cnic_init_csk_state(csk);
+
+       err = cnic_get_route(csk, saddr);
+       if (err)
+               goto err_out;
+
+       err = cnic_resolve_addr(csk, saddr);
+       if (!err)
+               return 0;
+
+err_out:
+       clear_bit(SK_F_CONNECT_START, &csk->flags);
+       return err;
+}
+
+static int cnic_cm_abort(struct cnic_sock *csk)
+{
+       struct cnic_local *cp = csk->dev->cnic_priv;
+       u32 opcode;
+
+       if (!cnic_in_use(csk))
+               return -EINVAL;
+
+       if (cnic_abort_prep(csk))
+               return cnic_cm_abort_req(csk);
+
+       /* Getting here means that we haven't started connect, or
+        * connect was not successful.
+        */
+
+       csk->state = L4_KCQE_OPCODE_VALUE_RESET_COMP;
+       if (test_bit(SK_F_PG_OFFLD_COMPLETE, &csk->flags))
+               opcode = csk->state;
+       else
+               opcode = L5CM_RAMROD_CMD_ID_TERMINATE_OFFLOAD;
+       cp->close_conn(csk, opcode);
+
+       return 0;
+}
+
+static int cnic_cm_close(struct cnic_sock *csk)
+{
+       if (!cnic_in_use(csk))
+               return -EINVAL;
+
+       if (cnic_close_prep(csk)) {
+               csk->state = L4_KCQE_OPCODE_VALUE_CLOSE_COMP;
+               return cnic_cm_close_req(csk);
+       }
+       return 0;
+}
+
+static void cnic_cm_upcall(struct cnic_local *cp, struct cnic_sock *csk,
+                          u8 opcode)
+{
+       struct cnic_ulp_ops *ulp_ops;
+       int ulp_type = csk->ulp_type;
+
+       rcu_read_lock();
+       ulp_ops = rcu_dereference(cp->ulp_ops[ulp_type]);
+       if (ulp_ops) {
+               if (opcode == L4_KCQE_OPCODE_VALUE_CONNECT_COMPLETE)
+                       ulp_ops->cm_connect_complete(csk);
+               else if (opcode == L4_KCQE_OPCODE_VALUE_CLOSE_COMP)
+                       ulp_ops->cm_close_complete(csk);
+               else if (opcode == L4_KCQE_OPCODE_VALUE_RESET_RECEIVED)
+                       ulp_ops->cm_remote_abort(csk);
+               else if (opcode == L4_KCQE_OPCODE_VALUE_RESET_COMP)
+                       ulp_ops->cm_abort_complete(csk);
+               else if (opcode == L4_KCQE_OPCODE_VALUE_CLOSE_RECEIVED)
+                       ulp_ops->cm_remote_close(csk);
+       }
+       rcu_read_unlock();
+}
+
+static int cnic_cm_set_pg(struct cnic_sock *csk)
+{
+       if (cnic_offld_prep(csk)) {
+               if (test_bit(SK_F_PG_OFFLD_COMPLETE, &csk->flags))
+                       cnic_cm_update_pg(csk);
+               else
+                       cnic_cm_offload_pg(csk);
+       }
+       return 0;
+}
+
+static void cnic_cm_process_offld_pg(struct cnic_dev *dev, struct l4_kcq *kcqe)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       u32 l5_cid = kcqe->pg_host_opaque;
+       u8 opcode = kcqe->op_code;
+       struct cnic_sock *csk = &cp->csk_tbl[l5_cid];
+
+       csk_hold(csk);
+       if (!cnic_in_use(csk))
+               goto done;
+
+       if (opcode == L4_KCQE_OPCODE_VALUE_UPDATE_PG) {
+               clear_bit(SK_F_OFFLD_SCHED, &csk->flags);
+               goto done;
+       }
+       csk->pg_cid = kcqe->pg_cid;
+       set_bit(SK_F_PG_OFFLD_COMPLETE, &csk->flags);
+       cnic_cm_conn_req(csk);
+
+done:
+       csk_put(csk);
+}
+
+static void cnic_cm_process_kcqe(struct cnic_dev *dev, struct kcqe *kcqe)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       struct l4_kcq *l4kcqe = (struct l4_kcq *) kcqe;
+       u8 opcode = l4kcqe->op_code;
+       u32 l5_cid;
+       struct cnic_sock *csk;
+
+       if (opcode == L4_KCQE_OPCODE_VALUE_OFFLOAD_PG ||
+           opcode == L4_KCQE_OPCODE_VALUE_UPDATE_PG) {
+               cnic_cm_process_offld_pg(dev, l4kcqe);
+               return;
+       }
+
+       l5_cid = l4kcqe->conn_id;
+       if (opcode & 0x80)
+               l5_cid = l4kcqe->cid;
+       if (l5_cid >= MAX_CM_SK_TBL_SZ)
+               return;
+
+       csk = &cp->csk_tbl[l5_cid];
+       csk_hold(csk);
+
+       if (!cnic_in_use(csk)) {
+               csk_put(csk);
+               return;
+       }
+
+       switch (opcode) {
+       case L4_KCQE_OPCODE_VALUE_CONNECT_COMPLETE:
+               if (l4kcqe->status == 0)
+                       set_bit(SK_F_OFFLD_COMPLETE, &csk->flags);
+
+               smp_mb__before_clear_bit();
+               clear_bit(SK_F_OFFLD_SCHED, &csk->flags);
+               cnic_cm_upcall(cp, csk, opcode);
+               break;
+
+       case L4_KCQE_OPCODE_VALUE_RESET_RECEIVED:
+               if (test_and_clear_bit(SK_F_OFFLD_COMPLETE, &csk->flags))
+                       csk->state = opcode;
+               /* fall through */
+       case L4_KCQE_OPCODE_VALUE_CLOSE_COMP:
+       case L4_KCQE_OPCODE_VALUE_RESET_COMP:
+               cp->close_conn(csk, opcode);
+               break;
+
+       case L4_KCQE_OPCODE_VALUE_CLOSE_RECEIVED:
+               cnic_cm_upcall(cp, csk, opcode);
+               break;
+       }
+       csk_put(csk);
+}
+
+static void cnic_cm_indicate_kcqe(void *data, struct kcqe *kcqe[], u32 num)
+{
+       struct cnic_dev *dev = data;
+       int i;
+
+       for (i = 0; i < num; i++)
+               cnic_cm_process_kcqe(dev, kcqe[i]);
+}
+
+static struct cnic_ulp_ops cm_ulp_ops = {
+       .indicate_kcqes         = cnic_cm_indicate_kcqe,
+};
+
+static void cnic_cm_free_mem(struct cnic_dev *dev)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+
+       kfree(cp->csk_tbl);
+       cp->csk_tbl = NULL;
+       cnic_free_id_tbl(&cp->csk_port_tbl);
+}
+
+static int cnic_cm_alloc_mem(struct cnic_dev *dev)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+
+       cp->csk_tbl = kzalloc(sizeof(struct cnic_sock) * MAX_CM_SK_TBL_SZ,
+                             GFP_KERNEL);
+       if (!cp->csk_tbl)
+               return -ENOMEM;
+
+       if (cnic_init_id_tbl(&cp->csk_port_tbl, CNIC_LOCAL_PORT_RANGE,
+                            CNIC_LOCAL_PORT_MIN)) {
+               cnic_cm_free_mem(dev);
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+static int cnic_ready_to_close(struct cnic_sock *csk, u32 opcode)
+{
+       if ((opcode == csk->state) ||
+           (opcode == L4_KCQE_OPCODE_VALUE_RESET_RECEIVED &&
+            csk->state == L4_KCQE_OPCODE_VALUE_CLOSE_COMP)) {
+               if (!test_and_set_bit(SK_F_CLOSING, &csk->flags))
+                       return 1;
+       }
+       return 0;
+}
+
+static void cnic_close_bnx2_conn(struct cnic_sock *csk, u32 opcode)
+{
+       struct cnic_dev *dev = csk->dev;
+       struct cnic_local *cp = dev->cnic_priv;
+
+       clear_bit(SK_F_CONNECT_START, &csk->flags);
+       if (cnic_ready_to_close(csk, opcode)) {
+               cnic_close_conn(csk);
+               cnic_cm_upcall(cp, csk, opcode);
+       }
+}
+
+static void cnic_cm_stop_bnx2_hw(struct cnic_dev *dev)
+{
+}
+
+static int cnic_cm_init_bnx2_hw(struct cnic_dev *dev)
+{
+       u32 seed;
+
+       get_random_bytes(&seed, 4);
+       cnic_ctx_wr(dev, 45, 0, seed);
+       return 0;
+}
+
+static int cnic_cm_open(struct cnic_dev *dev)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       int err;
+
+       err = cnic_cm_alloc_mem(dev);
+       if (err)
+               return err;
+
+       err = cp->start_cm(dev);
+
+       if (err)
+               goto err_out;
+
+       dev->cm_create = cnic_cm_create;
+       dev->cm_destroy = cnic_cm_destroy;
+       dev->cm_connect = cnic_cm_connect;
+       dev->cm_abort = cnic_cm_abort;
+       dev->cm_close = cnic_cm_close;
+       dev->cm_select_dev = cnic_cm_select_dev;
+
+       cp->ulp_handle[CNIC_ULP_L4] = dev;
+       rcu_assign_pointer(cp->ulp_ops[CNIC_ULP_L4], &cm_ulp_ops);
+       return 0;
+
+err_out:
+       cnic_cm_free_mem(dev);
+       return err;
+}
+
+static int cnic_cm_shutdown(struct cnic_dev *dev)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       int i;
+
+       cp->stop_cm(dev);
+
+       if (!cp->csk_tbl)
+               return 0;
+
+       for (i = 0; i < MAX_CM_SK_TBL_SZ; i++) {
+               struct cnic_sock *csk = &cp->csk_tbl[i];
+
+               clear_bit(SK_F_INUSE, &csk->flags);
+               cnic_cm_cleanup(csk);
+       }
+       cnic_cm_free_mem(dev);
+
+       return 0;
+}
+
+static void cnic_init_context(struct cnic_dev *dev, u32 cid)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       u32 cid_addr;
+       int i;
+
+       if (CHIP_NUM(cp) == CHIP_NUM_5709)
+               return;
+
+       cid_addr = GET_CID_ADDR(cid);
+
+       for (i = 0; i < CTX_SIZE; i += 4)
+               cnic_ctx_wr(dev, cid_addr, i, 0);
+}
+
+static int cnic_setup_5709_context(struct cnic_dev *dev, int valid)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       int ret = 0, i;
+       u32 valid_bit = valid ? BNX2_CTX_HOST_PAGE_TBL_DATA0_VALID : 0;
+
+       if (CHIP_NUM(cp) != CHIP_NUM_5709)
+               return 0;
+
+       for (i = 0; i < cp->ctx_blks; i++) {
+               int j;
+               u32 idx = cp->ctx_arr[i].cid / cp->cids_per_blk;
+               u32 val;
+
+               memset(cp->ctx_arr[i].ctx, 0, BCM_PAGE_SIZE);
+
+               CNIC_WR(dev, BNX2_CTX_HOST_PAGE_TBL_DATA0,
+                       (cp->ctx_arr[i].mapping & 0xffffffff) | valid_bit);
+               CNIC_WR(dev, BNX2_CTX_HOST_PAGE_TBL_DATA1,
+                       (u64) cp->ctx_arr[i].mapping >> 32);
+               CNIC_WR(dev, BNX2_CTX_HOST_PAGE_TBL_CTRL, idx |
+                       BNX2_CTX_HOST_PAGE_TBL_CTRL_WRITE_REQ);
+               for (j = 0; j < 10; j++) {
+
+                       val = CNIC_RD(dev, BNX2_CTX_HOST_PAGE_TBL_CTRL);
+                       if (!(val & BNX2_CTX_HOST_PAGE_TBL_CTRL_WRITE_REQ))
+                               break;
+                       udelay(5);
+               }
+               if (val & BNX2_CTX_HOST_PAGE_TBL_CTRL_WRITE_REQ) {
+                       ret = -EBUSY;
+                       break;
+               }
+       }
+       return ret;
+}
+
+static void cnic_free_irq(struct cnic_dev *dev)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       struct cnic_eth_dev *ethdev = cp->ethdev;
+
+       if (ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX) {
+               cp->disable_int_sync(dev);
+               tasklet_disable(&cp->cnic_irq_task);
+               free_irq(ethdev->irq_arr[0].vector, dev);
+       }
+}
+
+static int cnic_init_bnx2_irq(struct cnic_dev *dev)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       struct cnic_eth_dev *ethdev = cp->ethdev;
+
+       if (ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX) {
+               int err, i = 0;
+               int sblk_num = cp->status_blk_num;
+               u32 base = ((sblk_num - 1) * BNX2_HC_SB_CONFIG_SIZE) +
+                          BNX2_HC_SB_CONFIG_1;
+
+               CNIC_WR(dev, base, BNX2_HC_SB_CONFIG_1_ONE_SHOT);
+
+               CNIC_WR(dev, base + BNX2_HC_COMP_PROD_TRIP_OFF, (2 << 16) | 8);
+               CNIC_WR(dev, base + BNX2_HC_COM_TICKS_OFF, (64 << 16) | 220);
+               CNIC_WR(dev, base + BNX2_HC_CMD_TICKS_OFF, (64 << 16) | 220);
+
+               cp->bnx2_status_blk = cp->status_blk;
+               cp->last_status_idx = cp->bnx2_status_blk->status_idx;
+               tasklet_init(&cp->cnic_irq_task, &cnic_service_bnx2_msix,
+                            (unsigned long) dev);
+               err = request_irq(ethdev->irq_arr[0].vector, cnic_irq, 0,
+                                 "cnic", dev);
+               if (err) {
+                       tasklet_disable(&cp->cnic_irq_task);
+                       return err;
+               }
+               while (cp->bnx2_status_blk->status_completion_producer_index &&
+                      i < 10) {
+                       CNIC_WR(dev, BNX2_HC_COALESCE_NOW,
+                               1 << (11 + sblk_num));
+                       udelay(10);
+                       i++;
+                       barrier();
+               }
+               if (cp->bnx2_status_blk->status_completion_producer_index) {
+                       cnic_free_irq(dev);
+                       goto failed;
+               }
+
+       } else {
+               struct status_block *sblk = cp->status_blk;
+               u32 hc_cmd = CNIC_RD(dev, BNX2_HC_COMMAND);
+               int i = 0;
+
+               while (sblk->status_completion_producer_index && i < 10) {
+                       CNIC_WR(dev, BNX2_HC_COMMAND,
+                               hc_cmd | BNX2_HC_COMMAND_COAL_NOW_WO_INT);
+                       udelay(10);
+                       i++;
+                       barrier();
+               }
+               if (sblk->status_completion_producer_index)
+                       goto failed;
+
+       }
+       return 0;
+
+failed:
+       printk(KERN_ERR PFX "%s: " "KCQ index not resetting to 0.\n",
+              dev->netdev->name);
+       return -EBUSY;
+}
+
+static void cnic_enable_bnx2_int(struct cnic_dev *dev)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       struct cnic_eth_dev *ethdev = cp->ethdev;
+
+       if (!(ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX))
+               return;
+
+       CNIC_WR(dev, BNX2_PCICFG_INT_ACK_CMD, cp->int_num |
+               BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | cp->last_status_idx);
+}
+
+static void cnic_disable_bnx2_int_sync(struct cnic_dev *dev)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       struct cnic_eth_dev *ethdev = cp->ethdev;
+
+       if (!(ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX))
+               return;
+
+       CNIC_WR(dev, BNX2_PCICFG_INT_ACK_CMD, cp->int_num |
+               BNX2_PCICFG_INT_ACK_CMD_MASK_INT);
+       CNIC_RD(dev, BNX2_PCICFG_INT_ACK_CMD);
+       synchronize_irq(ethdev->irq_arr[0].vector);
+}
+
+static void cnic_init_bnx2_tx_ring(struct cnic_dev *dev)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       struct cnic_eth_dev *ethdev = cp->ethdev;
+       u32 cid_addr, tx_cid, sb_id;
+       u32 val, offset0, offset1, offset2, offset3;
+       int i;
+       struct tx_bd *txbd;
+       dma_addr_t buf_map;
+       struct status_block *s_blk = cp->status_blk;
+
+       sb_id = cp->status_blk_num;
+       tx_cid = 20;
+       cnic_init_context(dev, tx_cid);
+       cnic_init_context(dev, tx_cid + 1);
+       cp->tx_cons_ptr = &s_blk->status_tx_quick_consumer_index2;
+       if (ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX) {
+               struct status_block_msix *sblk = cp->status_blk;
+
+               tx_cid = TX_TSS_CID + sb_id - 1;
+               cnic_init_context(dev, tx_cid);
+               CNIC_WR(dev, BNX2_TSCH_TSS_CFG, (sb_id << 24) |
+                       (TX_TSS_CID << 7));
+               cp->tx_cons_ptr = &sblk->status_tx_quick_consumer_index;
+       }
+       cp->tx_cons = *cp->tx_cons_ptr;
+
+       cid_addr = GET_CID_ADDR(tx_cid);
+       if (CHIP_NUM(cp) == CHIP_NUM_5709) {
+               u32 cid_addr2 = GET_CID_ADDR(tx_cid + 4) + 0x40;
+
+               for (i = 0; i < PHY_CTX_SIZE; i += 4)
+                       cnic_ctx_wr(dev, cid_addr2, i, 0);
+
+               offset0 = BNX2_L2CTX_TYPE_XI;
+               offset1 = BNX2_L2CTX_CMD_TYPE_XI;
+               offset2 = BNX2_L2CTX_TBDR_BHADDR_HI_XI;
+               offset3 = BNX2_L2CTX_TBDR_BHADDR_LO_XI;
+       } else {
+               offset0 = BNX2_L2CTX_TYPE;
+               offset1 = BNX2_L2CTX_CMD_TYPE;
+               offset2 = BNX2_L2CTX_TBDR_BHADDR_HI;
+               offset3 = BNX2_L2CTX_TBDR_BHADDR_LO;
+       }
+       val = BNX2_L2CTX_TYPE_TYPE_L2 | BNX2_L2CTX_TYPE_SIZE_L2;
+       cnic_ctx_wr(dev, cid_addr, offset0, val);
+
+       val = BNX2_L2CTX_CMD_TYPE_TYPE_L2 | (8 << 16);
+       cnic_ctx_wr(dev, cid_addr, offset1, val);
+
+       txbd = (struct tx_bd *) cp->l2_ring;
+
+       buf_map = cp->l2_buf_map;
+       for (i = 0; i < MAX_TX_DESC_CNT; i++, txbd++) {
+               txbd->tx_bd_haddr_hi = (u64) buf_map >> 32;
+               txbd->tx_bd_haddr_lo = (u64) buf_map & 0xffffffff;
+       }
+       val = (u64) cp->l2_ring_map >> 32;
+       cnic_ctx_wr(dev, cid_addr, offset2, val);
+       txbd->tx_bd_haddr_hi = val;
+
+       val = (u64) cp->l2_ring_map & 0xffffffff;
+       cnic_ctx_wr(dev, cid_addr, offset3, val);
+       txbd->tx_bd_haddr_lo = val;
+}
+
+static void cnic_init_bnx2_rx_ring(struct cnic_dev *dev)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       struct cnic_eth_dev *ethdev = cp->ethdev;
+       u32 cid_addr, sb_id, val, coal_reg, coal_val;
+       int i;
+       struct rx_bd *rxbd;
+       struct status_block *s_blk = cp->status_blk;
+
+       sb_id = cp->status_blk_num;
+       cnic_init_context(dev, 2);
+       cp->rx_cons_ptr = &s_blk->status_rx_quick_consumer_index2;
+       coal_reg = BNX2_HC_COMMAND;
+       coal_val = CNIC_RD(dev, coal_reg);
+       if (ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX) {
+               struct status_block_msix *sblk = cp->status_blk;
+
+               cp->rx_cons_ptr = &sblk->status_rx_quick_consumer_index;
+               coal_reg = BNX2_HC_COALESCE_NOW;
+               coal_val = 1 << (11 + sb_id);
+       }
+       i = 0;
+       while (!(*cp->rx_cons_ptr != 0) && i < 10) {
+               CNIC_WR(dev, coal_reg, coal_val);
+               udelay(10);
+               i++;
+               barrier();
+       }
+       cp->rx_cons = *cp->rx_cons_ptr;
+
+       cid_addr = GET_CID_ADDR(2);
+       val = BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE_VALUE |
+             BNX2_L2CTX_CTX_TYPE_SIZE_L2 | (0x02 << 8);
+       cnic_ctx_wr(dev, cid_addr, BNX2_L2CTX_CTX_TYPE, val);
+
+       if (sb_id == 0)
+               val = 2 << BNX2_L2CTX_STATUSB_NUM_SHIFT;
+       else
+               val = BNX2_L2CTX_STATUSB_NUM(sb_id);
+       cnic_ctx_wr(dev, cid_addr, BNX2_L2CTX_HOST_BDIDX, val);
+
+       rxbd = (struct rx_bd *) (cp->l2_ring + BCM_PAGE_SIZE);
+       for (i = 0; i < MAX_RX_DESC_CNT; i++, rxbd++) {
+               dma_addr_t buf_map;
+               int n = (i % cp->l2_rx_ring_size) + 1;
+
+               buf_map = cp->l2_buf_map + (n * cp->l2_single_buf_size);
+               rxbd->rx_bd_len = cp->l2_single_buf_size;
+               rxbd->rx_bd_flags = RX_BD_FLAGS_START | RX_BD_FLAGS_END;
+               rxbd->rx_bd_haddr_hi = (u64) buf_map >> 32;
+               rxbd->rx_bd_haddr_lo = (u64) buf_map & 0xffffffff;
+       }
+       val = (u64) (cp->l2_ring_map + BCM_PAGE_SIZE) >> 32;
+       cnic_ctx_wr(dev, cid_addr, BNX2_L2CTX_NX_BDHADDR_HI, val);
+       rxbd->rx_bd_haddr_hi = val;
+
+       val = (u64) (cp->l2_ring_map + BCM_PAGE_SIZE) & 0xffffffff;
+       cnic_ctx_wr(dev, cid_addr, BNX2_L2CTX_NX_BDHADDR_LO, val);
+       rxbd->rx_bd_haddr_lo = val;
+
+       val = cnic_reg_rd_ind(dev, BNX2_RXP_SCRATCH_RXP_FLOOD);
+       cnic_reg_wr_ind(dev, BNX2_RXP_SCRATCH_RXP_FLOOD, val | (1 << 2));
+}
+
+static void cnic_shutdown_bnx2_rx_ring(struct cnic_dev *dev)
+{
+       struct kwqe *wqes[1], l2kwqe;
+
+       memset(&l2kwqe, 0, sizeof(l2kwqe));
+       wqes[0] = &l2kwqe;
+       l2kwqe.kwqe_op_flag = (L2_LAYER_CODE << KWQE_FLAGS_LAYER_SHIFT) |
+                             (L2_KWQE_OPCODE_VALUE_FLUSH <<
+                              KWQE_OPCODE_SHIFT) | 2;
+       dev->submit_kwqes(dev, wqes, 1);
+}
+
+static void cnic_set_bnx2_mac(struct cnic_dev *dev)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       u32 val;
+
+       val = cp->func << 2;
+
+       cp->shmem_base = cnic_reg_rd_ind(dev, BNX2_SHM_HDR_ADDR_0 + val);
+
+       val = cnic_reg_rd_ind(dev, cp->shmem_base +
+                             BNX2_PORT_HW_CFG_ISCSI_MAC_UPPER);
+       dev->mac_addr[0] = (u8) (val >> 8);
+       dev->mac_addr[1] = (u8) val;
+
+       CNIC_WR(dev, BNX2_EMAC_MAC_MATCH4, val);
+
+       val = cnic_reg_rd_ind(dev, cp->shmem_base +
+                             BNX2_PORT_HW_CFG_ISCSI_MAC_LOWER);
+       dev->mac_addr[2] = (u8) (val >> 24);
+       dev->mac_addr[3] = (u8) (val >> 16);
+       dev->mac_addr[4] = (u8) (val >> 8);
+       dev->mac_addr[5] = (u8) val;
+
+       CNIC_WR(dev, BNX2_EMAC_MAC_MATCH5, val);
+
+       val = 4 | BNX2_RPM_SORT_USER2_BC_EN;
+       if (CHIP_NUM(cp) != CHIP_NUM_5709)
+               val |= BNX2_RPM_SORT_USER2_PROM_VLAN;
+
+       CNIC_WR(dev, BNX2_RPM_SORT_USER2, 0x0);
+       CNIC_WR(dev, BNX2_RPM_SORT_USER2, val);
+       CNIC_WR(dev, BNX2_RPM_SORT_USER2, val | BNX2_RPM_SORT_USER2_ENA);
+}
+
+static int cnic_start_bnx2_hw(struct cnic_dev *dev)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       struct cnic_eth_dev *ethdev = cp->ethdev;
+       struct status_block *sblk = cp->status_blk;
+       u32 val;
+       int err;
+
+       cnic_set_bnx2_mac(dev);
+
+       val = CNIC_RD(dev, BNX2_MQ_CONFIG);
+       val &= ~BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE;
+       if (BCM_PAGE_BITS > 12)
+               val |= (12 - 8)  << 4;
+       else
+               val |= (BCM_PAGE_BITS - 8)  << 4;
+
+       CNIC_WR(dev, BNX2_MQ_CONFIG, val);
+
+       CNIC_WR(dev, BNX2_HC_COMP_PROD_TRIP, (2 << 16) | 8);
+       CNIC_WR(dev, BNX2_HC_COM_TICKS, (64 << 16) | 220);
+       CNIC_WR(dev, BNX2_HC_CMD_TICKS, (64 << 16) | 220);
+
+       err = cnic_setup_5709_context(dev, 1);
+       if (err)
+               return err;
+
+       cnic_init_context(dev, KWQ_CID);
+       cnic_init_context(dev, KCQ_CID);
+
+       cp->kwq_cid_addr = GET_CID_ADDR(KWQ_CID);
+       cp->kwq_io_addr = MB_GET_CID_ADDR(KWQ_CID) + L5_KRNLQ_HOST_QIDX;
+
+       cp->max_kwq_idx = MAX_KWQ_IDX;
+       cp->kwq_prod_idx = 0;
+       cp->kwq_con_idx = 0;
+       cp->cnic_local_flags |= CNIC_LCL_FL_KWQ_INIT;
+
+       if (CHIP_NUM(cp) == CHIP_NUM_5706 || CHIP_NUM(cp) == CHIP_NUM_5708)
+               cp->kwq_con_idx_ptr = &sblk->status_rx_quick_consumer_index15;
+       else
+               cp->kwq_con_idx_ptr = &sblk->status_cmd_consumer_index;
+
+       /* Initialize the kernel work queue context. */
+       val = KRNLQ_TYPE_TYPE_KRNLQ | KRNLQ_SIZE_TYPE_SIZE |
+             (BCM_PAGE_BITS - 8) | KRNLQ_FLAGS_QE_SELF_SEQ;
+       cnic_ctx_wr(dev, cp->kwq_cid_addr, L5_KRNLQ_TYPE, val);
+
+       val = (BCM_PAGE_SIZE / sizeof(struct kwqe) - 1) << 16;
+       cnic_ctx_wr(dev, cp->kwq_cid_addr, L5_KRNLQ_QE_SELF_SEQ_MAX, val);
+
+       val = ((BCM_PAGE_SIZE / sizeof(struct kwqe)) << 16) | KWQ_PAGE_CNT;
+       cnic_ctx_wr(dev, cp->kwq_cid_addr, L5_KRNLQ_PGTBL_NPAGES, val);
+
+       val = (u32) ((u64) cp->kwq_info.pgtbl_map >> 32);
+       cnic_ctx_wr(dev, cp->kwq_cid_addr, L5_KRNLQ_PGTBL_HADDR_HI, val);
+
+       val = (u32) cp->kwq_info.pgtbl_map;
+       cnic_ctx_wr(dev, cp->kwq_cid_addr, L5_KRNLQ_PGTBL_HADDR_LO, val);
+
+       cp->kcq_cid_addr = GET_CID_ADDR(KCQ_CID);
+       cp->kcq_io_addr = MB_GET_CID_ADDR(KCQ_CID) + L5_KRNLQ_HOST_QIDX;
+
+       cp->kcq_prod_idx = 0;
+
+       /* Initialize the kernel complete queue context. */
+       val = KRNLQ_TYPE_TYPE_KRNLQ | KRNLQ_SIZE_TYPE_SIZE |
+             (BCM_PAGE_BITS - 8) | KRNLQ_FLAGS_QE_SELF_SEQ;
+       cnic_ctx_wr(dev, cp->kcq_cid_addr, L5_KRNLQ_TYPE, val);
+
+       val = (BCM_PAGE_SIZE / sizeof(struct kcqe) - 1) << 16;
+       cnic_ctx_wr(dev, cp->kcq_cid_addr, L5_KRNLQ_QE_SELF_SEQ_MAX, val);
+
+       val = ((BCM_PAGE_SIZE / sizeof(struct kcqe)) << 16) | KCQ_PAGE_CNT;
+       cnic_ctx_wr(dev, cp->kcq_cid_addr, L5_KRNLQ_PGTBL_NPAGES, val);
+
+       val = (u32) ((u64) cp->kcq_info.pgtbl_map >> 32);
+       cnic_ctx_wr(dev, cp->kcq_cid_addr, L5_KRNLQ_PGTBL_HADDR_HI, val);
+
+       val = (u32) cp->kcq_info.pgtbl_map;
+       cnic_ctx_wr(dev, cp->kcq_cid_addr, L5_KRNLQ_PGTBL_HADDR_LO, val);
+
+       cp->int_num = 0;
+       if (ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX) {
+               u32 sb_id = cp->status_blk_num;
+               u32 sb = BNX2_L2CTX_STATUSB_NUM(sb_id);
+
+               cp->int_num = sb_id << BNX2_PCICFG_INT_ACK_CMD_INT_NUM_SHIFT;
+               cnic_ctx_wr(dev, cp->kwq_cid_addr, L5_KRNLQ_HOST_QIDX, sb);
+               cnic_ctx_wr(dev, cp->kcq_cid_addr, L5_KRNLQ_HOST_QIDX, sb);
+       }
+
+       /* Enable Commnad Scheduler notification when we write to the
+        * host producer index of the kernel contexts. */
+       CNIC_WR(dev, BNX2_MQ_KNL_CMD_MASK1, 2);
+
+       /* Enable Command Scheduler notification when we write to either
+        * the Send Queue or Receive Queue producer indexes of the kernel
+        * bypass contexts. */
+       CNIC_WR(dev, BNX2_MQ_KNL_BYP_CMD_MASK1, 7);
+       CNIC_WR(dev, BNX2_MQ_KNL_BYP_WRITE_MASK1, 7);
+
+       /* Notify COM when the driver post an application buffer. */
+       CNIC_WR(dev, BNX2_MQ_KNL_RX_V2P_MASK2, 0x2000);
+
+       /* Set the CP and COM doorbells.  These two processors polls the
+        * doorbell for a non zero value before running.  This must be done
+        * after setting up the kernel queue contexts. */
+       cnic_reg_wr_ind(dev, BNX2_CP_SCRATCH + 0x20, 1);
+       cnic_reg_wr_ind(dev, BNX2_COM_SCRATCH + 0x20, 1);
+
+       cnic_init_bnx2_tx_ring(dev);
+       cnic_init_bnx2_rx_ring(dev);
+
+       err = cnic_init_bnx2_irq(dev);
+       if (err) {
+               printk(KERN_ERR PFX "%s: cnic_init_irq failed\n",
+                      dev->netdev->name);
+               cnic_reg_wr_ind(dev, BNX2_CP_SCRATCH + 0x20, 0);
+               cnic_reg_wr_ind(dev, BNX2_COM_SCRATCH + 0x20, 0);
+               return err;
+       }
+
+       return 0;
+}
+
+static int cnic_start_hw(struct cnic_dev *dev)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       struct cnic_eth_dev *ethdev = cp->ethdev;
+       int err;
+
+       if (test_bit(CNIC_F_CNIC_UP, &dev->flags))
+               return -EALREADY;
+
+       err = ethdev->drv_register_cnic(dev->netdev, cp->cnic_ops, dev);
+       if (err) {
+               printk(KERN_ERR PFX "%s: register_cnic failed\n",
+                      dev->netdev->name);
+               goto err2;
+       }
+
+       dev->regview = ethdev->io_base;
+       cp->chip_id = ethdev->chip_id;
+       pci_dev_get(dev->pcidev);
+       cp->func = PCI_FUNC(dev->pcidev->devfn);
+       cp->status_blk = ethdev->irq_arr[0].status_blk;
+       cp->status_blk_num = ethdev->irq_arr[0].status_blk_num;
+
+       err = cp->alloc_resc(dev);
+       if (err) {
+               printk(KERN_ERR PFX "%s: allocate resource failure\n",
+                      dev->netdev->name);
+               goto err1;
+       }
+
+       err = cp->start_hw(dev);
+       if (err)
+               goto err1;
+
+       err = cnic_cm_open(dev);
+       if (err)
+               goto err1;
+
+       set_bit(CNIC_F_CNIC_UP, &dev->flags);
+
+       cp->enable_int(dev);
+
+       return 0;
+
+err1:
+       ethdev->drv_unregister_cnic(dev->netdev);
+       cp->free_resc(dev);
+       pci_dev_put(dev->pcidev);
+err2:
+       return err;
+}
+
+static void cnic_stop_bnx2_hw(struct cnic_dev *dev)
+{
+       struct cnic_local *cp = dev->cnic_priv;
+       struct cnic_eth_dev *ethdev = cp->ethdev;
+
+       cnic_disable_bnx2_int_sync(dev);
+
+       cnic_reg_wr_ind(dev, BNX2_CP_SCRATCH + 0x20, 0);
+       cnic_reg_wr_ind(dev, BNX2_COM_SCRATCH + 0x20, 0);
+
+       cnic_init_context(dev, KWQ_CID);
+       cnic_init_context(dev, KCQ_CID);
+
+       cnic_setup_5709_context(dev, 0);
+       cnic_free_irq(dev);
+
+       ethdev->drv_unregister_cnic(dev->netdev);
+
+       cnic_free_resc(dev);
+}
+
+static void cnic_stop_hw(struct cnic_dev *dev)
+{
+       if (test_bit(CNIC_F_CNIC_UP, &dev->flags)) {
+               struct cnic_local *cp = dev->cnic_priv;
+
+               clear_bit(CNIC_F_CNIC_UP, &dev->flags);
+               rcu_assign_pointer(cp->ulp_ops[CNIC_ULP_L4], NULL);
+               synchronize_rcu();
+               cnic_cm_shutdown(dev);
+               cp->stop_hw(dev);
+               pci_dev_put(dev->pcidev);
+       }
+}
+
+static void cnic_free_dev(struct cnic_dev *dev)
+{
+       int i = 0;
+
+       while ((atomic_read(&dev->ref_count) != 0) && i < 10) {
+               msleep(100);
+               i++;
+       }
+       if (atomic_read(&dev->ref_count) != 0)
+               printk(KERN_ERR PFX "%s: Failed waiting for ref count to go"
+                                   " to zero.\n", dev->netdev->name);
+
+       printk(KERN_INFO PFX "Removed CNIC device: %s\n", dev->netdev->name);
+       dev_put(dev->netdev);
+       kfree(dev);
+}
+
+static struct cnic_dev *cnic_alloc_dev(struct net_device *dev,
+                                      struct pci_dev *pdev)
+{
+       struct cnic_dev *cdev;
+       struct cnic_local *cp;
+       int alloc_size;
+
+       alloc_size = sizeof(struct cnic_dev) + sizeof(struct cnic_local);
+
+       cdev = kzalloc(alloc_size , GFP_KERNEL);
+       if (cdev == NULL) {
+               printk(KERN_ERR PFX "%s: allocate dev struct failure\n",
+                      dev->name);
+               return NULL;
+       }
+
+       cdev->netdev = dev;
+       cdev->cnic_priv = (char *)cdev + sizeof(struct cnic_dev);
+       cdev->register_device = cnic_register_device;
+       cdev->unregister_device = cnic_unregister_device;
+       cdev->iscsi_nl_msg_recv = cnic_iscsi_nl_msg_recv;
+
+       cp = cdev->cnic_priv;
+       cp->dev = cdev;
+       cp->uio_dev = -1;
+       cp->l2_single_buf_size = 0x400;
+       cp->l2_rx_ring_size = 3;
+
+       spin_lock_init(&cp->cnic_ulp_lock);
+
+       printk(KERN_INFO PFX "Added CNIC device: %s\n", dev->name);
+
+       return cdev;
+}
+
+static struct cnic_dev *init_bnx2_cnic(struct net_device *dev)
+{
+       struct pci_dev *pdev;
+       struct cnic_dev *cdev;
+       struct cnic_local *cp;
+       struct cnic_eth_dev *ethdev = NULL;
+       struct cnic_eth_dev *(*probe)(void *) = NULL;
+
+       probe = __symbol_get("bnx2_cnic_probe");
+       if (probe) {
+               ethdev = (*probe)(dev);
+               symbol_put_addr(probe);
+       }
+       if (!ethdev)
+               return NULL;
+
+       pdev = ethdev->pdev;
+       if (!pdev)
+               return NULL;
+
+       dev_hold(dev);
+       pci_dev_get(pdev);
+       if (pdev->device == PCI_DEVICE_ID_NX2_5709 ||
+           pdev->device == PCI_DEVICE_ID_NX2_5709S) {
+               u8 rev;
+
+               pci_read_config_byte(pdev, PCI_REVISION_ID, &rev);
+               if (rev < 0x10) {
+                       pci_dev_put(pdev);
+                       goto cnic_err;
+               }
+       }
+       pci_dev_put(pdev);
+
+       cdev = cnic_alloc_dev(dev, pdev);
+       if (cdev == NULL)
+               goto cnic_err;
+
+       set_bit(CNIC_F_BNX2_CLASS, &cdev->flags);
+       cdev->submit_kwqes = cnic_submit_bnx2_kwqes;
+
+       cp = cdev->cnic_priv;
+       cp->ethdev = ethdev;
+       cdev->pcidev = pdev;
+
+       cp->cnic_ops = &cnic_bnx2_ops;
+       cp->start_hw = cnic_start_bnx2_hw;
+       cp->stop_hw = cnic_stop_bnx2_hw;
+       cp->setup_pgtbl = cnic_setup_page_tbl;
+       cp->alloc_resc = cnic_alloc_bnx2_resc;
+       cp->free_resc = cnic_free_resc;
+       cp->start_cm = cnic_cm_init_bnx2_hw;
+       cp->stop_cm = cnic_cm_stop_bnx2_hw;
+       cp->enable_int = cnic_enable_bnx2_int;
+       cp->disable_int_sync = cnic_disable_bnx2_int_sync;
+       cp->close_conn = cnic_close_bnx2_conn;
+       cp->next_idx = cnic_bnx2_next_idx;
+       cp->hw_idx = cnic_bnx2_hw_idx;
+       return cdev;
+
+cnic_err:
+       dev_put(dev);
+       return NULL;
+}
+
+static struct cnic_dev *is_cnic_dev(struct net_device *dev)
+{
+       struct ethtool_drvinfo drvinfo;
+       struct cnic_dev *cdev = NULL;
+
+       if (dev->ethtool_ops && dev->ethtool_ops->get_drvinfo) {
+               memset(&drvinfo, 0, sizeof(drvinfo));
+               dev->ethtool_ops->get_drvinfo(dev, &drvinfo);
+
+               if (!strcmp(drvinfo.driver, "bnx2"))
+                       cdev = init_bnx2_cnic(dev);
+               if (cdev) {
+                       write_lock(&cnic_dev_lock);
+                       list_add(&cdev->list, &cnic_dev_list);
+                       write_unlock(&cnic_dev_lock);
+               }
+       }
+       return cdev;
+}
+
+/**
+ * netdev event handler
+ */
+static int cnic_netdev_event(struct notifier_block *this, unsigned long event,
+                                                        void *ptr)
+{
+       struct net_device *netdev = ptr;
+       struct cnic_dev *dev;
+       int if_type;
+       int new_dev = 0;
+
+       dev = cnic_from_netdev(netdev);
+
+       if (!dev && (event == NETDEV_REGISTER || event == NETDEV_UP)) {
+               /* Check for the hot-plug device */
+               dev = is_cnic_dev(netdev);
+               if (dev) {
+                       new_dev = 1;
+                       cnic_hold(dev);
+               }
+       }
+       if (dev) {
+               struct cnic_local *cp = dev->cnic_priv;
+
+               if (new_dev)
+                       cnic_ulp_init(dev);
+               else if (event == NETDEV_UNREGISTER)
+                       cnic_ulp_exit(dev);
+               else if (event == NETDEV_UP) {
+                       mutex_lock(&cnic_lock);
+                       if (!cnic_start_hw(dev))
+                               cnic_ulp_start(dev);
+                       mutex_unlock(&cnic_lock);
+               }
+
+               rcu_read_lock();
+               for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) {
+                       struct cnic_ulp_ops *ulp_ops;
+                       void *ctx;
+
+                       ulp_ops = rcu_dereference(cp->ulp_ops[if_type]);
+                       if (!ulp_ops || !ulp_ops->indicate_netevent)
+                               continue;
+
+                       ctx = cp->ulp_handle[if_type];
+
+                       ulp_ops->indicate_netevent(ctx, event);
+               }
+               rcu_read_unlock();
+
+               if (event == NETDEV_GOING_DOWN) {
+                       mutex_lock(&cnic_lock);
+                       cnic_ulp_stop(dev);
+                       cnic_stop_hw(dev);
+                       mutex_unlock(&cnic_lock);
+               } else if (event == NETDEV_UNREGISTER) {
+                       write_lock(&cnic_dev_lock);
+                       list_del_init(&dev->list);
+                       write_unlock(&cnic_dev_lock);
+
+                       cnic_put(dev);
+                       cnic_free_dev(dev);
+                       goto done;
+               }
+               cnic_put(dev);
+       }
+done:
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block cnic_netdev_notifier = {
+       .notifier_call = cnic_netdev_event
+};
+
+static void cnic_release(void)
+{
+       struct cnic_dev *dev;
+
+       while (!list_empty(&cnic_dev_list)) {
+               dev = list_entry(cnic_dev_list.next, struct cnic_dev, list);
+               if (test_bit(CNIC_F_CNIC_UP, &dev->flags)) {
+                       cnic_ulp_stop(dev);
+                       cnic_stop_hw(dev);
+               }
+
+               cnic_ulp_exit(dev);
+               list_del_init(&dev->list);
+               cnic_free_dev(dev);
+       }
+}
+
+static int __init cnic_init(void)
+{
+       int rc = 0;
+
+       printk(KERN_INFO "%s", version);
+
+       rc = register_netdevice_notifier(&cnic_netdev_notifier);
+       if (rc) {
+               cnic_release();
+               return rc;
+       }
+
+       return 0;
+}
+
+static void __exit cnic_exit(void)
+{
+       unregister_netdevice_notifier(&cnic_netdev_notifier);
+       cnic_release();
+       return;
+}
+
+module_init(cnic_init);
+module_exit(cnic_exit);
diff --git a/drivers/net/cnic.h b/drivers/net/cnic.h
new file mode 100644 (file)
index 0000000..5192d4a
--- /dev/null
@@ -0,0 +1,299 @@
+/* cnic.h: Broadcom CNIC core network driver.
+ *
+ * Copyright (c) 2006-2009 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.
+ *
+ */
+
+
+#ifndef CNIC_H
+#define CNIC_H
+
+#define KWQ_PAGE_CNT   4
+#define KCQ_PAGE_CNT   16
+
+#define KWQ_CID                24
+#define KCQ_CID                25
+
+/*
+ *     krnlq_context definition
+ */
+#define L5_KRNLQ_FLAGS 0x00000000
+#define L5_KRNLQ_SIZE  0x00000000
+#define L5_KRNLQ_TYPE  0x00000000
+#define KRNLQ_FLAGS_PG_SZ                                      (0xf<<0)
+#define KRNLQ_FLAGS_PG_SZ_256                                  (0<<0)
+#define KRNLQ_FLAGS_PG_SZ_512                                  (1<<0)
+#define KRNLQ_FLAGS_PG_SZ_1K                                   (2<<0)
+#define KRNLQ_FLAGS_PG_SZ_2K                                   (3<<0)
+#define KRNLQ_FLAGS_PG_SZ_4K                                   (4<<0)
+#define KRNLQ_FLAGS_PG_SZ_8K                                   (5<<0)
+#define KRNLQ_FLAGS_PG_SZ_16K                                  (6<<0)
+#define KRNLQ_FLAGS_PG_SZ_32K                                  (7<<0)
+#define KRNLQ_FLAGS_PG_SZ_64K                                  (8<<0)
+#define KRNLQ_FLAGS_PG_SZ_128K                                 (9<<0)
+#define KRNLQ_FLAGS_PG_SZ_256K                                 (10<<0)
+#define KRNLQ_FLAGS_PG_SZ_512K                                 (11<<0)
+#define KRNLQ_FLAGS_PG_SZ_1M                                   (12<<0)
+#define KRNLQ_FLAGS_PG_SZ_2M                                   (13<<0)
+#define KRNLQ_FLAGS_QE_SELF_SEQ                                        (1<<15)
+#define KRNLQ_SIZE_TYPE_SIZE   ((((0x28 + 0x1f) & ~0x1f) / 0x20) << 16)
+#define KRNLQ_TYPE_TYPE                                                (0xf<<28)
+#define KRNLQ_TYPE_TYPE_EMPTY                                  (0<<28)
+#define KRNLQ_TYPE_TYPE_KRNLQ                                  (6<<28)
+
+#define L5_KRNLQ_HOST_QIDX             0x00000004
+#define L5_KRNLQ_HOST_FW_QIDX          0x00000008
+#define L5_KRNLQ_NX_QE_SELF_SEQ        0x0000000c
+#define L5_KRNLQ_QE_SELF_SEQ_MAX       0x0000000c
+#define L5_KRNLQ_NX_QE_HADDR_HI        0x00000010
+#define L5_KRNLQ_NX_QE_HADDR_LO        0x00000014
+#define L5_KRNLQ_PGTBL_PGIDX           0x00000018
+#define L5_KRNLQ_NX_PG_QIDX            0x00000018
+#define L5_KRNLQ_PGTBL_NPAGES          0x0000001c
+#define L5_KRNLQ_QIDX_INCR             0x0000001c
+#define L5_KRNLQ_PGTBL_HADDR_HI        0x00000020
+#define L5_KRNLQ_PGTBL_HADDR_LO        0x00000024
+
+#define BNX2_PG_CTX_MAP                        0x1a0034
+#define BNX2_ISCSI_CTX_MAP             0x1a0074
+
+struct cnic_redirect_entry {
+       struct dst_entry *old_dst;
+       struct dst_entry *new_dst;
+};
+
+#define MAX_COMPLETED_KCQE     64
+
+#define MAX_CNIC_L5_CONTEXT    256
+
+#define MAX_CM_SK_TBL_SZ       MAX_CNIC_L5_CONTEXT
+
+#define MAX_ISCSI_TBL_SZ       256
+
+#define CNIC_LOCAL_PORT_MIN    60000
+#define CNIC_LOCAL_PORT_MAX    61000
+#define CNIC_LOCAL_PORT_RANGE  (CNIC_LOCAL_PORT_MAX - CNIC_LOCAL_PORT_MIN)
+
+#define KWQE_CNT (BCM_PAGE_SIZE / sizeof(struct kwqe))
+#define KCQE_CNT (BCM_PAGE_SIZE / sizeof(struct kcqe))
+#define MAX_KWQE_CNT (KWQE_CNT - 1)
+#define MAX_KCQE_CNT (KCQE_CNT - 1)
+
+#define MAX_KWQ_IDX    ((KWQ_PAGE_CNT * KWQE_CNT) - 1)
+#define MAX_KCQ_IDX    ((KCQ_PAGE_CNT * KCQE_CNT) - 1)
+
+#define KWQ_PG(x) (((x) & ~MAX_KWQE_CNT) >> (BCM_PAGE_BITS - 5))
+#define KWQ_IDX(x) ((x) & MAX_KWQE_CNT)
+
+#define KCQ_PG(x) (((x) & ~MAX_KCQE_CNT) >> (BCM_PAGE_BITS - 5))
+#define KCQ_IDX(x) ((x) & MAX_KCQE_CNT)
+
+#define BNX2X_NEXT_KCQE(x) (((x) & (MAX_KCQE_CNT - 1)) ==              \
+               (MAX_KCQE_CNT - 1)) ?                                   \
+               (x) + 2 : (x) + 1
+
+#define BNX2X_KWQ_DATA_PG(cp, x) ((x) / (cp)->kwq_16_data_pp)
+#define BNX2X_KWQ_DATA_IDX(cp, x) ((x) % (cp)->kwq_16_data_pp)
+#define BNX2X_KWQ_DATA(cp, x)                                          \
+       &(cp)->kwq_16_data[BNX2X_KWQ_DATA_PG(cp, x)][BNX2X_KWQ_DATA_IDX(cp, x)]
+
+#define DEF_IPID_COUNT         0xc001
+
+#define DEF_KA_TIMEOUT         10000
+#define DEF_KA_INTERVAL                300000
+#define DEF_KA_MAX_PROBE_COUNT 3
+#define DEF_TOS                        0
+#define DEF_TTL                        0xfe
+#define DEF_SND_SEQ_SCALE      0
+#define DEF_RCV_BUF            0xffff
+#define DEF_SND_BUF            0xffff
+#define DEF_SEED               0
+#define DEF_MAX_RT_TIME                500
+#define DEF_MAX_DA_COUNT       2
+#define DEF_SWS_TIMER          1000
+#define DEF_MAX_CWND           0xffff
+
+struct cnic_ctx {
+       u32             cid;
+       void            *ctx;
+       dma_addr_t      mapping;
+};
+
+#define BNX2_MAX_CID           0x2000
+
+struct cnic_dma {
+       int             num_pages;
+       void            **pg_arr;
+       dma_addr_t      *pg_map_arr;
+       int             pgtbl_size;
+       u32             *pgtbl;
+       dma_addr_t      pgtbl_map;
+};
+
+struct cnic_id_tbl {
+       spinlock_t      lock;
+       u32             start;
+       u32             max;
+       u32             next;
+       unsigned long   *table;
+};
+
+#define CNIC_KWQ16_DATA_SIZE   128
+
+struct kwqe_16_data {
+       u8      data[CNIC_KWQ16_DATA_SIZE];
+};
+
+struct cnic_iscsi {
+       struct cnic_dma         task_array_info;
+       struct cnic_dma         r2tq_info;
+       struct cnic_dma         hq_info;
+};
+
+struct cnic_context {
+       u32                     cid;
+       struct kwqe_16_data     *kwqe_data;
+       dma_addr_t              kwqe_data_mapping;
+       wait_queue_head_t       waitq;
+       int                     wait_cond;
+       unsigned long           timestamp;
+       u32                     ctx_flags;
+#define        CTX_FL_OFFLD_START      0x00000001
+       u8                      ulp_proto_id;
+       union {
+               struct cnic_iscsi       *iscsi;
+       } proto;
+};
+
+struct cnic_local {
+
+       spinlock_t cnic_ulp_lock;
+       void *ulp_handle[MAX_CNIC_ULP_TYPE];
+       unsigned long ulp_flags[MAX_CNIC_ULP_TYPE];
+#define ULP_F_INIT     0
+#define ULP_F_START    1
+       struct cnic_ulp_ops *ulp_ops[MAX_CNIC_ULP_TYPE];
+
+       /* protected by ulp_lock */
+       u32 cnic_local_flags;
+#define        CNIC_LCL_FL_KWQ_INIT    0x00000001
+
+       struct cnic_dev *dev;
+
+       struct cnic_eth_dev *ethdev;
+
+       void            *l2_ring;
+       dma_addr_t      l2_ring_map;
+       int             l2_ring_size;
+       int             l2_rx_ring_size;
+
+       void            *l2_buf;
+       dma_addr_t      l2_buf_map;
+       int             l2_buf_size;
+       int             l2_single_buf_size;
+
+       u16             *rx_cons_ptr;
+       u16             *tx_cons_ptr;
+       u16             rx_cons;
+       u16             tx_cons;
+
+       u32 kwq_cid_addr;
+       u32 kcq_cid_addr;
+
+       struct cnic_dma         kwq_info;
+       struct kwqe             **kwq;
+
+       struct cnic_dma         kwq_16_data_info;
+
+       u16             max_kwq_idx;
+
+       u16             kwq_prod_idx;
+       u32             kwq_io_addr;
+
+       u16             *kwq_con_idx_ptr;
+       u16             kwq_con_idx;
+
+       struct cnic_dma kcq_info;
+       struct kcqe     **kcq;
+
+       u16             kcq_prod_idx;
+       u32             kcq_io_addr;
+
+       void                            *status_blk;
+       struct status_block_msix        *bnx2_status_blk;
+       struct host_status_block        *bnx2x_status_blk;
+
+       u32                             status_blk_num;
+       u32                             int_num;
+       u32                             last_status_idx;
+       struct tasklet_struct           cnic_irq_task;
+
+       struct kcqe             *completed_kcq[MAX_COMPLETED_KCQE];
+
+       struct cnic_sock        *csk_tbl;
+       struct cnic_id_tbl      csk_port_tbl;
+
+       struct cnic_dma         conn_buf_info;
+       struct cnic_dma         gbl_buf_info;
+
+       struct cnic_iscsi       *iscsi_tbl;
+       struct cnic_context     *ctx_tbl;
+       struct cnic_id_tbl      cid_tbl;
+       int                     max_iscsi_conn;
+       atomic_t                iscsi_conn;
+
+       /* per connection parameters */
+       int                     num_iscsi_tasks;
+       int                     num_ccells;
+       int                     task_array_size;
+       int                     r2tq_size;
+       int                     hq_size;
+       int                     num_cqs;
+
+       struct cnic_ctx         *ctx_arr;
+       int                     ctx_blks;
+       int                     ctx_blk_size;
+       int                     cids_per_blk;
+
+       u32                     chip_id;
+       int                     func;
+       u32                     shmem_base;
+
+       u32                     uio_dev;
+       struct uio_info         *cnic_uinfo;
+
+       struct cnic_ops         *cnic_ops;
+       int                     (*start_hw)(struct cnic_dev *);
+       void                    (*stop_hw)(struct cnic_dev *);
+       void                    (*setup_pgtbl)(struct cnic_dev *,
+                                              struct cnic_dma *);
+       int                     (*alloc_resc)(struct cnic_dev *);
+       void                    (*free_resc)(struct cnic_dev *);
+       int                     (*start_cm)(struct cnic_dev *);
+       void                    (*stop_cm)(struct cnic_dev *);
+       void                    (*enable_int)(struct cnic_dev *);
+       void                    (*disable_int_sync)(struct cnic_dev *);
+       void                    (*ack_int)(struct cnic_dev *);
+       void                    (*close_conn)(struct cnic_sock *, u32 opcode);
+       u16                     (*next_idx)(u16);
+       u16                     (*hw_idx)(u16);
+};
+
+struct bnx2x_bd_chain_next {
+       u32     addr_lo;
+       u32     addr_hi;
+       u8      reserved[8];
+};
+
+#define ISCSI_RAMROD_CMD_ID_UPDATE_CONN                (ISCSI_KCQE_OPCODE_UPDATE_CONN)
+#define ISCSI_RAMROD_CMD_ID_INIT               (ISCSI_KCQE_OPCODE_INIT)
+
+#define CDU_REGION_NUMBER_XCM_AG 2
+#define CDU_REGION_NUMBER_UCM_AG 4
+
+#endif
+
diff --git a/drivers/net/cnic_defs.h b/drivers/net/cnic_defs.h
new file mode 100644 (file)
index 0000000..cee80f6
--- /dev/null
@@ -0,0 +1,580 @@
+
+/* cnic.c: Broadcom CNIC core network driver.
+ *
+ * Copyright (c) 2006-2009 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.
+ *
+ */
+
+#ifndef CNIC_DEFS_H
+#define CNIC_DEFS_H
+
+/* KWQ (kernel work queue) request op codes */
+#define L2_KWQE_OPCODE_VALUE_FLUSH                  (4)
+
+#define L4_KWQE_OPCODE_VALUE_CONNECT1               (50)
+#define L4_KWQE_OPCODE_VALUE_CONNECT2               (51)
+#define L4_KWQE_OPCODE_VALUE_CONNECT3               (52)
+#define L4_KWQE_OPCODE_VALUE_RESET                  (53)
+#define L4_KWQE_OPCODE_VALUE_CLOSE                  (54)
+#define L4_KWQE_OPCODE_VALUE_UPDATE_SECRET          (60)
+#define L4_KWQE_OPCODE_VALUE_INIT_ULP               (61)
+
+#define L4_KWQE_OPCODE_VALUE_OFFLOAD_PG             (1)
+#define L4_KWQE_OPCODE_VALUE_UPDATE_PG              (9)
+#define L4_KWQE_OPCODE_VALUE_UPLOAD_PG              (14)
+
+#define L5CM_RAMROD_CMD_ID_BASE                        (0x80)
+#define L5CM_RAMROD_CMD_ID_TCP_CONNECT         (L5CM_RAMROD_CMD_ID_BASE + 3)
+#define L5CM_RAMROD_CMD_ID_CLOSE               (L5CM_RAMROD_CMD_ID_BASE + 12)
+#define L5CM_RAMROD_CMD_ID_ABORT               (L5CM_RAMROD_CMD_ID_BASE + 13)
+#define L5CM_RAMROD_CMD_ID_SEARCHER_DELETE     (L5CM_RAMROD_CMD_ID_BASE + 14)
+#define L5CM_RAMROD_CMD_ID_TERMINATE_OFFLOAD   (L5CM_RAMROD_CMD_ID_BASE + 15)
+
+/* KCQ (kernel completion queue) response op codes */
+#define L4_KCQE_OPCODE_VALUE_CLOSE_COMP             (53)
+#define L4_KCQE_OPCODE_VALUE_RESET_COMP             (54)
+#define L4_KCQE_OPCODE_VALUE_FW_TCP_UPDATE          (55)
+#define L4_KCQE_OPCODE_VALUE_CONNECT_COMPLETE       (56)
+#define L4_KCQE_OPCODE_VALUE_RESET_RECEIVED         (57)
+#define L4_KCQE_OPCODE_VALUE_CLOSE_RECEIVED         (58)
+#define L4_KCQE_OPCODE_VALUE_INIT_ULP               (61)
+
+#define L4_KCQE_OPCODE_VALUE_OFFLOAD_PG             (1)
+#define L4_KCQE_OPCODE_VALUE_UPDATE_PG              (9)
+#define L4_KCQE_OPCODE_VALUE_UPLOAD_PG              (14)
+
+/* KCQ (kernel completion queue) completion status */
+#define L4_KCQE_COMPLETION_STATUS_SUCCESS                  (0)
+#define L4_KCQE_COMPLETION_STATUS_TIMEOUT        (0x93)
+
+#define L4_LAYER_CODE (4)
+#define L2_LAYER_CODE (2)
+
+/*
+ * L4 KCQ CQE
+ */
+struct l4_kcq {
+       u32 cid;
+       u32 pg_cid;
+       u32 conn_id;
+       u32 pg_host_opaque;
+#if defined(__BIG_ENDIAN)
+       u16 status;
+       u16 reserved1;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved1;
+       u16 status;
+#endif
+       u32 reserved2[2];
+#if defined(__BIG_ENDIAN)
+       u8 flags;
+#define L4_KCQ_RESERVED3 (0x7<<0)
+#define L4_KCQ_RESERVED3_SHIFT 0
+#define L4_KCQ_RAMROD_COMPLETION (0x1<<3) /* Everest only */
+#define L4_KCQ_RAMROD_COMPLETION_SHIFT 3
+#define L4_KCQ_LAYER_CODE (0x7<<4)
+#define L4_KCQ_LAYER_CODE_SHIFT 4
+#define L4_KCQ_RESERVED4 (0x1<<7)
+#define L4_KCQ_RESERVED4_SHIFT 7
+       u8 op_code;
+       u16 qe_self_seq;
+#elif defined(__LITTLE_ENDIAN)
+       u16 qe_self_seq;
+       u8 op_code;
+       u8 flags;
+#define L4_KCQ_RESERVED3 (0xF<<0)
+#define L4_KCQ_RESERVED3_SHIFT 0
+#define L4_KCQ_RAMROD_COMPLETION (0x1<<3) /* Everest only */
+#define L4_KCQ_RAMROD_COMPLETION_SHIFT 3
+#define L4_KCQ_LAYER_CODE (0x7<<4)
+#define L4_KCQ_LAYER_CODE_SHIFT 4
+#define L4_KCQ_RESERVED4 (0x1<<7)
+#define L4_KCQ_RESERVED4_SHIFT 7
+#endif
+};
+
+
+/*
+ * L4 KCQ CQE PG upload
+ */
+struct l4_kcq_upload_pg {
+       u32 pg_cid;
+#if defined(__BIG_ENDIAN)
+       u16 pg_status;
+       u16 pg_ipid_count;
+#elif defined(__LITTLE_ENDIAN)
+       u16 pg_ipid_count;
+       u16 pg_status;
+#endif
+       u32 reserved1[5];
+#if defined(__BIG_ENDIAN)
+       u8 flags;
+#define L4_KCQ_UPLOAD_PG_RESERVED3 (0xF<<0)
+#define L4_KCQ_UPLOAD_PG_RESERVED3_SHIFT 0
+#define L4_KCQ_UPLOAD_PG_LAYER_CODE (0x7<<4)
+#define L4_KCQ_UPLOAD_PG_LAYER_CODE_SHIFT 4
+#define L4_KCQ_UPLOAD_PG_RESERVED4 (0x1<<7)
+#define L4_KCQ_UPLOAD_PG_RESERVED4_SHIFT 7
+       u8 op_code;
+       u16 qe_self_seq;
+#elif defined(__LITTLE_ENDIAN)
+       u16 qe_self_seq;
+       u8 op_code;
+       u8 flags;
+#define L4_KCQ_UPLOAD_PG_RESERVED3 (0xF<<0)
+#define L4_KCQ_UPLOAD_PG_RESERVED3_SHIFT 0
+#define L4_KCQ_UPLOAD_PG_LAYER_CODE (0x7<<4)
+#define L4_KCQ_UPLOAD_PG_LAYER_CODE_SHIFT 4
+#define L4_KCQ_UPLOAD_PG_RESERVED4 (0x1<<7)
+#define L4_KCQ_UPLOAD_PG_RESERVED4_SHIFT 7
+#endif
+};
+
+
+/*
+ * Gracefully close the connection request
+ */
+struct l4_kwq_close_req {
+#if defined(__BIG_ENDIAN)
+       u8 flags;
+#define L4_KWQ_CLOSE_REQ_RESERVED1 (0xF<<0)
+#define L4_KWQ_CLOSE_REQ_RESERVED1_SHIFT 0
+#define L4_KWQ_CLOSE_REQ_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CLOSE_REQ_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CLOSE_REQ_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CLOSE_REQ_LINKED_WITH_NEXT_SHIFT 7
+       u8 op_code;
+       u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved0;
+       u8 op_code;
+       u8 flags;
+#define L4_KWQ_CLOSE_REQ_RESERVED1 (0xF<<0)
+#define L4_KWQ_CLOSE_REQ_RESERVED1_SHIFT 0
+#define L4_KWQ_CLOSE_REQ_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CLOSE_REQ_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CLOSE_REQ_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CLOSE_REQ_LINKED_WITH_NEXT_SHIFT 7
+#endif
+       u32 cid;
+       u32 reserved2[6];
+};
+
+
+/*
+ * The first request to be passed in order to establish connection in option2
+ */
+struct l4_kwq_connect_req1 {
+#if defined(__BIG_ENDIAN)
+       u8 flags;
+#define L4_KWQ_CONNECT_REQ1_RESERVED1 (0xF<<0)
+#define L4_KWQ_CONNECT_REQ1_RESERVED1_SHIFT 0
+#define L4_KWQ_CONNECT_REQ1_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CONNECT_REQ1_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CONNECT_REQ1_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CONNECT_REQ1_LINKED_WITH_NEXT_SHIFT 7
+       u8 op_code;
+       u8 reserved0;
+       u8 conn_flags;
+#define L4_KWQ_CONNECT_REQ1_IS_PG_HOST_OPAQUE (0x1<<0)
+#define L4_KWQ_CONNECT_REQ1_IS_PG_HOST_OPAQUE_SHIFT 0
+#define L4_KWQ_CONNECT_REQ1_IP_V6 (0x1<<1)
+#define L4_KWQ_CONNECT_REQ1_IP_V6_SHIFT 1
+#define L4_KWQ_CONNECT_REQ1_PASSIVE_FLAG (0x1<<2)
+#define L4_KWQ_CONNECT_REQ1_PASSIVE_FLAG_SHIFT 2
+#define L4_KWQ_CONNECT_REQ1_RSRV (0x1F<<3)
+#define L4_KWQ_CONNECT_REQ1_RSRV_SHIFT 3
+#elif defined(__LITTLE_ENDIAN)
+       u8 conn_flags;
+#define L4_KWQ_CONNECT_REQ1_IS_PG_HOST_OPAQUE (0x1<<0)
+#define L4_KWQ_CONNECT_REQ1_IS_PG_HOST_OPAQUE_SHIFT 0
+#define L4_KWQ_CONNECT_REQ1_IP_V6 (0x1<<1)
+#define L4_KWQ_CONNECT_REQ1_IP_V6_SHIFT 1
+#define L4_KWQ_CONNECT_REQ1_PASSIVE_FLAG (0x1<<2)
+#define L4_KWQ_CONNECT_REQ1_PASSIVE_FLAG_SHIFT 2
+#define L4_KWQ_CONNECT_REQ1_RSRV (0x1F<<3)
+#define L4_KWQ_CONNECT_REQ1_RSRV_SHIFT 3
+       u8 reserved0;
+       u8 op_code;
+       u8 flags;
+#define L4_KWQ_CONNECT_REQ1_RESERVED1 (0xF<<0)
+#define L4_KWQ_CONNECT_REQ1_RESERVED1_SHIFT 0
+#define L4_KWQ_CONNECT_REQ1_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CONNECT_REQ1_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CONNECT_REQ1_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CONNECT_REQ1_LINKED_WITH_NEXT_SHIFT 7
+#endif
+       u32 cid;
+       u32 pg_cid;
+       u32 src_ip;
+       u32 dst_ip;
+#if defined(__BIG_ENDIAN)
+       u16 dst_port;
+       u16 src_port;
+#elif defined(__LITTLE_ENDIAN)
+       u16 src_port;
+       u16 dst_port;
+#endif
+#if defined(__BIG_ENDIAN)
+       u8 rsrv1[3];
+       u8 tcp_flags;
+#define L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK (0x1<<0)
+#define L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK_SHIFT 0
+#define L4_KWQ_CONNECT_REQ1_KEEP_ALIVE (0x1<<1)
+#define L4_KWQ_CONNECT_REQ1_KEEP_ALIVE_SHIFT 1
+#define L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE (0x1<<2)
+#define L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE_SHIFT 2
+#define L4_KWQ_CONNECT_REQ1_TIME_STAMP (0x1<<3)
+#define L4_KWQ_CONNECT_REQ1_TIME_STAMP_SHIFT 3
+#define L4_KWQ_CONNECT_REQ1_SACK (0x1<<4)
+#define L4_KWQ_CONNECT_REQ1_SACK_SHIFT 4
+#define L4_KWQ_CONNECT_REQ1_SEG_SCALING (0x1<<5)
+#define L4_KWQ_CONNECT_REQ1_SEG_SCALING_SHIFT 5
+#define L4_KWQ_CONNECT_REQ1_RESERVED2 (0x3<<6)
+#define L4_KWQ_CONNECT_REQ1_RESERVED2_SHIFT 6
+#elif defined(__LITTLE_ENDIAN)
+       u8 tcp_flags;
+#define L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK (0x1<<0)
+#define L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK_SHIFT 0
+#define L4_KWQ_CONNECT_REQ1_KEEP_ALIVE (0x1<<1)
+#define L4_KWQ_CONNECT_REQ1_KEEP_ALIVE_SHIFT 1
+#define L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE (0x1<<2)
+#define L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE_SHIFT 2
+#define L4_KWQ_CONNECT_REQ1_TIME_STAMP (0x1<<3)
+#define L4_KWQ_CONNECT_REQ1_TIME_STAMP_SHIFT 3
+#define L4_KWQ_CONNECT_REQ1_SACK (0x1<<4)
+#define L4_KWQ_CONNECT_REQ1_SACK_SHIFT 4
+#define L4_KWQ_CONNECT_REQ1_SEG_SCALING (0x1<<5)
+#define L4_KWQ_CONNECT_REQ1_SEG_SCALING_SHIFT 5
+#define L4_KWQ_CONNECT_REQ1_RESERVED2 (0x3<<6)
+#define L4_KWQ_CONNECT_REQ1_RESERVED2_SHIFT 6
+       u8 rsrv1[3];
+#endif
+       u32 rsrv2;
+};
+
+
+/*
+ * The second ( optional )request to be passed in order to establish
+ * connection in option2 - for IPv6 only
+ */
+struct l4_kwq_connect_req2 {
+#if defined(__BIG_ENDIAN)
+       u8 flags;
+#define L4_KWQ_CONNECT_REQ2_RESERVED1 (0xF<<0)
+#define L4_KWQ_CONNECT_REQ2_RESERVED1_SHIFT 0
+#define L4_KWQ_CONNECT_REQ2_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CONNECT_REQ2_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CONNECT_REQ2_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CONNECT_REQ2_LINKED_WITH_NEXT_SHIFT 7
+       u8 op_code;
+       u8 reserved0;
+       u8 rsrv;
+#elif defined(__LITTLE_ENDIAN)
+       u8 rsrv;
+       u8 reserved0;
+       u8 op_code;
+       u8 flags;
+#define L4_KWQ_CONNECT_REQ2_RESERVED1 (0xF<<0)
+#define L4_KWQ_CONNECT_REQ2_RESERVED1_SHIFT 0
+#define L4_KWQ_CONNECT_REQ2_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CONNECT_REQ2_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CONNECT_REQ2_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CONNECT_REQ2_LINKED_WITH_NEXT_SHIFT 7
+#endif
+       u32 reserved2;
+       u32 src_ip_v6_2;
+       u32 src_ip_v6_3;
+       u32 src_ip_v6_4;
+       u32 dst_ip_v6_2;
+       u32 dst_ip_v6_3;
+       u32 dst_ip_v6_4;
+};
+
+
+/*
+ * The third ( and last )request to be passed in order to establish
+ * connection in option2
+ */
+struct l4_kwq_connect_req3 {
+#if defined(__BIG_ENDIAN)
+       u8 flags;
+#define L4_KWQ_CONNECT_REQ3_RESERVED1 (0xF<<0)
+#define L4_KWQ_CONNECT_REQ3_RESERVED1_SHIFT 0
+#define L4_KWQ_CONNECT_REQ3_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CONNECT_REQ3_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CONNECT_REQ3_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CONNECT_REQ3_LINKED_WITH_NEXT_SHIFT 7
+       u8 op_code;
+       u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved0;
+       u8 op_code;
+       u8 flags;
+#define L4_KWQ_CONNECT_REQ3_RESERVED1 (0xF<<0)
+#define L4_KWQ_CONNECT_REQ3_RESERVED1_SHIFT 0
+#define L4_KWQ_CONNECT_REQ3_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CONNECT_REQ3_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CONNECT_REQ3_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CONNECT_REQ3_LINKED_WITH_NEXT_SHIFT 7
+#endif
+       u32 ka_timeout;
+       u32 ka_interval ;
+#if defined(__BIG_ENDIAN)
+       u8 snd_seq_scale;
+       u8 ttl;
+       u8 tos;
+       u8 ka_max_probe_count;
+#elif defined(__LITTLE_ENDIAN)
+       u8 ka_max_probe_count;
+       u8 tos;
+       u8 ttl;
+       u8 snd_seq_scale;
+#endif
+#if defined(__BIG_ENDIAN)
+       u16 pmtu;
+       u16 mss;
+#elif defined(__LITTLE_ENDIAN)
+       u16 mss;
+       u16 pmtu;
+#endif
+       u32 rcv_buf;
+       u32 snd_buf;
+       u32 seed;
+};
+
+
+/*
+ * a KWQE request to offload a PG connection
+ */
+struct l4_kwq_offload_pg {
+#if defined(__BIG_ENDIAN)
+       u8 flags;
+#define L4_KWQ_OFFLOAD_PG_RESERVED1 (0xF<<0)
+#define L4_KWQ_OFFLOAD_PG_RESERVED1_SHIFT 0
+#define L4_KWQ_OFFLOAD_PG_LAYER_CODE (0x7<<4)
+#define L4_KWQ_OFFLOAD_PG_LAYER_CODE_SHIFT 4
+#define L4_KWQ_OFFLOAD_PG_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_OFFLOAD_PG_LINKED_WITH_NEXT_SHIFT 7
+       u8 op_code;
+       u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved0;
+       u8 op_code;
+       u8 flags;
+#define L4_KWQ_OFFLOAD_PG_RESERVED1 (0xF<<0)
+#define L4_KWQ_OFFLOAD_PG_RESERVED1_SHIFT 0
+#define L4_KWQ_OFFLOAD_PG_LAYER_CODE (0x7<<4)
+#define L4_KWQ_OFFLOAD_PG_LAYER_CODE_SHIFT 4
+#define L4_KWQ_OFFLOAD_PG_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_OFFLOAD_PG_LINKED_WITH_NEXT_SHIFT 7
+#endif
+#if defined(__BIG_ENDIAN)
+       u8 l2hdr_nbytes;
+       u8 pg_flags;
+#define L4_KWQ_OFFLOAD_PG_SNAP_ENCAP (0x1<<0)
+#define L4_KWQ_OFFLOAD_PG_SNAP_ENCAP_SHIFT 0
+#define L4_KWQ_OFFLOAD_PG_VLAN_TAGGING (0x1<<1)
+#define L4_KWQ_OFFLOAD_PG_VLAN_TAGGING_SHIFT 1
+#define L4_KWQ_OFFLOAD_PG_RESERVED2 (0x3F<<2)
+#define L4_KWQ_OFFLOAD_PG_RESERVED2_SHIFT 2
+       u8 da0;
+       u8 da1;
+#elif defined(__LITTLE_ENDIAN)
+       u8 da1;
+       u8 da0;
+       u8 pg_flags;
+#define L4_KWQ_OFFLOAD_PG_SNAP_ENCAP (0x1<<0)
+#define L4_KWQ_OFFLOAD_PG_SNAP_ENCAP_SHIFT 0
+#define L4_KWQ_OFFLOAD_PG_VLAN_TAGGING (0x1<<1)
+#define L4_KWQ_OFFLOAD_PG_VLAN_TAGGING_SHIFT 1
+#define L4_KWQ_OFFLOAD_PG_RESERVED2 (0x3F<<2)
+#define L4_KWQ_OFFLOAD_PG_RESERVED2_SHIFT 2
+       u8 l2hdr_nbytes;
+#endif
+#if defined(__BIG_ENDIAN)
+       u8 da2;
+       u8 da3;
+       u8 da4;
+       u8 da5;
+#elif defined(__LITTLE_ENDIAN)
+       u8 da5;
+       u8 da4;
+       u8 da3;
+       u8 da2;
+#endif
+#if defined(__BIG_ENDIAN)
+       u8 sa0;
+       u8 sa1;
+       u8 sa2;
+       u8 sa3;
+#elif defined(__LITTLE_ENDIAN)
+       u8 sa3;
+       u8 sa2;
+       u8 sa1;
+       u8 sa0;
+#endif
+#if defined(__BIG_ENDIAN)
+       u8 sa4;
+       u8 sa5;
+       u16 etype;
+#elif defined(__LITTLE_ENDIAN)
+       u16 etype;
+       u8 sa5;
+       u8 sa4;
+#endif
+#if defined(__BIG_ENDIAN)
+       u16 vlan_tag;
+       u16 ipid_start;
+#elif defined(__LITTLE_ENDIAN)
+       u16 ipid_start;
+       u16 vlan_tag;
+#endif
+#if defined(__BIG_ENDIAN)
+       u16 ipid_count;
+       u16 reserved3;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved3;
+       u16 ipid_count;
+#endif
+       u32 host_opaque;
+};
+
+
+/*
+ * Abortively close the connection request
+ */
+struct l4_kwq_reset_req {
+#if defined(__BIG_ENDIAN)
+       u8 flags;
+#define L4_KWQ_RESET_REQ_RESERVED1 (0xF<<0)
+#define L4_KWQ_RESET_REQ_RESERVED1_SHIFT 0
+#define L4_KWQ_RESET_REQ_LAYER_CODE (0x7<<4)
+#define L4_KWQ_RESET_REQ_LAYER_CODE_SHIFT 4
+#define L4_KWQ_RESET_REQ_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_RESET_REQ_LINKED_WITH_NEXT_SHIFT 7
+       u8 op_code;
+       u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved0;
+       u8 op_code;
+       u8 flags;
+#define L4_KWQ_RESET_REQ_RESERVED1 (0xF<<0)
+#define L4_KWQ_RESET_REQ_RESERVED1_SHIFT 0
+#define L4_KWQ_RESET_REQ_LAYER_CODE (0x7<<4)
+#define L4_KWQ_RESET_REQ_LAYER_CODE_SHIFT 4
+#define L4_KWQ_RESET_REQ_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_RESET_REQ_LINKED_WITH_NEXT_SHIFT 7
+#endif
+       u32 cid;
+       u32 reserved2[6];
+};
+
+
+/*
+ * a KWQE request to update a PG connection
+ */
+struct l4_kwq_update_pg {
+#if defined(__BIG_ENDIAN)
+       u8 flags;
+#define L4_KWQ_UPDATE_PG_RESERVED1 (0xF<<0)
+#define L4_KWQ_UPDATE_PG_RESERVED1_SHIFT 0
+#define L4_KWQ_UPDATE_PG_LAYER_CODE (0x7<<4)
+#define L4_KWQ_UPDATE_PG_LAYER_CODE_SHIFT 4
+#define L4_KWQ_UPDATE_PG_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_UPDATE_PG_LINKED_WITH_NEXT_SHIFT 7
+       u8 opcode;
+       u16 oper16;
+#elif defined(__LITTLE_ENDIAN)
+       u16 oper16;
+       u8 opcode;
+       u8 flags;
+#define L4_KWQ_UPDATE_PG_RESERVED1 (0xF<<0)
+#define L4_KWQ_UPDATE_PG_RESERVED1_SHIFT 0
+#define L4_KWQ_UPDATE_PG_LAYER_CODE (0x7<<4)
+#define L4_KWQ_UPDATE_PG_LAYER_CODE_SHIFT 4
+#define L4_KWQ_UPDATE_PG_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_UPDATE_PG_LINKED_WITH_NEXT_SHIFT 7
+#endif
+       u32 pg_cid;
+       u32 pg_host_opaque;
+#if defined(__BIG_ENDIAN)
+       u8 pg_valids;
+#define L4_KWQ_UPDATE_PG_VALIDS_IPID_COUNT (0x1<<0)
+#define L4_KWQ_UPDATE_PG_VALIDS_IPID_COUNT_SHIFT 0
+#define L4_KWQ_UPDATE_PG_VALIDS_DA (0x1<<1)
+#define L4_KWQ_UPDATE_PG_VALIDS_DA_SHIFT 1
+#define L4_KWQ_UPDATE_PG_RESERVERD2 (0x3F<<2)
+#define L4_KWQ_UPDATE_PG_RESERVERD2_SHIFT 2
+       u8 pg_unused_a;
+       u16 pg_ipid_count;
+#elif defined(__LITTLE_ENDIAN)
+       u16 pg_ipid_count;
+       u8 pg_unused_a;
+       u8 pg_valids;
+#define L4_KWQ_UPDATE_PG_VALIDS_IPID_COUNT (0x1<<0)
+#define L4_KWQ_UPDATE_PG_VALIDS_IPID_COUNT_SHIFT 0
+#define L4_KWQ_UPDATE_PG_VALIDS_DA (0x1<<1)
+#define L4_KWQ_UPDATE_PG_VALIDS_DA_SHIFT 1
+#define L4_KWQ_UPDATE_PG_RESERVERD2 (0x3F<<2)
+#define L4_KWQ_UPDATE_PG_RESERVERD2_SHIFT 2
+#endif
+#if defined(__BIG_ENDIAN)
+       u16 reserverd3;
+       u8 da0;
+       u8 da1;
+#elif defined(__LITTLE_ENDIAN)
+       u8 da1;
+       u8 da0;
+       u16 reserverd3;
+#endif
+#if defined(__BIG_ENDIAN)
+       u8 da2;
+       u8 da3;
+       u8 da4;
+       u8 da5;
+#elif defined(__LITTLE_ENDIAN)
+       u8 da5;
+       u8 da4;
+       u8 da3;
+       u8 da2;
+#endif
+       u32 reserved4;
+       u32 reserved5;
+};
+
+
+/*
+ * a KWQE request to upload a PG or L4 context
+ */
+struct l4_kwq_upload {
+#if defined(__BIG_ENDIAN)
+       u8 flags;
+#define L4_KWQ_UPLOAD_RESERVED1 (0xF<<0)
+#define L4_KWQ_UPLOAD_RESERVED1_SHIFT 0
+#define L4_KWQ_UPLOAD_LAYER_CODE (0x7<<4)
+#define L4_KWQ_UPLOAD_LAYER_CODE_SHIFT 4
+#define L4_KWQ_UPLOAD_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_UPLOAD_LINKED_WITH_NEXT_SHIFT 7
+       u8 opcode;
+       u16 oper16;
+#elif defined(__LITTLE_ENDIAN)
+       u16 oper16;
+       u8 opcode;
+       u8 flags;
+#define L4_KWQ_UPLOAD_RESERVED1 (0xF<<0)
+#define L4_KWQ_UPLOAD_RESERVED1_SHIFT 0
+#define L4_KWQ_UPLOAD_LAYER_CODE (0x7<<4)
+#define L4_KWQ_UPLOAD_LAYER_CODE_SHIFT 4
+#define L4_KWQ_UPLOAD_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_UPLOAD_LINKED_WITH_NEXT_SHIFT 7
+#endif
+       u32 cid;
+       u32 reserved2[6];
+};
+
+#endif /* CNIC_DEFS_H */
diff --git a/drivers/net/cnic_if.h b/drivers/net/cnic_if.h
new file mode 100644 (file)
index 0000000..0638096
--- /dev/null
@@ -0,0 +1,299 @@
+/* cnic_if.h: Broadcom CNIC core network driver.
+ *
+ * Copyright (c) 2006 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.
+ *
+ */
+
+
+#ifndef CNIC_IF_H
+#define CNIC_IF_H
+
+#define CNIC_MODULE_VERSION    "2.0.0"
+#define CNIC_MODULE_RELDATE    "May 21, 2009"
+
+#define CNIC_ULP_RDMA          0
+#define CNIC_ULP_ISCSI         1
+#define CNIC_ULP_L4            2
+#define MAX_CNIC_ULP_TYPE_EXT  2
+#define MAX_CNIC_ULP_TYPE      3
+
+struct kwqe {
+       u32 kwqe_op_flag;
+
+#define KWQE_OPCODE_MASK       0x00ff0000
+#define KWQE_OPCODE_SHIFT      16
+#define KWQE_FLAGS_LAYER_SHIFT 28
+#define KWQE_OPCODE(x)         ((x & KWQE_OPCODE_MASK) >> KWQE_OPCODE_SHIFT)
+
+       u32 kwqe_info0;
+       u32 kwqe_info1;
+       u32 kwqe_info2;
+       u32 kwqe_info3;
+       u32 kwqe_info4;
+       u32 kwqe_info5;
+       u32 kwqe_info6;
+};
+
+struct kwqe_16 {
+       u32 kwqe_info0;
+       u32 kwqe_info1;
+       u32 kwqe_info2;
+       u32 kwqe_info3;
+};
+
+struct kcqe {
+       u32 kcqe_info0;
+       u32 kcqe_info1;
+       u32 kcqe_info2;
+       u32 kcqe_info3;
+       u32 kcqe_info4;
+       u32 kcqe_info5;
+       u32 kcqe_info6;
+       u32 kcqe_op_flag;
+               #define KCQE_RAMROD_COMPLETION          (0x1<<27) /* Everest */
+               #define KCQE_FLAGS_LAYER_MASK           (0x7<<28)
+               #define KCQE_FLAGS_LAYER_MASK_MISC      (0<<28)
+               #define KCQE_FLAGS_LAYER_MASK_L2        (2<<28)
+               #define KCQE_FLAGS_LAYER_MASK_L3        (3<<28)
+               #define KCQE_FLAGS_LAYER_MASK_L4        (4<<28)
+               #define KCQE_FLAGS_LAYER_MASK_L5_RDMA   (5<<28)
+               #define KCQE_FLAGS_LAYER_MASK_L5_ISCSI  (6<<28)
+               #define KCQE_FLAGS_NEXT                 (1<<31)
+               #define KCQE_FLAGS_OPCODE_MASK          (0xff<<16)
+               #define KCQE_FLAGS_OPCODE_SHIFT         (16)
+               #define KCQE_OPCODE(op)                 \
+               (((op) & KCQE_FLAGS_OPCODE_MASK) >> KCQE_FLAGS_OPCODE_SHIFT)
+};
+
+#define MAX_CNIC_CTL_DATA      64
+#define MAX_DRV_CTL_DATA       64
+
+#define CNIC_CTL_STOP_CMD              1
+#define CNIC_CTL_START_CMD             2
+#define CNIC_CTL_COMPLETION_CMD                3
+
+#define DRV_CTL_IO_WR_CMD              0x101
+#define DRV_CTL_IO_RD_CMD              0x102
+#define DRV_CTL_CTX_WR_CMD             0x103
+#define DRV_CTL_CTXTBL_WR_CMD          0x104
+#define DRV_CTL_COMPLETION_CMD         0x105
+
+struct cnic_ctl_completion {
+       u32     cid;
+};
+
+struct drv_ctl_completion {
+       u32     comp_count;
+};
+
+struct cnic_ctl_info {
+       int     cmd;
+       union {
+               struct cnic_ctl_completion comp;
+               char bytes[MAX_CNIC_CTL_DATA];
+       } data;
+};
+
+struct drv_ctl_io {
+       u32             cid_addr;
+       u32             offset;
+       u32             data;
+       dma_addr_t      dma_addr;
+};
+
+struct drv_ctl_info {
+       int     cmd;
+       union {
+               struct drv_ctl_completion comp;
+               struct drv_ctl_io io;
+               char bytes[MAX_DRV_CTL_DATA];
+       } data;
+};
+
+struct cnic_ops {
+       struct module   *cnic_owner;
+       /* Calls to these functions are protected by RCU.  When
+        * unregistering, we wait for any calls to complete before
+        * continuing.
+        */
+       int             (*cnic_handler)(void *, void *);
+       int             (*cnic_ctl)(void *, struct cnic_ctl_info *);
+};
+
+#define MAX_CNIC_VEC   8
+
+struct cnic_irq {
+       unsigned int    vector;
+       void            *status_blk;
+       u32             status_blk_num;
+       u32             irq_flags;
+#define CNIC_IRQ_FL_MSIX               0x00000001
+};
+
+struct cnic_eth_dev {
+       struct module   *drv_owner;
+       u32             drv_state;
+#define CNIC_DRV_STATE_REGD            0x00000001
+#define CNIC_DRV_STATE_USING_MSIX      0x00000002
+       u32             chip_id;
+       u32             max_kwqe_pending;
+       struct pci_dev  *pdev;
+       void __iomem    *io_base;
+
+       u32             ctx_tbl_offset;
+       u32             ctx_tbl_len;
+       int             ctx_blk_size;
+       u32             starting_cid;
+       u32             max_iscsi_conn;
+       u32             max_fcoe_conn;
+       u32             max_rdma_conn;
+       u32             reserved0[2];
+
+       int             num_irq;
+       struct cnic_irq irq_arr[MAX_CNIC_VEC];
+       int             (*drv_register_cnic)(struct net_device *,
+                                            struct cnic_ops *, void *);
+       int             (*drv_unregister_cnic)(struct net_device *);
+       int             (*drv_submit_kwqes_32)(struct net_device *,
+                                              struct kwqe *[], u32);
+       int             (*drv_submit_kwqes_16)(struct net_device *,
+                                              struct kwqe_16 *[], u32);
+       int             (*drv_ctl)(struct net_device *, struct drv_ctl_info *);
+       unsigned long   reserved1[2];
+};
+
+struct cnic_sockaddr {
+       union {
+               struct sockaddr_in      v4;
+               struct sockaddr_in6     v6;
+       } local;
+       union {
+               struct sockaddr_in      v4;
+               struct sockaddr_in6     v6;
+       } remote;
+};
+
+struct cnic_sock {
+       struct cnic_dev *dev;
+       void    *context;
+       u32     src_ip[4];
+       u32     dst_ip[4];
+       u16     src_port;
+       u16     dst_port;
+       u16     vlan_id;
+       unsigned char old_ha[6];
+       unsigned char ha[6];
+       u32     mtu;
+       u32     cid;
+       u32     l5_cid;
+       u32     pg_cid;
+       int     ulp_type;
+
+       u32     ka_timeout;
+       u32     ka_interval;
+       u8      ka_max_probe_count;
+       u8      tos;
+       u8      ttl;
+       u8      snd_seq_scale;
+       u32     rcv_buf;
+       u32     snd_buf;
+       u32     seed;
+
+       unsigned long   tcp_flags;
+#define SK_TCP_NO_DELAY_ACK    0x1
+#define SK_TCP_KEEP_ALIVE      0x2
+#define SK_TCP_NAGLE           0x4
+#define SK_TCP_TIMESTAMP       0x8
+#define SK_TCP_SACK            0x10
+#define SK_TCP_SEG_SCALING     0x20
+       unsigned long   flags;
+#define SK_F_INUSE             0
+#define SK_F_OFFLD_COMPLETE    1
+#define SK_F_OFFLD_SCHED       2
+#define SK_F_PG_OFFLD_COMPLETE 3
+#define SK_F_CONNECT_START     4
+#define SK_F_IPV6              5
+#define SK_F_CLOSING           7
+
+       atomic_t ref_count;
+       u32 state;
+       struct kwqe kwqe1;
+       struct kwqe kwqe2;
+       struct kwqe kwqe3;
+};
+
+struct cnic_dev {
+       struct net_device       *netdev;
+       struct pci_dev          *pcidev;
+       void __iomem            *regview;
+       struct list_head        list;
+
+       int (*register_device)(struct cnic_dev *dev, int ulp_type,
+                              void *ulp_ctx);
+       int (*unregister_device)(struct cnic_dev *dev, int ulp_type);
+       int (*submit_kwqes)(struct cnic_dev *dev, struct kwqe *wqes[],
+                               u32 num_wqes);
+       int (*submit_kwqes_16)(struct cnic_dev *dev, struct kwqe_16 *wqes[],
+                               u32 num_wqes);
+
+       int (*cm_create)(struct cnic_dev *, int, u32, u32, struct cnic_sock **,
+                        void *);
+       int (*cm_destroy)(struct cnic_sock *);
+       int (*cm_connect)(struct cnic_sock *, struct cnic_sockaddr *);
+       int (*cm_abort)(struct cnic_sock *);
+       int (*cm_close)(struct cnic_sock *);
+       struct cnic_dev *(*cm_select_dev)(struct sockaddr_in *, int ulp_type);
+       int (*iscsi_nl_msg_recv)(struct cnic_dev *dev, u32 msg_type,
+                                char *data, u16 data_size);
+       unsigned long   flags;
+#define CNIC_F_CNIC_UP         1
+#define CNIC_F_BNX2_CLASS      3
+#define CNIC_F_BNX2X_CLASS     4
+       atomic_t        ref_count;
+       u8              mac_addr[6];
+
+       int             max_iscsi_conn;
+       int             max_fcoe_conn;
+       int             max_rdma_conn;
+
+       void            *cnic_priv;
+};
+
+#define CNIC_WR(dev, off, val)         writel(val, dev->regview + off)
+#define CNIC_WR16(dev, off, val)       writew(val, dev->regview + off)
+#define CNIC_WR8(dev, off, val)                writeb(val, dev->regview + off)
+#define CNIC_RD(dev, off)              readl(dev->regview + off)
+#define CNIC_RD16(dev, off)            readw(dev->regview + off)
+
+struct cnic_ulp_ops {
+       /* Calls to these functions are protected by RCU.  When
+        * unregistering, we wait for any calls to complete before
+        * continuing.
+        */
+
+       void (*cnic_init)(struct cnic_dev *dev);
+       void (*cnic_exit)(struct cnic_dev *dev);
+       void (*cnic_start)(void *ulp_ctx);
+       void (*cnic_stop)(void *ulp_ctx);
+       void (*indicate_kcqes)(void *ulp_ctx, struct kcqe *cqes[],
+                               u32 num_cqes);
+       void (*indicate_netevent)(void *ulp_ctx, unsigned long event);
+       void (*cm_connect_complete)(struct cnic_sock *);
+       void (*cm_close_complete)(struct cnic_sock *);
+       void (*cm_abort_complete)(struct cnic_sock *);
+       void (*cm_remote_close)(struct cnic_sock *);
+       void (*cm_remote_abort)(struct cnic_sock *);
+       void (*iscsi_nl_send_msg)(struct cnic_dev *dev, u32 msg_type,
+                                 char *data, u16 data_size);
+       struct module *owner;
+};
+
+extern int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops);
+
+extern int cnic_unregister_driver(int ulp_type);
+
+#endif
index 4d1d47953fc6e4d780c538b5ffd00c901ffc06bf..7fa620ddeb2170ea7b3393ea6b5cfdbf9adfb2e8 100644 (file)
@@ -845,6 +845,10 @@ static int virtnet_probe(struct virtio_device *vdev)
        int err;
        struct net_device *dev;
        struct virtnet_info *vi;
+       struct virtqueue *vqs[3];
+       vq_callback_t *callbacks[] = { skb_recv_done, skb_xmit_done, NULL};
+       const char *names[] = { "input", "output", "control" };
+       int nvqs;
 
        /* Allocate ourselves a network device with room for our info */
        dev = alloc_etherdev(sizeof(struct virtnet_info));
@@ -905,25 +909,19 @@ static int virtnet_probe(struct virtio_device *vdev)
        if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF))
                vi->mergeable_rx_bufs = true;
 
-       /* We expect two virtqueues, receive then send. */
-       vi->rvq = vdev->config->find_vq(vdev, 0, skb_recv_done);
-       if (IS_ERR(vi->rvq)) {
-               err = PTR_ERR(vi->rvq);
+       /* We expect two virtqueues, receive then send,
+        * and optionally control. */
+       nvqs = virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ) ? 3 : 2;
+
+       err = vdev->config->find_vqs(vdev, nvqs, vqs, callbacks, names);
+       if (err)
                goto free;
-       }
 
-       vi->svq = vdev->config->find_vq(vdev, 1, skb_xmit_done);
-       if (IS_ERR(vi->svq)) {
-               err = PTR_ERR(vi->svq);
-               goto free_recv;
-       }
+       vi->rvq = vqs[0];
+       vi->svq = vqs[1];
 
        if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ)) {
-               vi->cvq = vdev->config->find_vq(vdev, 2, NULL);
-               if (IS_ERR(vi->cvq)) {
-                       err = PTR_ERR(vi->svq);
-                       goto free_send;
-               }
+               vi->cvq = vqs[2];
 
                if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VLAN))
                        dev->features |= NETIF_F_HW_VLAN_FILTER;
@@ -941,7 +939,7 @@ static int virtnet_probe(struct virtio_device *vdev)
        err = register_netdev(dev);
        if (err) {
                pr_debug("virtio_net: registering device failed\n");
-               goto free_ctrl;
+               goto free_vqs;
        }
 
        /* Last of all, set up some receive buffers. */
@@ -962,13 +960,8 @@ static int virtnet_probe(struct virtio_device *vdev)
 
 unregister:
        unregister_netdev(dev);
-free_ctrl:
-       if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ))
-               vdev->config->del_vq(vi->cvq);
-free_send:
-       vdev->config->del_vq(vi->svq);
-free_recv:
-       vdev->config->del_vq(vi->rvq);
+free_vqs:
+       vdev->config->del_vqs(vdev);
 free:
        free_netdev(dev);
        return err;
@@ -994,12 +987,10 @@ static void virtnet_remove(struct virtio_device *vdev)
 
        BUG_ON(vi->num != 0);
 
-       vdev->config->del_vq(vi->svq);
-       vdev->config->del_vq(vi->rvq);
-       if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ))
-               vdev->config->del_vq(vi->cvq);
        unregister_netdev(vi->dev);
 
+       vdev->config->del_vqs(vi->vdev);
+
        while (vi->pages)
                __free_pages(get_a_page(vi, GFP_KERNEL), 0);
 
index f821dbc952a42bb2e53a49c110decc859eff3d55..27f3b81333dec00bf0c5c212b2f0162261a40564 100644 (file)
@@ -1,21 +1,21 @@
 config OF_DEVICE
        def_bool y
-       depends on OF && (SPARC || PPC_OF)
+       depends on OF && (SPARC || PPC_OF || MICROBLAZE)
 
 config OF_GPIO
        def_bool y
-       depends on OF && PPC_OF && GPIOLIB
+       depends on OF && (PPC_OF || MICROBLAZE) && GPIOLIB
        help
          OpenFirmware GPIO accessors
 
 config OF_I2C
        def_tristate I2C
-       depends on PPC_OF && I2C
+       depends on (PPC_OF || MICROBLAZE) && I2C
        help
          OpenFirmware I2C accessors
 
 config OF_SPI
        def_tristate SPI
-       depends on OF && PPC_OF && SPI
+       depends on OF && (PPC_OF || MICROBLAZE) && SPI
        help
          OpenFirmware SPI accessors
index f604061d2bb0334981540e592b3aeee02320489c..ba97654278862ea5824c9645bcaa740dfcfe3061 100644 (file)
@@ -638,6 +638,24 @@ int pnp_possible_config(struct pnp_dev *dev, int type, resource_size_t start,
 }
 EXPORT_SYMBOL(pnp_possible_config);
 
+int pnp_range_reserved(resource_size_t start, resource_size_t end)
+{
+       struct pnp_dev *dev;
+       struct pnp_resource *pnp_res;
+       resource_size_t *dev_start, *dev_end;
+
+       pnp_for_each_dev(dev) {
+               list_for_each_entry(pnp_res, &dev->resources, list) {
+                       dev_start = &pnp_res->res.start;
+                       dev_end   = &pnp_res->res.end;
+                       if (ranged_conflict(&start, &end, dev_start, dev_end))
+                               return 1;
+               }
+       }
+       return 0;
+}
+EXPORT_SYMBOL(pnp_range_reserved);
+
 /* format is: pnp_reserve_irq=irq1[,irq2] .... */
 static int __init pnp_setup_reserve_irq(char *str)
 {
index cbc8566fab705f954aae795f18ff58744b93b9c7..e38e5d306faf577f9ccc9bfb25a75a1f29c4785e 100644 (file)
@@ -173,8 +173,9 @@ static void kvm_notify(struct virtqueue *vq)
  * this device and sets it up.
  */
 static struct virtqueue *kvm_find_vq(struct virtio_device *vdev,
-                                   unsigned index,
-                                   void (*callback)(struct virtqueue *vq))
+                                    unsigned index,
+                                    void (*callback)(struct virtqueue *vq),
+                                    const char *name)
 {
        struct kvm_device *kdev = to_kvmdev(vdev);
        struct kvm_vqconfig *config;
@@ -194,7 +195,7 @@ static struct virtqueue *kvm_find_vq(struct virtio_device *vdev,
 
        vq = vring_new_virtqueue(config->num, KVM_S390_VIRTIO_RING_ALIGN,
                                 vdev, (void *) config->address,
-                                kvm_notify, callback);
+                                kvm_notify, callback, name);
        if (!vq) {
                err = -ENOMEM;
                goto unmap;
@@ -226,6 +227,38 @@ static void kvm_del_vq(struct virtqueue *vq)
                                       KVM_S390_VIRTIO_RING_ALIGN));
 }
 
+static void kvm_del_vqs(struct virtio_device *vdev)
+{
+       struct virtqueue *vq, *n;
+
+       list_for_each_entry_safe(vq, n, &vdev->vqs, list)
+               kvm_del_vq(vq);
+}
+
+static int kvm_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+                       struct virtqueue *vqs[],
+                       vq_callback_t *callbacks[],
+                       const char *names[])
+{
+       struct kvm_device *kdev = to_kvmdev(vdev);
+       int i;
+
+       /* We must have this many virtqueues. */
+       if (nvqs > kdev->desc->num_vq)
+               return -ENOENT;
+
+       for (i = 0; i < nvqs; ++i) {
+               vqs[i] = kvm_find_vq(vdev, i, callbacks[i], names[i]);
+               if (IS_ERR(vqs[i]))
+                       goto error;
+       }
+       return 0;
+
+error:
+       kvm_del_vqs(vdev);
+       return PTR_ERR(vqs[i]);
+}
+
 /*
  * The config ops structure as defined by virtio config
  */
@@ -237,8 +270,8 @@ static struct virtio_config_ops kvm_vq_configspace_ops = {
        .get_status = kvm_get_status,
        .set_status = kvm_set_status,
        .reset = kvm_reset,
-       .find_vq = kvm_find_vq,
-       .del_vq = kvm_del_vq,
+       .find_vqs = kvm_find_vqs,
+       .del_vqs = kvm_del_vqs,
 };
 
 /*
index 733fe3bf6285f0e20c6d6737b600beac7ec7332e..b2fe5cdbcaeec3cb4462b22040fa6282bd70c7b5 100644 (file)
 
 #include "zfcp_ext.h"
 
+#define ZFCP_MODEL_PRIV 0x4
+
+static struct ccw_device_id zfcp_ccw_device_id[] = {
+       { CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, 0x3) },
+       { CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, ZFCP_MODEL_PRIV) },
+       {},
+};
+MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id);
+
+/**
+ * zfcp_ccw_priv_sch - check if subchannel is privileged
+ * @adapter: Adapter/Subchannel to check
+ */
+int zfcp_ccw_priv_sch(struct zfcp_adapter *adapter)
+{
+       return adapter->ccw_device->id.dev_model == ZFCP_MODEL_PRIV;
+}
+
 /**
  * zfcp_ccw_probe - probe function of zfcp driver
  * @ccw_device: pointer to belonging ccw device
@@ -176,8 +194,8 @@ static int zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
                                        "ccnoti4", NULL);
                break;
        case CIO_BOXED:
-               dev_warn(&adapter->ccw_device->dev,
-                        "The ccw device did not respond in time.\n");
+               dev_warn(&adapter->ccw_device->dev, "The FCP device "
+                        "did not respond within the specified time\n");
                zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti5", NULL);
                break;
        }
@@ -199,14 +217,6 @@ static void zfcp_ccw_shutdown(struct ccw_device *cdev)
        up(&zfcp_data.config_sema);
 }
 
-static struct ccw_device_id zfcp_ccw_device_id[] = {
-       { CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, 0x3) },
-       { CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, 0x4) }, /* priv. */
-       {},
-};
-
-MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id);
-
 static struct ccw_driver zfcp_ccw_driver = {
        .owner       = THIS_MODULE,
        .name        = "zfcp",
index 0a1a5dd8d01831b9e13986ba9fd553b7ffc55342..b99b87ce5a39618d8236167fd98b4db6e43a9a3b 100644 (file)
@@ -163,7 +163,7 @@ void zfcp_hba_dbf_event_fsf_response(struct zfcp_fsf_req *fsf_req)
        }
 
        response->fsf_command = fsf_req->fsf_command;
-       response->fsf_reqid = (unsigned long)fsf_req;
+       response->fsf_reqid = fsf_req->req_id;
        response->fsf_seqno = fsf_req->seq_no;
        response->fsf_issued = fsf_req->issued;
        response->fsf_prot_status = qtcb->prefix.prot_status;
@@ -737,7 +737,7 @@ void zfcp_san_dbf_event_ct_request(struct zfcp_fsf_req *fsf_req)
        spin_lock_irqsave(&adapter->san_dbf_lock, flags);
        memset(r, 0, sizeof(*r));
        strncpy(r->tag, "octc", ZFCP_DBF_TAG_SIZE);
-       r->fsf_reqid = (unsigned long)fsf_req;
+       r->fsf_reqid = fsf_req->req_id;
        r->fsf_seqno = fsf_req->seq_no;
        r->s_id = fc_host_port_id(adapter->scsi_host);
        r->d_id = wka_port->d_id;
@@ -773,7 +773,7 @@ void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *fsf_req)
        spin_lock_irqsave(&adapter->san_dbf_lock, flags);
        memset(r, 0, sizeof(*r));
        strncpy(r->tag, "rctc", ZFCP_DBF_TAG_SIZE);
-       r->fsf_reqid = (unsigned long)fsf_req;
+       r->fsf_reqid = fsf_req->req_id;
        r->fsf_seqno = fsf_req->seq_no;
        r->s_id = wka_port->d_id;
        r->d_id = fc_host_port_id(adapter->scsi_host);
@@ -803,7 +803,7 @@ static void zfcp_san_dbf_event_els(const char *tag, int level,
        spin_lock_irqsave(&adapter->san_dbf_lock, flags);
        memset(rec, 0, sizeof(*rec));
        strncpy(rec->tag, tag, ZFCP_DBF_TAG_SIZE);
-       rec->fsf_reqid = (unsigned long)fsf_req;
+       rec->fsf_reqid = fsf_req->req_id;
        rec->fsf_seqno = fsf_req->seq_no;
        rec->s_id = s_id;
        rec->d_id = d_id;
@@ -965,7 +965,7 @@ static void zfcp_scsi_dbf_event(const char *tag, const char *tag2, int level,
                                                      ZFCP_DBF_SCSI_FCP_SNS_INFO);
                                }
 
-                               rec->fsf_reqid = (unsigned long)fsf_req;
+                               rec->fsf_reqid = fsf_req->req_id;
                                rec->fsf_seqno = fsf_req->seq_no;
                                rec->fsf_issued = fsf_req->issued;
                        }
index 4c362a9069f07463c8db6869a86575099024300a..2074d45dbf6c4b1a426b5f9aafc39a11e1c41fe9 100644 (file)
 
 /********************* CIO/QDIO SPECIFIC DEFINES *****************************/
 
-/* Adapter Identification Parameters */
-#define ZFCP_CONTROL_UNIT_TYPE  0x1731
-#define ZFCP_CONTROL_UNIT_MODEL 0x03
-#define ZFCP_DEVICE_TYPE        0x1732
-#define ZFCP_DEVICE_MODEL       0x03
-#define ZFCP_DEVICE_MODEL_PRIV 0x04
-
 /* DMQ bug workaround: don't use last SBALE */
 #define ZFCP_MAX_SBALES_PER_SBAL       (QDIO_MAX_ELEMENTS_PER_BUFFER - 1)
 
index fdc9b4352a6493e888ffb9ac4535619c2b379bc9..e50ea465bc2b993b44829a7d1554259e475fa2da 100644 (file)
@@ -880,6 +880,7 @@ static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *act)
                                zfcp_port_put(port);
                        return ZFCP_ERP_CONTINUES;
                }
+               /* fall through */
        case ZFCP_ERP_STEP_NAMESERVER_LOOKUP:
                if (!port->d_id)
                        return ZFCP_ERP_FAILED;
@@ -894,8 +895,13 @@ static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *act)
                                act->step = ZFCP_ERP_STEP_PORT_CLOSING;
                                return ZFCP_ERP_CONTINUES;
                        }
-               /* fall through otherwise */
                }
+               if (port->d_id && !(p_status & ZFCP_STATUS_COMMON_NOESC)) {
+                       port->d_id = 0;
+                       _zfcp_erp_port_reopen(port, 0, "erpsoc1", NULL);
+                       return ZFCP_ERP_EXIT;
+               }
+               /* fall through otherwise */
        }
        return ZFCP_ERP_FAILED;
 }
index 2e31b536548c2e28344831c3f4266f32c70c5c6b..120a9a1c81f7cf491e12b208d48c346ab5586780 100644 (file)
@@ -27,6 +27,7 @@ extern int zfcp_sg_setup_table(struct scatterlist *, int);
 
 /* zfcp_ccw.c */
 extern int zfcp_ccw_register(void);
+extern int zfcp_ccw_priv_sch(struct zfcp_adapter *);
 extern struct zfcp_adapter *zfcp_get_adapter_by_busid(char *);
 
 /* zfcp_cfdc.c */
index 19ae0842047c4750f0614479381c39b76d40d7ca..bb2752b4130fde32f21982248171b895e5f5c3cc 100644 (file)
@@ -150,9 +150,14 @@ static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range,
        struct zfcp_port *port;
 
        read_lock_irqsave(&zfcp_data.config_lock, flags);
-       list_for_each_entry(port, &fsf_req->adapter->port_list_head, list)
+       list_for_each_entry(port, &fsf_req->adapter->port_list_head, list) {
                if ((port->d_id & range) == (elem->nport_did & range))
                        zfcp_test_link(port);
+               if (!port->d_id)
+                       zfcp_erp_port_reopen(port,
+                                            ZFCP_STATUS_COMMON_ERP_FAILED,
+                                            "fcrscn1", NULL);
+       }
 
        read_unlock_irqrestore(&zfcp_data.config_lock, flags);
 }
index 74dee32afba84619d564055909e26d41954e5ef3..e6dae3744e7981c348ddd554dbdf6dd27049c98f 100644 (file)
@@ -526,6 +526,7 @@ static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req)
                break;
        case FSF_TOPO_AL:
                fc_host_port_type(shost) = FC_PORTTYPE_NLPORT;
+               /* fall through */
        default:
                dev_err(&adapter->ccw_device->dev,
                        "Unknown or unsupported arbitrated loop "
@@ -897,6 +898,7 @@ static void zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *req)
                switch (fsq->word[0]) {
                case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
                        zfcp_test_link(unit->port);
+                       /* fall through */
                case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
                        req->status |= ZFCP_STATUS_FSFREQ_ERROR;
                        break;
@@ -993,6 +995,7 @@ static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *req)
                break;
        case FSF_PORT_HANDLE_NOT_VALID:
                zfcp_erp_adapter_reopen(adapter, 0, "fsscth1", req);
+               /* fall through */
        case FSF_GENERIC_COMMAND_REJECTED:
        case FSF_PAYLOAD_SIZE_MISMATCH:
        case FSF_REQUEST_SIZE_TOO_LARGE:
@@ -1399,7 +1402,7 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
        struct fsf_plogi *plogi;
 
        if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
-               return;
+               goto out;
 
        switch (header->fsf_status) {
        case FSF_PORT_ALREADY_OPEN:
@@ -1461,6 +1464,9 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
                req->status |= ZFCP_STATUS_FSFREQ_ERROR;
                break;
        }
+
+out:
+       zfcp_port_put(port);
 }
 
 /**
@@ -1473,6 +1479,7 @@ int zfcp_fsf_open_port(struct zfcp_erp_action *erp_action)
        struct qdio_buffer_element *sbale;
        struct zfcp_adapter *adapter = erp_action->adapter;
        struct zfcp_fsf_req *req;
+       struct zfcp_port *port = erp_action->port;
        int retval = -EIO;
 
        spin_lock_bh(&adapter->req_q_lock);
@@ -1493,16 +1500,18 @@ int zfcp_fsf_open_port(struct zfcp_erp_action *erp_action)
         sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
 
        req->handler = zfcp_fsf_open_port_handler;
-       req->qtcb->bottom.support.d_id = erp_action->port->d_id;
-       req->data = erp_action->port;
+       req->qtcb->bottom.support.d_id = port->d_id;
+       req->data = port;
        req->erp_action = erp_action;
        erp_action->fsf_req = req;
+       zfcp_port_get(port);
 
        zfcp_fsf_start_erp_timer(req);
        retval = zfcp_fsf_req_send(req);
        if (retval) {
                zfcp_fsf_req_free(req);
                erp_action->fsf_req = NULL;
+               zfcp_port_put(port);
        }
 out:
        spin_unlock_bh(&adapter->req_q_lock);
@@ -1590,8 +1599,10 @@ static void zfcp_fsf_open_wka_port_handler(struct zfcp_fsf_req *req)
        case FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED:
                dev_warn(&req->adapter->ccw_device->dev,
                         "Opening WKA port 0x%x failed\n", wka_port->d_id);
+               /* fall through */
        case FSF_ADAPTER_STATUS_AVAILABLE:
                req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+               /* fall through */
        case FSF_ACCESS_DENIED:
                wka_port->status = ZFCP_WKA_PORT_OFFLINE;
                break;
@@ -1876,7 +1887,7 @@ static void zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *req)
 
                if (!(adapter->connection_features & FSF_FEATURE_NPIV_MODE) &&
                    (adapter->adapter_features & FSF_FEATURE_LUN_SHARING) &&
-                   (adapter->ccw_device->id.dev_model != ZFCP_DEVICE_MODEL_PRIV)) {
+                   !zfcp_ccw_priv_sch(adapter)) {
                        exclusive = (bottom->lun_access_info &
                                        FSF_UNIT_ACCESS_EXCLUSIVE);
                        readwrite = (bottom->lun_access_info &
@@ -2314,7 +2325,7 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
 {
        struct zfcp_fsf_req *req;
        struct fcp_cmnd_iu *fcp_cmnd_iu;
-       unsigned int sbtype;
+       unsigned int sbtype = SBAL_FLAGS0_TYPE_READ;
        int real_bytes, retval = -EIO;
        struct zfcp_adapter *adapter = unit->port->adapter;
 
@@ -2356,11 +2367,9 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
        switch (scsi_cmnd->sc_data_direction) {
        case DMA_NONE:
                req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND;
-               sbtype = SBAL_FLAGS0_TYPE_READ;
                break;
        case DMA_FROM_DEVICE:
                req->qtcb->bottom.io.data_direction = FSF_DATADIR_READ;
-               sbtype = SBAL_FLAGS0_TYPE_READ;
                fcp_cmnd_iu->rddata = 1;
                break;
        case DMA_TO_DEVICE:
@@ -2369,8 +2378,6 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
                fcp_cmnd_iu->wddata = 1;
                break;
        case DMA_BIDIRECTIONAL:
-       default:
-               retval = -EIO;
                goto failed_scsi_cmnd;
        }
 
@@ -2394,9 +2401,7 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
                                             scsi_sglist(scsi_cmnd),
                                             FSF_MAX_SBALS_PER_REQ);
        if (unlikely(real_bytes < 0)) {
-               if (req->sbal_number < FSF_MAX_SBALS_PER_REQ)
-                       retval = -EIO;
-               else {
+               if (req->sbal_number >= FSF_MAX_SBALS_PER_REQ) {
                        dev_err(&adapter->ccw_device->dev,
                                "Oversize data package, unit 0x%016Lx "
                                "on port 0x%016Lx closed\n",
index e8fbeaeb5fbfb4046d1a04ed3cab14e642bdce2a..7d0da230eb637ce92468f2d6565e1b04be4fdde3 100644 (file)
 #include "zfcp_ext.h"
 #include <asm/atomic.h>
 
+static unsigned int default_depth = 32;
+module_param_named(queue_depth, default_depth, uint, 0600);
+MODULE_PARM_DESC(queue_depth, "Default queue depth for new SCSI devices");
+
 /* Find start of Sense Information in FCP response unit*/
 char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *fcp_rsp_iu)
 {
@@ -24,6 +28,12 @@ char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *fcp_rsp_iu)
        return fcp_sns_info_ptr;
 }
 
+static int zfcp_scsi_change_queue_depth(struct scsi_device *sdev, int depth)
+{
+       scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+       return sdev->queue_depth;
+}
+
 static void zfcp_scsi_slave_destroy(struct scsi_device *sdpnt)
 {
        struct zfcp_unit *unit = (struct zfcp_unit *) sdpnt->hostdata;
@@ -34,7 +44,7 @@ static void zfcp_scsi_slave_destroy(struct scsi_device *sdpnt)
 static int zfcp_scsi_slave_configure(struct scsi_device *sdp)
 {
        if (sdp->tagged_supported)
-               scsi_adjust_queue_depth(sdp, MSG_SIMPLE_TAG, 32);
+               scsi_adjust_queue_depth(sdp, MSG_SIMPLE_TAG, default_depth);
        else
                scsi_adjust_queue_depth(sdp, 0, 1);
        return 0;
@@ -647,6 +657,7 @@ struct zfcp_data zfcp_data = {
                .name                    = "zfcp",
                .module                  = THIS_MODULE,
                .proc_name               = "zfcp",
+               .change_queue_depth      = zfcp_scsi_change_queue_depth,
                .slave_alloc             = zfcp_scsi_slave_alloc,
                .slave_configure         = zfcp_scsi_slave_configure,
                .slave_destroy           = zfcp_scsi_slave_destroy,
index fb2740789b6879d48ce2501272e3ddc53337108c..6a19ed9a1194aff51553bed7ad9384f5925d6779 100644 (file)
@@ -191,20 +191,19 @@ config SCSI_ENCLOSURE
          it has an enclosure device.  Selecting this option will just allow
          certain enclosure conditions to be reported and is not required.
 
-comment "Some SCSI devices (e.g. CD jukebox) support multiple LUNs"
-       depends on SCSI
-
 config SCSI_MULTI_LUN
        bool "Probe all LUNs on each SCSI device"
        depends on SCSI
        help
-         If you have a SCSI device that supports more than one LUN (Logical
-         Unit Number), e.g. a CD jukebox, and only one LUN is detected, you
-         can say Y here to force the SCSI driver to probe for multiple LUNs.
-         A SCSI device with multiple LUNs acts logically like multiple SCSI
-         devices. The vast majority of SCSI devices have only one LUN, and
-         so most people can say N here. The max_luns boot/module parameter 
-         allows to override this setting.
+         Some devices support more than one LUN (Logical Unit Number) in order
+         to allow access to several media, e.g. CD jukebox, USB card reader,
+         mobile phone in mass storage mode. This option forces the kernel to
+         probe for all LUNs by default. This setting can be overriden by
+         max_luns boot/module parameter. Note that this option does not affect
+         devices conforming to SCSI-3 or higher as they can explicitely report
+         their number of LUNs. It is safe to say Y here unless you have one of
+         those rare devices which reacts in an unexpected way when probed for
+         multiple LUNs.
 
 config SCSI_CONSTANTS
        bool "Verbose SCSI error reporting (kernel size +=12K)"
@@ -355,6 +354,7 @@ config ISCSI_TCP
         http://open-iscsi.org
 
 source "drivers/scsi/cxgb3i/Kconfig"
+source "drivers/scsi/bnx2i/Kconfig"
 
 config SGIWD93_SCSI
        tristate "SGI WD93C93 SCSI Driver"
@@ -508,6 +508,7 @@ config SCSI_AIC7XXX_OLD
 
 source "drivers/scsi/aic7xxx/Kconfig.aic79xx"
 source "drivers/scsi/aic94xx/Kconfig"
+source "drivers/scsi/mvsas/Kconfig"
 
 config SCSI_DPT_I2O
        tristate "Adaptec I2O RAID support "
@@ -1050,16 +1051,6 @@ config SCSI_IZIP_SLOW_CTR
 
          Generally, saying N is fine.
 
-config SCSI_MVSAS
-       tristate "Marvell 88SE6440 SAS/SATA support"
-       depends on PCI && SCSI
-       select SCSI_SAS_LIBSAS
-       help
-         This driver supports Marvell SAS/SATA PCI devices.
-
-         To compiler this driver as a module, choose M here: the module
-         will be called mvsas.
-
 config SCSI_NCR53C406A
        tristate "NCR53c406a SCSI support"
        depends on ISA && SCSI
index a5049cfb40edb1c4e3c69aa9e4e95076590a0e57..25429ea63d0ad0708d236e1242819b6fc152e676 100644 (file)
@@ -126,9 +126,10 @@ obj-$(CONFIG_SCSI_IBMVSCSIS)       += ibmvscsi/
 obj-$(CONFIG_SCSI_IBMVFC)      += ibmvscsi/
 obj-$(CONFIG_SCSI_HPTIOP)      += hptiop.o
 obj-$(CONFIG_SCSI_STEX)                += stex.o
-obj-$(CONFIG_SCSI_MVSAS)       += mvsas.o
+obj-$(CONFIG_SCSI_MVSAS)       += mvsas/
 obj-$(CONFIG_PS3_ROM)          += ps3rom.o
 obj-$(CONFIG_SCSI_CXGB3_ISCSI) += libiscsi.o libiscsi_tcp.o cxgb3i/
+obj-$(CONFIG_SCSI_BNX2_ISCSI)  += libiscsi.o bnx2i/
 
 obj-$(CONFIG_ARM)              += arm/
 
index c889d8458684b002d5e03bd9bebf3a4e639cf4c4..1cdf09a4779a560532e7dc2edafc4dc4f91e2170 100644 (file)
@@ -224,7 +224,7 @@ NCR_D700_probe_one(struct NCR_D700_private *p, int siop, int irq,
        return ret;
 }
 
-static int
+static irqreturn_t
 NCR_D700_intr(int irq, void *data)
 {
        struct NCR_D700_private *p = (struct NCR_D700_private *)data;
diff --git a/drivers/scsi/bnx2i/57xx_iscsi_constants.h b/drivers/scsi/bnx2i/57xx_iscsi_constants.h
new file mode 100644 (file)
index 0000000..2fceb19
--- /dev/null
@@ -0,0 +1,155 @@
+/* 57xx_iscsi_constants.h: Broadcom NetXtreme II iSCSI HSI
+ *
+ * Copyright (c) 2006 - 2009 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: Anil Veerabhadrappa (anilgv@broadcom.com)
+ */
+#ifndef __57XX_ISCSI_CONSTANTS_H_
+#define __57XX_ISCSI_CONSTANTS_H_
+
+/**
+* This file defines HSI constants for the iSCSI flows
+*/
+
+/* iSCSI request op codes */
+#define ISCSI_OPCODE_CLEANUP_REQUEST    (7)
+
+/* iSCSI response/messages op codes */
+#define ISCSI_OPCODE_CLEANUP_RESPONSE          (0x27)
+#define ISCSI_OPCODE_NOPOUT_LOCAL_COMPLETION    (0)
+
+/* iSCSI task types */
+#define ISCSI_TASK_TYPE_READ    (0)
+#define ISCSI_TASK_TYPE_WRITE   (1)
+#define ISCSI_TASK_TYPE_MPATH   (2)
+
+/* initial CQ sequence numbers */
+#define ISCSI_INITIAL_SN    (1)
+
+/* KWQ (kernel work queue) layer codes */
+#define ISCSI_KWQE_LAYER_CODE   (6)
+
+/* KWQ (kernel work queue) request op codes */
+#define ISCSI_KWQE_OPCODE_OFFLOAD_CONN1 (0)
+#define ISCSI_KWQE_OPCODE_OFFLOAD_CONN2 (1)
+#define ISCSI_KWQE_OPCODE_UPDATE_CONN   (2)
+#define ISCSI_KWQE_OPCODE_DESTROY_CONN  (3)
+#define ISCSI_KWQE_OPCODE_INIT1         (4)
+#define ISCSI_KWQE_OPCODE_INIT2         (5)
+
+/* KCQ (kernel completion queue) response op codes */
+#define ISCSI_KCQE_OPCODE_OFFLOAD_CONN  (0x10)
+#define ISCSI_KCQE_OPCODE_UPDATE_CONN   (0x12)
+#define ISCSI_KCQE_OPCODE_DESTROY_CONN  (0x13)
+#define ISCSI_KCQE_OPCODE_INIT          (0x14)
+#define ISCSI_KCQE_OPCODE_FW_CLEAN_TASK        (0x15)
+#define ISCSI_KCQE_OPCODE_TCP_RESET     (0x16)
+#define ISCSI_KCQE_OPCODE_TCP_SYN       (0x17)
+#define ISCSI_KCQE_OPCODE_TCP_FIN       (0X18)
+#define ISCSI_KCQE_OPCODE_TCP_ERROR     (0x19)
+#define ISCSI_KCQE_OPCODE_CQ_EVENT_NOTIFICATION (0x20)
+#define ISCSI_KCQE_OPCODE_ISCSI_ERROR   (0x21)
+
+/* KCQ (kernel completion queue) completion status */
+#define ISCSI_KCQE_COMPLETION_STATUS_SUCCESS                            (0x0)
+#define ISCSI_KCQE_COMPLETION_STATUS_INVALID_OPCODE                     (0x1)
+#define ISCSI_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAILURE                  (0x2)
+#define ISCSI_KCQE_COMPLETION_STATUS_CTX_FREE_FAILURE                   (0x3)
+#define ISCSI_KCQE_COMPLETION_STATUS_NIC_ERROR                          (0x4)
+
+#define ISCSI_KCQE_COMPLETION_STATUS_HDR_DIG_ERR                        (0x5)
+#define ISCSI_KCQE_COMPLETION_STATUS_DATA_DIG_ERR                       (0x6)
+
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_UNEXPECTED_OPCODE     (0xa)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_OPCODE                (0xb)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_AHS_LEN               (0xc)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_ITT                   (0xd)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_STATSN                (0xe)
+
+/* Response */
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_EXP_DATASN            (0xf)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T              (0x10)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DATA_SEG_LEN_IS_ZERO  (0x2c)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DATA_SEG_LEN_TOO_BIG  (0x2d)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_0                 (0x11)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_1                 (0x12)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_2                 (0x13)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_3                 (0x14)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_4                 (0x15)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_5                 (0x16)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_6                 (0x17)
+
+/* Data-In */
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REMAIN_RCV_LEN        (0x18)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_MAX_RCV_PDU_LEN       (0x19)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_F_BIT_ZERO            (0x1a)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_TTT_NOT_RSRV          (0x1b)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DATASN                (0x1c)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REMAIN_BURST_LEN      (0x1d)
+
+/* R2T */
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_BUFFER_OFF            (0x1f)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_LUN                   (0x20)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_R2TSN                 (0x21)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DESIRED_DATA_TRNS_LEN_0 (0x22)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DESIRED_DATA_TRNS_LEN_1 (0x23)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T_EXCEED       (0x24)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_TTT_IS_RSRV           (0x25)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_MAX_BURST_LEN         (0x26)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DATA_SEG_LEN_NOT_ZERO (0x27)
+
+/* TMF */
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REJECT_PDU_LEN        (0x28)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_ASYNC_PDU_LEN         (0x29)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_NOPIN_PDU_LEN         (0x2a)
+#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T_IN_CLEANUP   (0x2b)
+
+/* IP/TCP processing errors: */
+#define ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_IP_FRAGMENT               (0x40)
+#define ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_IP_OPTIONS                (0x41)
+#define ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_URGENT_FLAG               (0x42)
+#define ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_MAX_RTRANS                (0x43)
+
+/* iSCSI licensing errors */
+/* general iSCSI license not installed */
+#define ISCSI_KCQE_COMPLETION_STATUS_ISCSI_NOT_SUPPORTED                (0x50)
+/* additional LOM specific iSCSI license not installed */
+#define ISCSI_KCQE_COMPLETION_STATUS_LOM_ISCSI_NOT_ENABLED              (0x51)
+
+/* SQ/RQ/CQ DB structure sizes */
+#define ISCSI_SQ_DB_SIZE    (16)
+#define ISCSI_RQ_DB_SIZE    (16)
+#define ISCSI_CQ_DB_SIZE    (80)
+
+#define ISCSI_SQN_TO_NOTIFY_NOT_VALID                                   0xFFFF
+
+/* Page size codes (for flags field in connection offload request) */
+#define ISCSI_PAGE_SIZE_256     (0)
+#define ISCSI_PAGE_SIZE_512     (1)
+#define ISCSI_PAGE_SIZE_1K      (2)
+#define ISCSI_PAGE_SIZE_2K      (3)
+#define ISCSI_PAGE_SIZE_4K      (4)
+#define ISCSI_PAGE_SIZE_8K      (5)
+#define ISCSI_PAGE_SIZE_16K     (6)
+#define ISCSI_PAGE_SIZE_32K     (7)
+#define ISCSI_PAGE_SIZE_64K     (8)
+#define ISCSI_PAGE_SIZE_128K    (9)
+#define ISCSI_PAGE_SIZE_256K    (10)
+#define ISCSI_PAGE_SIZE_512K    (11)
+#define ISCSI_PAGE_SIZE_1M      (12)
+#define ISCSI_PAGE_SIZE_2M      (13)
+#define ISCSI_PAGE_SIZE_4M      (14)
+#define ISCSI_PAGE_SIZE_8M      (15)
+
+/* Iscsi PDU related defines */
+#define ISCSI_HEADER_SIZE   (48)
+#define ISCSI_DIGEST_SHIFT  (2)
+#define ISCSI_DIGEST_SIZE   (4)
+
+#define B577XX_ISCSI_CONNECTION_TYPE    3
+
+#endif /*__57XX_ISCSI_CONSTANTS_H_ */
diff --git a/drivers/scsi/bnx2i/57xx_iscsi_hsi.h b/drivers/scsi/bnx2i/57xx_iscsi_hsi.h
new file mode 100644 (file)
index 0000000..36af1af
--- /dev/null
@@ -0,0 +1,1509 @@
+/* 57xx_iscsi_hsi.h: Broadcom NetXtreme II iSCSI HSI.
+ *
+ * Copyright (c) 2006 - 2009 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: Anil Veerabhadrappa (anilgv@broadcom.com)
+ */
+#ifndef __57XX_ISCSI_HSI_LINUX_LE__
+#define __57XX_ISCSI_HSI_LINUX_LE__
+
+/*
+ * iSCSI Async CQE
+ */
+struct bnx2i_async_msg {
+#if defined(__BIG_ENDIAN)
+       u8 op_code;
+       u8 reserved1;
+       u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved0;
+       u8 reserved1;
+       u8 op_code;
+#endif
+       u32 reserved2;
+       u32 exp_cmd_sn;
+       u32 max_cmd_sn;
+       u32 reserved3[2];
+#if defined(__BIG_ENDIAN)
+       u16 reserved5;
+       u8 err_code;
+       u8 reserved4;
+#elif defined(__LITTLE_ENDIAN)
+       u8 reserved4;
+       u8 err_code;
+       u16 reserved5;
+#endif
+       u32 reserved6;
+       u32 lun[2];
+#if defined(__BIG_ENDIAN)
+       u8 async_event;
+       u8 async_vcode;
+       u16 param1;
+#elif defined(__LITTLE_ENDIAN)
+       u16 param1;
+       u8 async_vcode;
+       u8 async_event;
+#endif
+#if defined(__BIG_ENDIAN)
+       u16 param2;
+       u16 param3;
+#elif defined(__LITTLE_ENDIAN)
+       u16 param3;
+       u16 param2;
+#endif
+       u32 reserved7[3];
+       u32 cq_req_sn;
+};
+
+
+/*
+ * iSCSI Buffer Descriptor (BD)
+ */
+struct iscsi_bd {
+       u32 buffer_addr_hi;
+       u32 buffer_addr_lo;
+#if defined(__BIG_ENDIAN)
+       u16 reserved0;
+       u16 buffer_length;
+#elif defined(__LITTLE_ENDIAN)
+       u16 buffer_length;
+       u16 reserved0;
+#endif
+#if defined(__BIG_ENDIAN)
+       u16 reserved3;
+       u16 flags;
+#define ISCSI_BD_RESERVED1 (0x3F<<0)
+#define ISCSI_BD_RESERVED1_SHIFT 0
+#define ISCSI_BD_LAST_IN_BD_CHAIN (0x1<<6)
+#define ISCSI_BD_LAST_IN_BD_CHAIN_SHIFT 6
+#define ISCSI_BD_FIRST_IN_BD_CHAIN (0x1<<7)
+#define ISCSI_BD_FIRST_IN_BD_CHAIN_SHIFT 7
+#define ISCSI_BD_RESERVED2 (0xFF<<8)
+#define ISCSI_BD_RESERVED2_SHIFT 8
+#elif defined(__LITTLE_ENDIAN)
+       u16 flags;
+#define ISCSI_BD_RESERVED1 (0x3F<<0)
+#define ISCSI_BD_RESERVED1_SHIFT 0
+#define ISCSI_BD_LAST_IN_BD_CHAIN (0x1<<6)
+#define ISCSI_BD_LAST_IN_BD_CHAIN_SHIFT 6
+#define ISCSI_BD_FIRST_IN_BD_CHAIN (0x1<<7)
+#define ISCSI_BD_FIRST_IN_BD_CHAIN_SHIFT 7
+#define ISCSI_BD_RESERVED2 (0xFF<<8)
+#define ISCSI_BD_RESERVED2_SHIFT 8
+       u16 reserved3;
+#endif
+};
+
+
+/*
+ * iSCSI Cleanup SQ WQE
+ */
+struct bnx2i_cleanup_request {
+#if defined(__BIG_ENDIAN)
+       u8 op_code;
+       u8 reserved1;
+       u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved0;
+       u8 reserved1;
+       u8 op_code;
+#endif
+       u32 reserved2[3];
+#if defined(__BIG_ENDIAN)
+       u16 reserved3;
+       u16 itt;
+#define ISCSI_CLEANUP_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_CLEANUP_REQUEST_INDEX_SHIFT 0
+#define ISCSI_CLEANUP_REQUEST_TYPE (0x3<<14)
+#define ISCSI_CLEANUP_REQUEST_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+       u16 itt;
+#define ISCSI_CLEANUP_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_CLEANUP_REQUEST_INDEX_SHIFT 0
+#define ISCSI_CLEANUP_REQUEST_TYPE (0x3<<14)
+#define ISCSI_CLEANUP_REQUEST_TYPE_SHIFT 14
+       u16 reserved3;
+#endif
+       u32 reserved4[10];
+#if defined(__BIG_ENDIAN)
+       u8 cq_index;
+       u8 reserved6;
+       u16 reserved5;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved5;
+       u8 reserved6;
+       u8 cq_index;
+#endif
+};
+
+
+/*
+ * iSCSI Cleanup CQE
+ */
+struct bnx2i_cleanup_response {
+#if defined(__BIG_ENDIAN)
+       u8 op_code;
+       u8 status;
+       u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved0;
+       u8 status;
+       u8 op_code;
+#endif
+       u32 reserved1[3];
+       u32 reserved2[2];
+#if defined(__BIG_ENDIAN)
+       u16 reserved4;
+       u8 err_code;
+       u8 reserved3;
+#elif defined(__LITTLE_ENDIAN)
+       u8 reserved3;
+       u8 err_code;
+       u16 reserved4;
+#endif
+       u32 reserved5[7];
+#if defined(__BIG_ENDIAN)
+       u16 reserved6;
+       u16 itt;
+#define ISCSI_CLEANUP_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_CLEANUP_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_CLEANUP_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_CLEANUP_RESPONSE_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+       u16 itt;
+#define ISCSI_CLEANUP_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_CLEANUP_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_CLEANUP_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_CLEANUP_RESPONSE_TYPE_SHIFT 14
+       u16 reserved6;
+#endif
+       u32 cq_req_sn;
+};
+
+
+/*
+ * SCSI read/write SQ WQE
+ */
+struct bnx2i_cmd_request {
+#if defined(__BIG_ENDIAN)
+       u8 op_code;
+       u8 op_attr;
+#define ISCSI_CMD_REQUEST_TASK_ATTR (0x7<<0)
+#define ISCSI_CMD_REQUEST_TASK_ATTR_SHIFT 0
+#define ISCSI_CMD_REQUEST_RESERVED1 (0x3<<3)
+#define ISCSI_CMD_REQUEST_RESERVED1_SHIFT 3
+#define ISCSI_CMD_REQUEST_WRITE (0x1<<5)
+#define ISCSI_CMD_REQUEST_WRITE_SHIFT 5
+#define ISCSI_CMD_REQUEST_READ (0x1<<6)
+#define ISCSI_CMD_REQUEST_READ_SHIFT 6
+#define ISCSI_CMD_REQUEST_FINAL (0x1<<7)
+#define ISCSI_CMD_REQUEST_FINAL_SHIFT 7
+       u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved0;
+       u8 op_attr;
+#define ISCSI_CMD_REQUEST_TASK_ATTR (0x7<<0)
+#define ISCSI_CMD_REQUEST_TASK_ATTR_SHIFT 0
+#define ISCSI_CMD_REQUEST_RESERVED1 (0x3<<3)
+#define ISCSI_CMD_REQUEST_RESERVED1_SHIFT 3
+#define ISCSI_CMD_REQUEST_WRITE (0x1<<5)
+#define ISCSI_CMD_REQUEST_WRITE_SHIFT 5
+#define ISCSI_CMD_REQUEST_READ (0x1<<6)
+#define ISCSI_CMD_REQUEST_READ_SHIFT 6
+#define ISCSI_CMD_REQUEST_FINAL (0x1<<7)
+#define ISCSI_CMD_REQUEST_FINAL_SHIFT 7
+       u8 op_code;
+#endif
+#if defined(__BIG_ENDIAN)
+       u16 ud_buffer_offset;
+       u16 sd_buffer_offset;
+#elif defined(__LITTLE_ENDIAN)
+       u16 sd_buffer_offset;
+       u16 ud_buffer_offset;
+#endif
+       u32 lun[2];
+#if defined(__BIG_ENDIAN)
+       u16 reserved2;
+       u16 itt;
+#define ISCSI_CMD_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_CMD_REQUEST_INDEX_SHIFT 0
+#define ISCSI_CMD_REQUEST_TYPE (0x3<<14)
+#define ISCSI_CMD_REQUEST_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+       u16 itt;
+#define ISCSI_CMD_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_CMD_REQUEST_INDEX_SHIFT 0
+#define ISCSI_CMD_REQUEST_TYPE (0x3<<14)
+#define ISCSI_CMD_REQUEST_TYPE_SHIFT 14
+       u16 reserved2;
+#endif
+       u32 total_data_transfer_length;
+       u32 cmd_sn;
+       u32 reserved3;
+       u32 cdb[4];
+       u32 zero_fill;
+       u32 bd_list_addr_lo;
+       u32 bd_list_addr_hi;
+#if defined(__BIG_ENDIAN)
+       u8 cq_index;
+       u8 sd_start_bd_index;
+       u8 ud_start_bd_index;
+       u8 num_bds;
+#elif defined(__LITTLE_ENDIAN)
+       u8 num_bds;
+       u8 ud_start_bd_index;
+       u8 sd_start_bd_index;
+       u8 cq_index;
+#endif
+};
+
+
+/*
+ * task statistics for write response
+ */
+struct bnx2i_write_resp_task_stat {
+       u32 num_data_ins;
+};
+
+/*
+ * task statistics for read response
+ */
+struct bnx2i_read_resp_task_stat {
+#if defined(__BIG_ENDIAN)
+       u16 num_data_outs;
+       u16 num_r2ts;
+#elif defined(__LITTLE_ENDIAN)
+       u16 num_r2ts;
+       u16 num_data_outs;
+#endif
+};
+
+/*
+ * task statistics for iSCSI cmd response
+ */
+union bnx2i_cmd_resp_task_stat {
+       struct bnx2i_write_resp_task_stat write_stat;
+       struct bnx2i_read_resp_task_stat read_stat;
+};
+
+/*
+ * SCSI Command CQE
+ */
+struct bnx2i_cmd_response {
+#if defined(__BIG_ENDIAN)
+       u8 op_code;
+       u8 response_flags;
+#define ISCSI_CMD_RESPONSE_RESERVED0 (0x1<<0)
+#define ISCSI_CMD_RESPONSE_RESERVED0_SHIFT 0
+#define ISCSI_CMD_RESPONSE_RESIDUAL_UNDERFLOW (0x1<<1)
+#define ISCSI_CMD_RESPONSE_RESIDUAL_UNDERFLOW_SHIFT 1
+#define ISCSI_CMD_RESPONSE_RESIDUAL_OVERFLOW (0x1<<2)
+#define ISCSI_CMD_RESPONSE_RESIDUAL_OVERFLOW_SHIFT 2
+#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_UNDERFLOW (0x1<<3)
+#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_UNDERFLOW_SHIFT 3
+#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_OVERFLOW (0x1<<4)
+#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_OVERFLOW_SHIFT 4
+#define ISCSI_CMD_RESPONSE_RESERVED1 (0x7<<5)
+#define ISCSI_CMD_RESPONSE_RESERVED1_SHIFT 5
+       u8 response;
+       u8 status;
+#elif defined(__LITTLE_ENDIAN)
+       u8 status;
+       u8 response;
+       u8 response_flags;
+#define ISCSI_CMD_RESPONSE_RESERVED0 (0x1<<0)
+#define ISCSI_CMD_RESPONSE_RESERVED0_SHIFT 0
+#define ISCSI_CMD_RESPONSE_RESIDUAL_UNDERFLOW (0x1<<1)
+#define ISCSI_CMD_RESPONSE_RESIDUAL_UNDERFLOW_SHIFT 1
+#define ISCSI_CMD_RESPONSE_RESIDUAL_OVERFLOW (0x1<<2)
+#define ISCSI_CMD_RESPONSE_RESIDUAL_OVERFLOW_SHIFT 2
+#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_UNDERFLOW (0x1<<3)
+#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_UNDERFLOW_SHIFT 3
+#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_OVERFLOW (0x1<<4)
+#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_OVERFLOW_SHIFT 4
+#define ISCSI_CMD_RESPONSE_RESERVED1 (0x7<<5)
+#define ISCSI_CMD_RESPONSE_RESERVED1_SHIFT 5
+       u8 op_code;
+#endif
+       u32 data_length;
+       u32 exp_cmd_sn;
+       u32 max_cmd_sn;
+       u32 reserved2;
+       u32 residual_count;
+#if defined(__BIG_ENDIAN)
+       u16 reserved4;
+       u8 err_code;
+       u8 reserved3;
+#elif defined(__LITTLE_ENDIAN)
+       u8 reserved3;
+       u8 err_code;
+       u16 reserved4;
+#endif
+       u32 reserved5[5];
+       union bnx2i_cmd_resp_task_stat task_stat;
+       u32 reserved6;
+#if defined(__BIG_ENDIAN)
+       u16 reserved7;
+       u16 itt;
+#define ISCSI_CMD_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_CMD_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_CMD_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_CMD_RESPONSE_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+       u16 itt;
+#define ISCSI_CMD_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_CMD_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_CMD_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_CMD_RESPONSE_TYPE_SHIFT 14
+       u16 reserved7;
+#endif
+       u32 cq_req_sn;
+};
+
+
+
+/*
+ * firmware middle-path request SQ WQE
+ */
+struct bnx2i_fw_mp_request {
+#if defined(__BIG_ENDIAN)
+       u8 op_code;
+       u8 op_attr;
+       u16 hdr_opaque1;
+#elif defined(__LITTLE_ENDIAN)
+       u16 hdr_opaque1;
+       u8 op_attr;
+       u8 op_code;
+#endif
+       u32 data_length;
+       u32 hdr_opaque2[2];
+#if defined(__BIG_ENDIAN)
+       u16 reserved0;
+       u16 itt;
+#define ISCSI_FW_MP_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_FW_MP_REQUEST_INDEX_SHIFT 0
+#define ISCSI_FW_MP_REQUEST_TYPE (0x3<<14)
+#define ISCSI_FW_MP_REQUEST_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+       u16 itt;
+#define ISCSI_FW_MP_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_FW_MP_REQUEST_INDEX_SHIFT 0
+#define ISCSI_FW_MP_REQUEST_TYPE (0x3<<14)
+#define ISCSI_FW_MP_REQUEST_TYPE_SHIFT 14
+       u16 reserved0;
+#endif
+       u32 hdr_opaque3[4];
+       u32 resp_bd_list_addr_lo;
+       u32 resp_bd_list_addr_hi;
+       u32 resp_buffer;
+#define ISCSI_FW_MP_REQUEST_RESP_BUFFER_LENGTH (0xFFFFFF<<0)
+#define ISCSI_FW_MP_REQUEST_RESP_BUFFER_LENGTH_SHIFT 0
+#define ISCSI_FW_MP_REQUEST_NUM_RESP_BDS (0xFF<<24)
+#define ISCSI_FW_MP_REQUEST_NUM_RESP_BDS_SHIFT 24
+#if defined(__BIG_ENDIAN)
+       u16 reserved4;
+       u8 reserved3;
+       u8 flags;
+#define ISCSI_FW_MP_REQUEST_RESERVED1 (0x1<<0)
+#define ISCSI_FW_MP_REQUEST_RESERVED1_SHIFT 0
+#define ISCSI_FW_MP_REQUEST_LOCAL_COMPLETION (0x1<<1)
+#define ISCSI_FW_MP_REQUEST_LOCAL_COMPLETION_SHIFT 1
+#define ISCSI_FW_MP_REQUEST_UPDATE_EXP_STAT_SN (0x1<<2)
+#define ISCSI_FW_MP_REQUEST_UPDATE_EXP_STAT_SN_SHIFT 2
+#define ISCSI_FW_MP_REQUEST_RESERVED2 (0x1F<<3)
+#define ISCSI_FW_MP_REQUEST_RESERVED2_SHIFT 3
+#elif defined(__LITTLE_ENDIAN)
+       u8 flags;
+#define ISCSI_FW_MP_REQUEST_RESERVED1 (0x1<<0)
+#define ISCSI_FW_MP_REQUEST_RESERVED1_SHIFT 0
+#define ISCSI_FW_MP_REQUEST_LOCAL_COMPLETION (0x1<<1)
+#define ISCSI_FW_MP_REQUEST_LOCAL_COMPLETION_SHIFT 1
+#define ISCSI_FW_MP_REQUEST_UPDATE_EXP_STAT_SN (0x1<<2)
+#define ISCSI_FW_MP_REQUEST_UPDATE_EXP_STAT_SN_SHIFT 2
+#define ISCSI_FW_MP_REQUEST_RESERVED2 (0x1F<<3)
+#define ISCSI_FW_MP_REQUEST_RESERVED2_SHIFT 3
+       u8 reserved3;
+       u16 reserved4;
+#endif
+       u32 bd_list_addr_lo;
+       u32 bd_list_addr_hi;
+#if defined(__BIG_ENDIAN)
+       u8 cq_index;
+       u8 reserved6;
+       u8 reserved5;
+       u8 num_bds;
+#elif defined(__LITTLE_ENDIAN)
+       u8 num_bds;
+       u8 reserved5;
+       u8 reserved6;
+       u8 cq_index;
+#endif
+};
+
+
+/*
+ * firmware response - CQE: used only by firmware
+ */
+struct bnx2i_fw_response {
+       u32 hdr_dword1[2];
+       u32 hdr_exp_cmd_sn;
+       u32 hdr_max_cmd_sn;
+       u32 hdr_ttt;
+       u32 hdr_res_cnt;
+       u32 cqe_flags;
+#define ISCSI_FW_RESPONSE_RESERVED2 (0xFF<<0)
+#define ISCSI_FW_RESPONSE_RESERVED2_SHIFT 0
+#define ISCSI_FW_RESPONSE_ERR_CODE (0xFF<<8)
+#define ISCSI_FW_RESPONSE_ERR_CODE_SHIFT 8
+#define ISCSI_FW_RESPONSE_RESERVED3 (0xFFFF<<16)
+#define ISCSI_FW_RESPONSE_RESERVED3_SHIFT 16
+       u32 stat_sn;
+       u32 hdr_dword2[2];
+       u32 hdr_dword3[2];
+       u32 task_stat;
+       u32 reserved0;
+       u32 hdr_itt;
+       u32 cq_req_sn;
+};
+
+
+/*
+ * iSCSI KCQ CQE parameters
+ */
+union iscsi_kcqe_params {
+       u32 reserved0[4];
+};
+
+/*
+ * iSCSI KCQ CQE
+ */
+struct iscsi_kcqe {
+       u32 iscsi_conn_id;
+       u32 completion_status;
+       u32 iscsi_conn_context_id;
+       union iscsi_kcqe_params params;
+#if defined(__BIG_ENDIAN)
+       u8 flags;
+#define ISCSI_KCQE_RESERVED0 (0xF<<0)
+#define ISCSI_KCQE_RESERVED0_SHIFT 0
+#define ISCSI_KCQE_LAYER_CODE (0x7<<4)
+#define ISCSI_KCQE_LAYER_CODE_SHIFT 4
+#define ISCSI_KCQE_RESERVED1 (0x1<<7)
+#define ISCSI_KCQE_RESERVED1_SHIFT 7
+       u8 op_code;
+       u16 qe_self_seq;
+#elif defined(__LITTLE_ENDIAN)
+       u16 qe_self_seq;
+       u8 op_code;
+       u8 flags;
+#define ISCSI_KCQE_RESERVED0 (0xF<<0)
+#define ISCSI_KCQE_RESERVED0_SHIFT 0
+#define ISCSI_KCQE_LAYER_CODE (0x7<<4)
+#define ISCSI_KCQE_LAYER_CODE_SHIFT 4
+#define ISCSI_KCQE_RESERVED1 (0x1<<7)
+#define ISCSI_KCQE_RESERVED1_SHIFT 7
+#endif
+};
+
+
+
+/*
+ * iSCSI KWQE header
+ */
+struct iscsi_kwqe_header {
+#if defined(__BIG_ENDIAN)
+       u8 flags;
+#define ISCSI_KWQE_HEADER_RESERVED0 (0xF<<0)
+#define ISCSI_KWQE_HEADER_RESERVED0_SHIFT 0
+#define ISCSI_KWQE_HEADER_LAYER_CODE (0x7<<4)
+#define ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT 4
+#define ISCSI_KWQE_HEADER_RESERVED1 (0x1<<7)
+#define ISCSI_KWQE_HEADER_RESERVED1_SHIFT 7
+       u8 op_code;
+#elif defined(__LITTLE_ENDIAN)
+       u8 op_code;
+       u8 flags;
+#define ISCSI_KWQE_HEADER_RESERVED0 (0xF<<0)
+#define ISCSI_KWQE_HEADER_RESERVED0_SHIFT 0
+#define ISCSI_KWQE_HEADER_LAYER_CODE (0x7<<4)
+#define ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT 4
+#define ISCSI_KWQE_HEADER_RESERVED1 (0x1<<7)
+#define ISCSI_KWQE_HEADER_RESERVED1_SHIFT 7
+#endif
+};
+
+/*
+ * iSCSI firmware init request 1
+ */
+struct iscsi_kwqe_init1 {
+#if defined(__BIG_ENDIAN)
+       struct iscsi_kwqe_header hdr;
+       u8 reserved0;
+       u8 num_cqs;
+#elif defined(__LITTLE_ENDIAN)
+       u8 num_cqs;
+       u8 reserved0;
+       struct iscsi_kwqe_header hdr;
+#endif
+       u32 dummy_buffer_addr_lo;
+       u32 dummy_buffer_addr_hi;
+#if defined(__BIG_ENDIAN)
+       u16 num_ccells_per_conn;
+       u16 num_tasks_per_conn;
+#elif defined(__LITTLE_ENDIAN)
+       u16 num_tasks_per_conn;
+       u16 num_ccells_per_conn;
+#endif
+#if defined(__BIG_ENDIAN)
+       u16 sq_wqes_per_page;
+       u16 sq_num_wqes;
+#elif defined(__LITTLE_ENDIAN)
+       u16 sq_num_wqes;
+       u16 sq_wqes_per_page;
+#endif
+#if defined(__BIG_ENDIAN)
+       u8 cq_log_wqes_per_page;
+       u8 flags;
+#define ISCSI_KWQE_INIT1_PAGE_SIZE (0xF<<0)
+#define ISCSI_KWQE_INIT1_PAGE_SIZE_SHIFT 0
+#define ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE (0x1<<4)
+#define ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE_SHIFT 4
+#define ISCSI_KWQE_INIT1_KEEP_ALIVE_ENABLE (0x1<<5)
+#define ISCSI_KWQE_INIT1_KEEP_ALIVE_ENABLE_SHIFT 5
+#define ISCSI_KWQE_INIT1_RESERVED1 (0x3<<6)
+#define ISCSI_KWQE_INIT1_RESERVED1_SHIFT 6
+       u16 cq_num_wqes;
+#elif defined(__LITTLE_ENDIAN)
+       u16 cq_num_wqes;
+       u8 flags;
+#define ISCSI_KWQE_INIT1_PAGE_SIZE (0xF<<0)
+#define ISCSI_KWQE_INIT1_PAGE_SIZE_SHIFT 0
+#define ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE (0x1<<4)
+#define ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE_SHIFT 4
+#define ISCSI_KWQE_INIT1_KEEP_ALIVE_ENABLE (0x1<<5)
+#define ISCSI_KWQE_INIT1_KEEP_ALIVE_ENABLE_SHIFT 5
+#define ISCSI_KWQE_INIT1_RESERVED1 (0x3<<6)
+#define ISCSI_KWQE_INIT1_RESERVED1_SHIFT 6
+       u8 cq_log_wqes_per_page;
+#endif
+#if defined(__BIG_ENDIAN)
+       u16 cq_num_pages;
+       u16 sq_num_pages;
+#elif defined(__LITTLE_ENDIAN)
+       u16 sq_num_pages;
+       u16 cq_num_pages;
+#endif
+#if defined(__BIG_ENDIAN)
+       u16 rq_buffer_size;
+       u16 rq_num_wqes;
+#elif defined(__LITTLE_ENDIAN)
+       u16 rq_num_wqes;
+       u16 rq_buffer_size;
+#endif
+};
+
+/*
+ * iSCSI firmware init request 2
+ */
+struct iscsi_kwqe_init2 {
+#if defined(__BIG_ENDIAN)
+       struct iscsi_kwqe_header hdr;
+       u16 max_cq_sqn;
+#elif defined(__LITTLE_ENDIAN)
+       u16 max_cq_sqn;
+       struct iscsi_kwqe_header hdr;
+#endif
+       u32 error_bit_map[2];
+       u32 reserved1[5];
+};
+
+/*
+ * Initial iSCSI connection offload request 1
+ */
+struct iscsi_kwqe_conn_offload1 {
+#if defined(__BIG_ENDIAN)
+       struct iscsi_kwqe_header hdr;
+       u16 iscsi_conn_id;
+#elif defined(__LITTLE_ENDIAN)
+       u16 iscsi_conn_id;
+       struct iscsi_kwqe_header hdr;
+#endif
+       u32 sq_page_table_addr_lo;
+       u32 sq_page_table_addr_hi;
+       u32 cq_page_table_addr_lo;
+       u32 cq_page_table_addr_hi;
+       u32 reserved0[3];
+};
+
+/*
+ * iSCSI Page Table Entry (PTE)
+ */
+struct iscsi_pte {
+       u32 hi;
+       u32 lo;
+};
+
+/*
+ * Initial iSCSI connection offload request 2
+ */
+struct iscsi_kwqe_conn_offload2 {
+#if defined(__BIG_ENDIAN)
+       struct iscsi_kwqe_header hdr;
+       u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved0;
+       struct iscsi_kwqe_header hdr;
+#endif
+       u32 rq_page_table_addr_lo;
+       u32 rq_page_table_addr_hi;
+       struct iscsi_pte sq_first_pte;
+       struct iscsi_pte cq_first_pte;
+       u32 num_additional_wqes;
+};
+
+
+/*
+ * Initial iSCSI connection offload request 3
+ */
+struct iscsi_kwqe_conn_offload3 {
+#if defined(__BIG_ENDIAN)
+       struct iscsi_kwqe_header hdr;
+       u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved0;
+       struct iscsi_kwqe_header hdr;
+#endif
+       u32 reserved1;
+       struct iscsi_pte qp_first_pte[3];
+};
+
+
+/*
+ * iSCSI connection update request
+ */
+struct iscsi_kwqe_conn_update {
+#if defined(__BIG_ENDIAN)
+       struct iscsi_kwqe_header hdr;
+       u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved0;
+       struct iscsi_kwqe_header hdr;
+#endif
+#if defined(__BIG_ENDIAN)
+       u8 session_error_recovery_level;
+       u8 max_outstanding_r2ts;
+       u8 reserved2;
+       u8 conn_flags;
+#define ISCSI_KWQE_CONN_UPDATE_HEADER_DIGEST (0x1<<0)
+#define ISCSI_KWQE_CONN_UPDATE_HEADER_DIGEST_SHIFT 0
+#define ISCSI_KWQE_CONN_UPDATE_DATA_DIGEST (0x1<<1)
+#define ISCSI_KWQE_CONN_UPDATE_DATA_DIGEST_SHIFT 1
+#define ISCSI_KWQE_CONN_UPDATE_INITIAL_R2T (0x1<<2)
+#define ISCSI_KWQE_CONN_UPDATE_INITIAL_R2T_SHIFT 2
+#define ISCSI_KWQE_CONN_UPDATE_IMMEDIATE_DATA (0x1<<3)
+#define ISCSI_KWQE_CONN_UPDATE_IMMEDIATE_DATA_SHIFT 3
+#define ISCSI_KWQE_CONN_UPDATE_RESERVED1 (0xF<<4)
+#define ISCSI_KWQE_CONN_UPDATE_RESERVED1_SHIFT 4
+#elif defined(__LITTLE_ENDIAN)
+       u8 conn_flags;
+#define ISCSI_KWQE_CONN_UPDATE_HEADER_DIGEST (0x1<<0)
+#define ISCSI_KWQE_CONN_UPDATE_HEADER_DIGEST_SHIFT 0
+#define ISCSI_KWQE_CONN_UPDATE_DATA_DIGEST (0x1<<1)
+#define ISCSI_KWQE_CONN_UPDATE_DATA_DIGEST_SHIFT 1
+#define ISCSI_KWQE_CONN_UPDATE_INITIAL_R2T (0x1<<2)
+#define ISCSI_KWQE_CONN_UPDATE_INITIAL_R2T_SHIFT 2
+#define ISCSI_KWQE_CONN_UPDATE_IMMEDIATE_DATA (0x1<<3)
+#define ISCSI_KWQE_CONN_UPDATE_IMMEDIATE_DATA_SHIFT 3
+#define ISCSI_KWQE_CONN_UPDATE_RESERVED1 (0xF<<4)
+#define ISCSI_KWQE_CONN_UPDATE_RESERVED1_SHIFT 4
+       u8 reserved2;
+       u8 max_outstanding_r2ts;
+       u8 session_error_recovery_level;
+#endif
+       u32 context_id;
+       u32 max_send_pdu_length;
+       u32 max_recv_pdu_length;
+       u32 first_burst_length;
+       u32 max_burst_length;
+       u32 exp_stat_sn;
+};
+
+/*
+ * iSCSI destroy connection request
+ */
+struct iscsi_kwqe_conn_destroy {
+#if defined(__BIG_ENDIAN)
+       struct iscsi_kwqe_header hdr;
+       u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved0;
+       struct iscsi_kwqe_header hdr;
+#endif
+       u32 context_id;
+       u32 reserved1[6];
+};
+
+/*
+ * iSCSI KWQ WQE
+ */
+union iscsi_kwqe {
+       struct iscsi_kwqe_init1 init1;
+       struct iscsi_kwqe_init2 init2;
+       struct iscsi_kwqe_conn_offload1 conn_offload1;
+       struct iscsi_kwqe_conn_offload2 conn_offload2;
+       struct iscsi_kwqe_conn_update conn_update;
+       struct iscsi_kwqe_conn_destroy conn_destroy;
+};
+
+/*
+ * iSCSI Login SQ WQE
+ */
+struct bnx2i_login_request {
+#if defined(__BIG_ENDIAN)
+       u8 op_code;
+       u8 op_attr;
+#define ISCSI_LOGIN_REQUEST_NEXT_STAGE (0x3<<0)
+#define ISCSI_LOGIN_REQUEST_NEXT_STAGE_SHIFT 0
+#define ISCSI_LOGIN_REQUEST_CURRENT_STAGE (0x3<<2)
+#define ISCSI_LOGIN_REQUEST_CURRENT_STAGE_SHIFT 2
+#define ISCSI_LOGIN_REQUEST_RESERVED0 (0x3<<4)
+#define ISCSI_LOGIN_REQUEST_RESERVED0_SHIFT 4
+#define ISCSI_LOGIN_REQUEST_CONT (0x1<<6)
+#define ISCSI_LOGIN_REQUEST_CONT_SHIFT 6
+#define ISCSI_LOGIN_REQUEST_TRANSIT (0x1<<7)
+#define ISCSI_LOGIN_REQUEST_TRANSIT_SHIFT 7
+       u8 version_max;
+       u8 version_min;
+#elif defined(__LITTLE_ENDIAN)
+       u8 version_min;
+       u8 version_max;
+       u8 op_attr;
+#define ISCSI_LOGIN_REQUEST_NEXT_STAGE (0x3<<0)
+#define ISCSI_LOGIN_REQUEST_NEXT_STAGE_SHIFT 0
+#define ISCSI_LOGIN_REQUEST_CURRENT_STAGE (0x3<<2)
+#define ISCSI_LOGIN_REQUEST_CURRENT_STAGE_SHIFT 2
+#define ISCSI_LOGIN_REQUEST_RESERVED0 (0x3<<4)
+#define ISCSI_LOGIN_REQUEST_RESERVED0_SHIFT 4
+#define ISCSI_LOGIN_REQUEST_CONT (0x1<<6)
+#define ISCSI_LOGIN_REQUEST_CONT_SHIFT 6
+#define ISCSI_LOGIN_REQUEST_TRANSIT (0x1<<7)
+#define ISCSI_LOGIN_REQUEST_TRANSIT_SHIFT 7
+       u8 op_code;
+#endif
+       u32 data_length;
+       u32 isid_lo;
+#if defined(__BIG_ENDIAN)
+       u16 isid_hi;
+       u16 tsih;
+#elif defined(__LITTLE_ENDIAN)
+       u16 tsih;
+       u16 isid_hi;
+#endif
+#if defined(__BIG_ENDIAN)
+       u16 reserved2;
+       u16 itt;
+#define ISCSI_LOGIN_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_LOGIN_REQUEST_INDEX_SHIFT 0
+#define ISCSI_LOGIN_REQUEST_TYPE (0x3<<14)
+#define ISCSI_LOGIN_REQUEST_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+       u16 itt;
+#define ISCSI_LOGIN_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_LOGIN_REQUEST_INDEX_SHIFT 0
+#define ISCSI_LOGIN_REQUEST_TYPE (0x3<<14)
+#define ISCSI_LOGIN_REQUEST_TYPE_SHIFT 14
+       u16 reserved2;
+#endif
+#if defined(__BIG_ENDIAN)
+       u16 cid;
+       u16 reserved3;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved3;
+       u16 cid;
+#endif
+       u32 cmd_sn;
+       u32 exp_stat_sn;
+       u32 reserved4;
+       u32 resp_bd_list_addr_lo;
+       u32 resp_bd_list_addr_hi;
+       u32 resp_buffer;
+#define ISCSI_LOGIN_REQUEST_RESP_BUFFER_LENGTH (0xFFFFFF<<0)
+#define ISCSI_LOGIN_REQUEST_RESP_BUFFER_LENGTH_SHIFT 0
+#define ISCSI_LOGIN_REQUEST_NUM_RESP_BDS (0xFF<<24)
+#define ISCSI_LOGIN_REQUEST_NUM_RESP_BDS_SHIFT 24
+#if defined(__BIG_ENDIAN)
+       u16 reserved8;
+       u8 reserved7;
+       u8 flags;
+#define ISCSI_LOGIN_REQUEST_RESERVED5 (0x3<<0)
+#define ISCSI_LOGIN_REQUEST_RESERVED5_SHIFT 0
+#define ISCSI_LOGIN_REQUEST_UPDATE_EXP_STAT_SN (0x1<<2)
+#define ISCSI_LOGIN_REQUEST_UPDATE_EXP_STAT_SN_SHIFT 2
+#define ISCSI_LOGIN_REQUEST_RESERVED6 (0x1F<<3)
+#define ISCSI_LOGIN_REQUEST_RESERVED6_SHIFT 3
+#elif defined(__LITTLE_ENDIAN)
+       u8 flags;
+#define ISCSI_LOGIN_REQUEST_RESERVED5 (0x3<<0)
+#define ISCSI_LOGIN_REQUEST_RESERVED5_SHIFT 0
+#define ISCSI_LOGIN_REQUEST_UPDATE_EXP_STAT_SN (0x1<<2)
+#define ISCSI_LOGIN_REQUEST_UPDATE_EXP_STAT_SN_SHIFT 2
+#define ISCSI_LOGIN_REQUEST_RESERVED6 (0x1F<<3)
+#define ISCSI_LOGIN_REQUEST_RESERVED6_SHIFT 3
+       u8 reserved7;
+       u16 reserved8;
+#endif
+       u32 bd_list_addr_lo;
+       u32 bd_list_addr_hi;
+#if defined(__BIG_ENDIAN)
+       u8 cq_index;
+       u8 reserved10;
+       u8 reserved9;
+       u8 num_bds;
+#elif defined(__LITTLE_ENDIAN)
+       u8 num_bds;
+       u8 reserved9;
+       u8 reserved10;
+       u8 cq_index;
+#endif
+};
+
+
+/*
+ * iSCSI Login CQE
+ */
+struct bnx2i_login_response {
+#if defined(__BIG_ENDIAN)
+       u8 op_code;
+       u8 response_flags;
+#define ISCSI_LOGIN_RESPONSE_NEXT_STAGE (0x3<<0)
+#define ISCSI_LOGIN_RESPONSE_NEXT_STAGE_SHIFT 0
+#define ISCSI_LOGIN_RESPONSE_CURRENT_STAGE (0x3<<2)
+#define ISCSI_LOGIN_RESPONSE_CURRENT_STAGE_SHIFT 2
+#define ISCSI_LOGIN_RESPONSE_RESERVED0 (0x3<<4)
+#define ISCSI_LOGIN_RESPONSE_RESERVED0_SHIFT 4
+#define ISCSI_LOGIN_RESPONSE_CONT (0x1<<6)
+#define ISCSI_LOGIN_RESPONSE_CONT_SHIFT 6
+#define ISCSI_LOGIN_RESPONSE_TRANSIT (0x1<<7)
+#define ISCSI_LOGIN_RESPONSE_TRANSIT_SHIFT 7
+       u8 version_max;
+       u8 version_active;
+#elif defined(__LITTLE_ENDIAN)
+       u8 version_active;
+       u8 version_max;
+       u8 response_flags;
+#define ISCSI_LOGIN_RESPONSE_NEXT_STAGE (0x3<<0)
+#define ISCSI_LOGIN_RESPONSE_NEXT_STAGE_SHIFT 0
+#define ISCSI_LOGIN_RESPONSE_CURRENT_STAGE (0x3<<2)
+#define ISCSI_LOGIN_RESPONSE_CURRENT_STAGE_SHIFT 2
+#define ISCSI_LOGIN_RESPONSE_RESERVED0 (0x3<<4)
+#define ISCSI_LOGIN_RESPONSE_RESERVED0_SHIFT 4
+#define ISCSI_LOGIN_RESPONSE_CONT (0x1<<6)
+#define ISCSI_LOGIN_RESPONSE_CONT_SHIFT 6
+#define ISCSI_LOGIN_RESPONSE_TRANSIT (0x1<<7)
+#define ISCSI_LOGIN_RESPONSE_TRANSIT_SHIFT 7
+       u8 op_code;
+#endif
+       u32 data_length;
+       u32 exp_cmd_sn;
+       u32 max_cmd_sn;
+       u32 reserved1[2];
+#if defined(__BIG_ENDIAN)
+       u16 reserved3;
+       u8 err_code;
+       u8 reserved2;
+#elif defined(__LITTLE_ENDIAN)
+       u8 reserved2;
+       u8 err_code;
+       u16 reserved3;
+#endif
+       u32 stat_sn;
+       u32 isid_lo;
+#if defined(__BIG_ENDIAN)
+       u16 isid_hi;
+       u16 tsih;
+#elif defined(__LITTLE_ENDIAN)
+       u16 tsih;
+       u16 isid_hi;
+#endif
+#if defined(__BIG_ENDIAN)
+       u8 status_class;
+       u8 status_detail;
+       u16 reserved4;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved4;
+       u8 status_detail;
+       u8 status_class;
+#endif
+       u32 reserved5[3];
+#if defined(__BIG_ENDIAN)
+       u16 reserved6;
+       u16 itt;
+#define ISCSI_LOGIN_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_LOGIN_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_LOGIN_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_LOGIN_RESPONSE_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+       u16 itt;
+#define ISCSI_LOGIN_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_LOGIN_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_LOGIN_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_LOGIN_RESPONSE_TYPE_SHIFT 14
+       u16 reserved6;
+#endif
+       u32 cq_req_sn;
+};
+
+
+/*
+ * iSCSI Logout SQ WQE
+ */
+struct bnx2i_logout_request {
+#if defined(__BIG_ENDIAN)
+       u8 op_code;
+       u8 op_attr;
+#define ISCSI_LOGOUT_REQUEST_REASON (0x7F<<0)
+#define ISCSI_LOGOUT_REQUEST_REASON_SHIFT 0
+#define ISCSI_LOGOUT_REQUEST_ALWAYS_ONE (0x1<<7)
+#define ISCSI_LOGOUT_REQUEST_ALWAYS_ONE_SHIFT 7
+       u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved0;
+       u8 op_attr;
+#define ISCSI_LOGOUT_REQUEST_REASON (0x7F<<0)
+#define ISCSI_LOGOUT_REQUEST_REASON_SHIFT 0
+#define ISCSI_LOGOUT_REQUEST_ALWAYS_ONE (0x1<<7)
+#define ISCSI_LOGOUT_REQUEST_ALWAYS_ONE_SHIFT 7
+       u8 op_code;
+#endif
+       u32 data_length;
+       u32 reserved1[2];
+#if defined(__BIG_ENDIAN)
+       u16 reserved2;
+       u16 itt;
+#define ISCSI_LOGOUT_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_LOGOUT_REQUEST_INDEX_SHIFT 0
+#define ISCSI_LOGOUT_REQUEST_TYPE (0x3<<14)
+#define ISCSI_LOGOUT_REQUEST_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+       u16 itt;
+#define ISCSI_LOGOUT_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_LOGOUT_REQUEST_INDEX_SHIFT 0
+#define ISCSI_LOGOUT_REQUEST_TYPE (0x3<<14)
+#define ISCSI_LOGOUT_REQUEST_TYPE_SHIFT 14
+       u16 reserved2;
+#endif
+#if defined(__BIG_ENDIAN)
+       u16 cid;
+       u16 reserved3;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved3;
+       u16 cid;
+#endif
+       u32 cmd_sn;
+       u32 reserved4[5];
+       u32 zero_fill;
+       u32 bd_list_addr_lo;
+       u32 bd_list_addr_hi;
+#if defined(__BIG_ENDIAN)
+       u8 cq_index;
+       u8 reserved6;
+       u8 reserved5;
+       u8 num_bds;
+#elif defined(__LITTLE_ENDIAN)
+       u8 num_bds;
+       u8 reserved5;
+       u8 reserved6;
+       u8 cq_index;
+#endif
+};
+
+
+/*
+ * iSCSI Logout CQE
+ */
+struct bnx2i_logout_response {
+#if defined(__BIG_ENDIAN)
+       u8 op_code;
+       u8 reserved1;
+       u8 response;
+       u8 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u8 reserved0;
+       u8 response;
+       u8 reserved1;
+       u8 op_code;
+#endif
+       u32 reserved2;
+       u32 exp_cmd_sn;
+       u32 max_cmd_sn;
+       u32 reserved3[2];
+#if defined(__BIG_ENDIAN)
+       u16 reserved5;
+       u8 err_code;
+       u8 reserved4;
+#elif defined(__LITTLE_ENDIAN)
+       u8 reserved4;
+       u8 err_code;
+       u16 reserved5;
+#endif
+       u32 reserved6[3];
+#if defined(__BIG_ENDIAN)
+       u16 time_to_wait;
+       u16 time_to_retain;
+#elif defined(__LITTLE_ENDIAN)
+       u16 time_to_retain;
+       u16 time_to_wait;
+#endif
+       u32 reserved7[3];
+#if defined(__BIG_ENDIAN)
+       u16 reserved8;
+       u16 itt;
+#define ISCSI_LOGOUT_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_LOGOUT_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_LOGOUT_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_LOGOUT_RESPONSE_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+       u16 itt;
+#define ISCSI_LOGOUT_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_LOGOUT_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_LOGOUT_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_LOGOUT_RESPONSE_TYPE_SHIFT 14
+       u16 reserved8;
+#endif
+       u32 cq_req_sn;
+};
+
+
+/*
+ * iSCSI Nop-In CQE
+ */
+struct bnx2i_nop_in_msg {
+#if defined(__BIG_ENDIAN)
+       u8 op_code;
+       u8 reserved1;
+       u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved0;
+       u8 reserved1;
+       u8 op_code;
+#endif
+       u32 data_length;
+       u32 exp_cmd_sn;
+       u32 max_cmd_sn;
+       u32 ttt;
+       u32 reserved2;
+#if defined(__BIG_ENDIAN)
+       u16 reserved4;
+       u8 err_code;
+       u8 reserved3;
+#elif defined(__LITTLE_ENDIAN)
+       u8 reserved3;
+       u8 err_code;
+       u16 reserved4;
+#endif
+       u32 reserved5;
+       u32 lun[2];
+       u32 reserved6[4];
+#if defined(__BIG_ENDIAN)
+       u16 reserved7;
+       u16 itt;
+#define ISCSI_NOP_IN_MSG_INDEX (0x3FFF<<0)
+#define ISCSI_NOP_IN_MSG_INDEX_SHIFT 0
+#define ISCSI_NOP_IN_MSG_TYPE (0x3<<14)
+#define ISCSI_NOP_IN_MSG_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+       u16 itt;
+#define ISCSI_NOP_IN_MSG_INDEX (0x3FFF<<0)
+#define ISCSI_NOP_IN_MSG_INDEX_SHIFT 0
+#define ISCSI_NOP_IN_MSG_TYPE (0x3<<14)
+#define ISCSI_NOP_IN_MSG_TYPE_SHIFT 14
+       u16 reserved7;
+#endif
+       u32 cq_req_sn;
+};
+
+
+/*
+ * iSCSI NOP-OUT SQ WQE
+ */
+struct bnx2i_nop_out_request {
+#if defined(__BIG_ENDIAN)
+       u8 op_code;
+       u8 op_attr;
+#define ISCSI_NOP_OUT_REQUEST_RESERVED1 (0x7F<<0)
+#define ISCSI_NOP_OUT_REQUEST_RESERVED1_SHIFT 0
+#define ISCSI_NOP_OUT_REQUEST_ALWAYS_ONE (0x1<<7)
+#define ISCSI_NOP_OUT_REQUEST_ALWAYS_ONE_SHIFT 7
+       u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved0;
+       u8 op_attr;
+#define ISCSI_NOP_OUT_REQUEST_RESERVED1 (0x7F<<0)
+#define ISCSI_NOP_OUT_REQUEST_RESERVED1_SHIFT 0
+#define ISCSI_NOP_OUT_REQUEST_ALWAYS_ONE (0x1<<7)
+#define ISCSI_NOP_OUT_REQUEST_ALWAYS_ONE_SHIFT 7
+       u8 op_code;
+#endif
+       u32 data_length;
+       u32 lun[2];
+#if defined(__BIG_ENDIAN)
+       u16 reserved2;
+       u16 itt;
+#define ISCSI_NOP_OUT_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_NOP_OUT_REQUEST_INDEX_SHIFT 0
+#define ISCSI_NOP_OUT_REQUEST_TYPE (0x3<<14)
+#define ISCSI_NOP_OUT_REQUEST_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+       u16 itt;
+#define ISCSI_NOP_OUT_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_NOP_OUT_REQUEST_INDEX_SHIFT 0
+#define ISCSI_NOP_OUT_REQUEST_TYPE (0x3<<14)
+#define ISCSI_NOP_OUT_REQUEST_TYPE_SHIFT 14
+       u16 reserved2;
+#endif
+       u32 ttt;
+       u32 cmd_sn;
+       u32 reserved3[2];
+       u32 resp_bd_list_addr_lo;
+       u32 resp_bd_list_addr_hi;
+       u32 resp_buffer;
+#define ISCSI_NOP_OUT_REQUEST_RESP_BUFFER_LENGTH (0xFFFFFF<<0)
+#define ISCSI_NOP_OUT_REQUEST_RESP_BUFFER_LENGTH_SHIFT 0
+#define ISCSI_NOP_OUT_REQUEST_NUM_RESP_BDS (0xFF<<24)
+#define ISCSI_NOP_OUT_REQUEST_NUM_RESP_BDS_SHIFT 24
+#if defined(__BIG_ENDIAN)
+       u16 reserved7;
+       u8 reserved6;
+       u8 flags;
+#define ISCSI_NOP_OUT_REQUEST_RESERVED4 (0x1<<0)
+#define ISCSI_NOP_OUT_REQUEST_RESERVED4_SHIFT 0
+#define ISCSI_NOP_OUT_REQUEST_LOCAL_COMPLETION (0x1<<1)
+#define ISCSI_NOP_OUT_REQUEST_LOCAL_COMPLETION_SHIFT 1
+#define ISCSI_NOP_OUT_REQUEST_ZERO_FILL (0x3F<<2)
+#define ISCSI_NOP_OUT_REQUEST_ZERO_FILL_SHIFT 2
+#elif defined(__LITTLE_ENDIAN)
+       u8 flags;
+#define ISCSI_NOP_OUT_REQUEST_RESERVED4 (0x1<<0)
+#define ISCSI_NOP_OUT_REQUEST_RESERVED4_SHIFT 0
+#define ISCSI_NOP_OUT_REQUEST_LOCAL_COMPLETION (0x1<<1)
+#define ISCSI_NOP_OUT_REQUEST_LOCAL_COMPLETION_SHIFT 1
+#define ISCSI_NOP_OUT_REQUEST_ZERO_FILL (0x3F<<2)
+#define ISCSI_NOP_OUT_REQUEST_ZERO_FILL_SHIFT 2
+       u8 reserved6;
+       u16 reserved7;
+#endif
+       u32 bd_list_addr_lo;
+       u32 bd_list_addr_hi;
+#if defined(__BIG_ENDIAN)
+       u8 cq_index;
+       u8 reserved9;
+       u8 reserved8;
+       u8 num_bds;
+#elif defined(__LITTLE_ENDIAN)
+       u8 num_bds;
+       u8 reserved8;
+       u8 reserved9;
+       u8 cq_index;
+#endif
+};
+
+/*
+ * iSCSI Reject CQE
+ */
+struct bnx2i_reject_msg {
+#if defined(__BIG_ENDIAN)
+       u8 op_code;
+       u8 reserved1;
+       u8 reason;
+       u8 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u8 reserved0;
+       u8 reason;
+       u8 reserved1;
+       u8 op_code;
+#endif
+       u32 data_length;
+       u32 exp_cmd_sn;
+       u32 max_cmd_sn;
+       u32 reserved2[2];
+#if defined(__BIG_ENDIAN)
+       u16 reserved4;
+       u8 err_code;
+       u8 reserved3;
+#elif defined(__LITTLE_ENDIAN)
+       u8 reserved3;
+       u8 err_code;
+       u16 reserved4;
+#endif
+       u32 reserved5[8];
+       u32 cq_req_sn;
+};
+
+/*
+ * bnx2i iSCSI TMF SQ WQE
+ */
+struct bnx2i_tmf_request {
+#if defined(__BIG_ENDIAN)
+       u8 op_code;
+       u8 op_attr;
+#define ISCSI_TMF_REQUEST_FUNCTION (0x7F<<0)
+#define ISCSI_TMF_REQUEST_FUNCTION_SHIFT 0
+#define ISCSI_TMF_REQUEST_ALWAYS_ONE (0x1<<7)
+#define ISCSI_TMF_REQUEST_ALWAYS_ONE_SHIFT 7
+       u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved0;
+       u8 op_attr;
+#define ISCSI_TMF_REQUEST_FUNCTION (0x7F<<0)
+#define ISCSI_TMF_REQUEST_FUNCTION_SHIFT 0
+#define ISCSI_TMF_REQUEST_ALWAYS_ONE (0x1<<7)
+#define ISCSI_TMF_REQUEST_ALWAYS_ONE_SHIFT 7
+       u8 op_code;
+#endif
+       u32 data_length;
+       u32 lun[2];
+#if defined(__BIG_ENDIAN)
+       u16 reserved1;
+       u16 itt;
+#define ISCSI_TMF_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_TMF_REQUEST_INDEX_SHIFT 0
+#define ISCSI_TMF_REQUEST_TYPE (0x3<<14)
+#define ISCSI_TMF_REQUEST_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+       u16 itt;
+#define ISCSI_TMF_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_TMF_REQUEST_INDEX_SHIFT 0
+#define ISCSI_TMF_REQUEST_TYPE (0x3<<14)
+#define ISCSI_TMF_REQUEST_TYPE_SHIFT 14
+       u16 reserved1;
+#endif
+       u32 ref_itt;
+       u32 cmd_sn;
+       u32 reserved2;
+       u32 ref_cmd_sn;
+       u32 reserved3[3];
+       u32 zero_fill;
+       u32 bd_list_addr_lo;
+       u32 bd_list_addr_hi;
+#if defined(__BIG_ENDIAN)
+       u8 cq_index;
+       u8 reserved5;
+       u8 reserved4;
+       u8 num_bds;
+#elif defined(__LITTLE_ENDIAN)
+       u8 num_bds;
+       u8 reserved4;
+       u8 reserved5;
+       u8 cq_index;
+#endif
+};
+
+/*
+ * iSCSI Text SQ WQE
+ */
+struct bnx2i_text_request {
+#if defined(__BIG_ENDIAN)
+       u8 op_code;
+       u8 op_attr;
+#define ISCSI_TEXT_REQUEST_RESERVED1 (0x3F<<0)
+#define ISCSI_TEXT_REQUEST_RESERVED1_SHIFT 0
+#define ISCSI_TEXT_REQUEST_CONT (0x1<<6)
+#define ISCSI_TEXT_REQUEST_CONT_SHIFT 6
+#define ISCSI_TEXT_REQUEST_FINAL (0x1<<7)
+#define ISCSI_TEXT_REQUEST_FINAL_SHIFT 7
+       u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved0;
+       u8 op_attr;
+#define ISCSI_TEXT_REQUEST_RESERVED1 (0x3F<<0)
+#define ISCSI_TEXT_REQUEST_RESERVED1_SHIFT 0
+#define ISCSI_TEXT_REQUEST_CONT (0x1<<6)
+#define ISCSI_TEXT_REQUEST_CONT_SHIFT 6
+#define ISCSI_TEXT_REQUEST_FINAL (0x1<<7)
+#define ISCSI_TEXT_REQUEST_FINAL_SHIFT 7
+       u8 op_code;
+#endif
+       u32 data_length;
+       u32 lun[2];
+#if defined(__BIG_ENDIAN)
+       u16 reserved3;
+       u16 itt;
+#define ISCSI_TEXT_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_TEXT_REQUEST_INDEX_SHIFT 0
+#define ISCSI_TEXT_REQUEST_TYPE (0x3<<14)
+#define ISCSI_TEXT_REQUEST_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+       u16 itt;
+#define ISCSI_TEXT_REQUEST_INDEX (0x3FFF<<0)
+#define ISCSI_TEXT_REQUEST_INDEX_SHIFT 0
+#define ISCSI_TEXT_REQUEST_TYPE (0x3<<14)
+#define ISCSI_TEXT_REQUEST_TYPE_SHIFT 14
+       u16 reserved3;
+#endif
+       u32 ttt;
+       u32 cmd_sn;
+       u32 reserved4[2];
+       u32 resp_bd_list_addr_lo;
+       u32 resp_bd_list_addr_hi;
+       u32 resp_buffer;
+#define ISCSI_TEXT_REQUEST_RESP_BUFFER_LENGTH (0xFFFFFF<<0)
+#define ISCSI_TEXT_REQUEST_RESP_BUFFER_LENGTH_SHIFT 0
+#define ISCSI_TEXT_REQUEST_NUM_RESP_BDS (0xFF<<24)
+#define ISCSI_TEXT_REQUEST_NUM_RESP_BDS_SHIFT 24
+       u32 zero_fill;
+       u32 bd_list_addr_lo;
+       u32 bd_list_addr_hi;
+#if defined(__BIG_ENDIAN)
+       u8 cq_index;
+       u8 reserved7;
+       u8 reserved6;
+       u8 num_bds;
+#elif defined(__LITTLE_ENDIAN)
+       u8 num_bds;
+       u8 reserved6;
+       u8 reserved7;
+       u8 cq_index;
+#endif
+};
+
+/*
+ * iSCSI SQ WQE
+ */
+union iscsi_request {
+       struct bnx2i_cmd_request cmd;
+       struct bnx2i_tmf_request tmf;
+       struct bnx2i_nop_out_request nop_out;
+       struct bnx2i_login_request login_req;
+       struct bnx2i_text_request text;
+       struct bnx2i_logout_request logout_req;
+       struct bnx2i_cleanup_request cleanup;
+};
+
+
+/*
+ * iSCSI TMF CQE
+ */
+struct bnx2i_tmf_response {
+#if defined(__BIG_ENDIAN)
+       u8 op_code;
+       u8 reserved1;
+       u8 response;
+       u8 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u8 reserved0;
+       u8 response;
+       u8 reserved1;
+       u8 op_code;
+#endif
+       u32 reserved2;
+       u32 exp_cmd_sn;
+       u32 max_cmd_sn;
+       u32 reserved3[2];
+#if defined(__BIG_ENDIAN)
+       u16 reserved5;
+       u8 err_code;
+       u8 reserved4;
+#elif defined(__LITTLE_ENDIAN)
+       u8 reserved4;
+       u8 err_code;
+       u16 reserved5;
+#endif
+       u32 reserved6[7];
+#if defined(__BIG_ENDIAN)
+       u16 reserved7;
+       u16 itt;
+#define ISCSI_TMF_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_TMF_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_TMF_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_TMF_RESPONSE_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+       u16 itt;
+#define ISCSI_TMF_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_TMF_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_TMF_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_TMF_RESPONSE_TYPE_SHIFT 14
+       u16 reserved7;
+#endif
+       u32 cq_req_sn;
+};
+
+/*
+ * iSCSI Text CQE
+ */
+struct bnx2i_text_response {
+#if defined(__BIG_ENDIAN)
+       u8 op_code;
+       u8 response_flags;
+#define ISCSI_TEXT_RESPONSE_RESERVED1 (0x3F<<0)
+#define ISCSI_TEXT_RESPONSE_RESERVED1_SHIFT 0
+#define ISCSI_TEXT_RESPONSE_CONT (0x1<<6)
+#define ISCSI_TEXT_RESPONSE_CONT_SHIFT 6
+#define ISCSI_TEXT_RESPONSE_FINAL (0x1<<7)
+#define ISCSI_TEXT_RESPONSE_FINAL_SHIFT 7
+       u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+       u16 reserved0;
+       u8 response_flags;
+#define ISCSI_TEXT_RESPONSE_RESERVED1 (0x3F<<0)
+#define ISCSI_TEXT_RESPONSE_RESERVED1_SHIFT 0
+#define ISCSI_TEXT_RESPONSE_CONT (0x1<<6)
+#define ISCSI_TEXT_RESPONSE_CONT_SHIFT 6
+#define ISCSI_TEXT_RESPONSE_FINAL (0x1<<7)
+#define ISCSI_TEXT_RESPONSE_FINAL_SHIFT 7
+       u8 op_code;
+#endif
+       u32 data_length;
+       u32 exp_cmd_sn;
+       u32 max_cmd_sn;
+       u32 ttt;
+       u32 reserved2;
+#if defined(__BIG_ENDIAN)
+       u16 reserved4;
+       u8 err_code;
+       u8 reserved3;
+#elif defined(__LITTLE_ENDIAN)
+       u8 reserved3;
+       u8 err_code;
+       u16 reserved4;
+#endif
+       u32 reserved5;
+       u32 lun[2];
+       u32 reserved6[4];
+#if defined(__BIG_ENDIAN)
+       u16 reserved7;
+       u16 itt;
+#define ISCSI_TEXT_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_TEXT_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_TEXT_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_TEXT_RESPONSE_TYPE_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+       u16 itt;
+#define ISCSI_TEXT_RESPONSE_INDEX (0x3FFF<<0)
+#define ISCSI_TEXT_RESPONSE_INDEX_SHIFT 0
+#define ISCSI_TEXT_RESPONSE_TYPE (0x3<<14)
+#define ISCSI_TEXT_RESPONSE_TYPE_SHIFT 14
+       u16 reserved7;
+#endif
+       u32 cq_req_sn;
+};
+
+/*
+ * iSCSI CQE
+ */
+union iscsi_response {
+       struct bnx2i_cmd_response cmd;
+       struct bnx2i_tmf_response tmf;
+       struct bnx2i_login_response login_resp;
+       struct bnx2i_text_response text;
+       struct bnx2i_logout_response logout_resp;
+       struct bnx2i_cleanup_response cleanup;
+       struct bnx2i_reject_msg reject;
+       struct bnx2i_async_msg async;
+       struct bnx2i_nop_in_msg nop_in;
+};
+
+#endif /* __57XX_ISCSI_HSI_LINUX_LE__ */
diff --git a/drivers/scsi/bnx2i/Kconfig b/drivers/scsi/bnx2i/Kconfig
new file mode 100644 (file)
index 0000000..820d428
--- /dev/null
@@ -0,0 +1,7 @@
+config SCSI_BNX2_ISCSI
+       tristate "Broadcom NetXtreme II iSCSI support"
+       select SCSI_ISCSI_ATTRS
+       select CNIC
+       ---help---
+       This driver supports iSCSI offload for the Broadcom NetXtreme II
+       devices.
diff --git a/drivers/scsi/bnx2i/Makefile b/drivers/scsi/bnx2i/Makefile
new file mode 100644 (file)
index 0000000..b5802bd
--- /dev/null
@@ -0,0 +1,3 @@
+bnx2i-y := bnx2i_init.o bnx2i_hwi.o bnx2i_iscsi.o bnx2i_sysfs.o
+
+obj-$(CONFIG_SCSI_BNX2_ISCSI) += bnx2i.o
diff --git a/drivers/scsi/bnx2i/bnx2i.h b/drivers/scsi/bnx2i/bnx2i.h
new file mode 100644 (file)
index 0000000..d7576f2
--- /dev/null
@@ -0,0 +1,771 @@
+/* bnx2i.h: Broadcom NetXtreme II iSCSI driver.
+ *
+ * Copyright (c) 2006 - 2009 Broadcom Corporation
+ * Copyright (c) 2007, 2008 Red Hat, Inc.  All rights reserved.
+ * Copyright (c) 2007, 2008 Mike Christie
+ *
+ * This program is free software; 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: Anil Veerabhadrappa (anilgv@broadcom.com)
+ */
+
+#ifndef _BNX2I_H_
+#define _BNX2I_H_
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/in.h>
+#include <linux/kfifo.h>
+#include <linux/netdevice.h>
+#include <linux/completion.h>
+
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi.h>
+#include <scsi/iscsi_proto.h>
+#include <scsi/libiscsi.h>
+#include <scsi/scsi_transport_iscsi.h>
+
+#include "../../net/cnic_if.h"
+#include "57xx_iscsi_hsi.h"
+#include "57xx_iscsi_constants.h"
+
+#define BNX2_ISCSI_DRIVER_NAME         "bnx2i"
+
+#define BNX2I_MAX_ADAPTERS             8
+
+#define ISCSI_MAX_CONNS_PER_HBA                128
+#define ISCSI_MAX_SESS_PER_HBA         ISCSI_MAX_CONNS_PER_HBA
+#define ISCSI_MAX_CMDS_PER_SESS                128
+
+/* Total active commands across all connections supported by devices */
+#define ISCSI_MAX_CMDS_PER_HBA_5708    (28 * (ISCSI_MAX_CMDS_PER_SESS - 1))
+#define ISCSI_MAX_CMDS_PER_HBA_5709    (128 * (ISCSI_MAX_CMDS_PER_SESS - 1))
+#define ISCSI_MAX_CMDS_PER_HBA_57710   (256 * (ISCSI_MAX_CMDS_PER_SESS - 1))
+
+#define ISCSI_MAX_BDS_PER_CMD          32
+
+#define MAX_PAGES_PER_CTRL_STRUCT_POOL 8
+#define BNX2I_RESERVED_SLOW_PATH_CMD_SLOTS     4
+
+/* 5706/08 hardware has limit on maximum buffer size per BD it can handle */
+#define MAX_BD_LENGTH                  65535
+#define BD_SPLIT_SIZE                  32768
+
+/* min, max & default values for SQ/RQ/CQ size, configurable via' modparam */
+#define BNX2I_SQ_WQES_MIN              16
+#define BNX2I_570X_SQ_WQES_MAX                 128
+#define BNX2I_5770X_SQ_WQES_MAX        512
+#define BNX2I_570X_SQ_WQES_DEFAULT     128
+#define BNX2I_5770X_SQ_WQES_DEFAULT    256
+
+#define BNX2I_570X_CQ_WQES_MAX                 128
+#define BNX2I_5770X_CQ_WQES_MAX        512
+
+#define BNX2I_RQ_WQES_MIN              16
+#define BNX2I_RQ_WQES_MAX              32
+#define BNX2I_RQ_WQES_DEFAULT          16
+
+/* CCELLs per conn */
+#define BNX2I_CCELLS_MIN               16
+#define BNX2I_CCELLS_MAX               96
+#define BNX2I_CCELLS_DEFAULT           64
+
+#define ITT_INVALID_SIGNATURE          0xFFFF
+
+#define ISCSI_CMD_CLEANUP_TIMEOUT      100
+
+#define BNX2I_CONN_CTX_BUF_SIZE                16384
+
+#define BNX2I_SQ_WQE_SIZE              64
+#define BNX2I_RQ_WQE_SIZE              256
+#define BNX2I_CQE_SIZE                 64
+
+#define MB_KERNEL_CTX_SHIFT            8
+#define MB_KERNEL_CTX_SIZE             (1 << MB_KERNEL_CTX_SHIFT)
+
+#define CTX_SHIFT                      7
+#define GET_CID_NUM(cid_addr)          ((cid_addr) >> CTX_SHIFT)
+
+#define CTX_OFFSET                     0x10000
+#define MAX_CID_CNT                    0x4000
+
+/* 5709 context registers */
+#define BNX2_MQ_CONFIG2                        0x00003d00
+#define BNX2_MQ_CONFIG2_CONT_SZ                (0x7L<<4)
+#define BNX2_MQ_CONFIG2_FIRST_L4L5     (0x1fL<<8)
+
+/* 57710's BAR2 is mapped to doorbell registers */
+#define BNX2X_DOORBELL_PCI_BAR         2
+#define BNX2X_MAX_CQS                  8
+
+#define CNIC_ARM_CQE                   1
+#define CNIC_DISARM_CQE                        0
+
+#define REG_RD(__hba, offset)                          \
+               readl(__hba->regview + offset)
+#define REG_WR(__hba, offset, val)                     \
+               writel(val, __hba->regview + offset)
+
+
+/**
+ * struct generic_pdu_resc - login pdu resource structure
+ *
+ * @req_buf:            driver buffer used to stage payload associated with
+ *                      the login request
+ * @req_dma_addr:       dma address for iscsi login request payload buffer
+ * @req_buf_size:       actual login request payload length
+ * @req_wr_ptr:         pointer into login request buffer when next data is
+ *                      to be written
+ * @resp_hdr:           iscsi header where iscsi login response header is to
+ *                      be recreated
+ * @resp_buf:           buffer to stage login response payload
+ * @resp_dma_addr:      login response payload buffer dma address
+ * @resp_buf_size:      login response paylod length
+ * @resp_wr_ptr:        pointer into login response buffer when next data is
+ *                      to be written
+ * @req_bd_tbl:         iscsi login request payload BD table
+ * @req_bd_dma:         login request BD table dma address
+ * @resp_bd_tbl:        iscsi login response payload BD table
+ * @resp_bd_dma:        login request BD table dma address
+ *
+ * following structure defines buffer info for generic pdus such as iSCSI Login,
+ *     Logout and NOP
+ */
+struct generic_pdu_resc {
+       char *req_buf;
+       dma_addr_t req_dma_addr;
+       u32 req_buf_size;
+       char *req_wr_ptr;
+       struct iscsi_hdr resp_hdr;
+       char *resp_buf;
+       dma_addr_t resp_dma_addr;
+       u32 resp_buf_size;
+       char *resp_wr_ptr;
+       char *req_bd_tbl;
+       dma_addr_t req_bd_dma;
+       char *resp_bd_tbl;
+       dma_addr_t resp_bd_dma;
+};
+
+
+/**
+ * struct bd_resc_page - tracks DMA'able memory allocated for BD tables
+ *
+ * @link:               list head to link elements
+ * @max_ptrs:           maximun pointers that can be stored in this page
+ * @num_valid:          number of pointer valid in this page
+ * @page:               base addess for page pointer array
+ *
+ * structure to track DMA'able memory allocated for command BD tables
+ */
+struct bd_resc_page {
+       struct list_head link;
+       u32 max_ptrs;
+       u32 num_valid;
+       void *page[1];
+};
+
+
+/**
+ * struct io_bdt - I/O buffer destricptor table
+ *
+ * @bd_tbl:             BD table's virtual address
+ * @bd_tbl_dma:         BD table's dma address
+ * @bd_valid:           num valid BD entries
+ *
+ * IO BD table
+ */
+struct io_bdt {
+       struct iscsi_bd *bd_tbl;
+       dma_addr_t bd_tbl_dma;
+       u16 bd_valid;
+};
+
+
+/**
+ * bnx2i_cmd - iscsi command structure
+ *
+ * @scsi_cmd:           SCSI-ML task pointer corresponding to this iscsi cmd
+ * @sg:                 SG list
+ * @io_tbl:             buffer descriptor (BD) table
+ * @bd_tbl_dma:         buffer descriptor (BD) table's dma address
+ */
+struct bnx2i_cmd {
+       struct iscsi_hdr hdr;
+       struct bnx2i_conn *conn;
+       struct scsi_cmnd *scsi_cmd;
+       struct scatterlist *sg;
+       struct io_bdt io_tbl;
+       dma_addr_t bd_tbl_dma;
+       struct bnx2i_cmd_request req;
+};
+
+
+/**
+ * struct bnx2i_conn - iscsi connection structure
+ *
+ * @cls_conn:              pointer to iscsi cls conn
+ * @hba:                   adapter structure pointer
+ * @iscsi_conn_cid:        iscsi conn id
+ * @fw_cid:                firmware iscsi context id
+ * @ep:                    endpoint structure pointer
+ * @gen_pdu:               login/nopout/logout pdu resources
+ * @violation_notified:    bit mask used to track iscsi error/warning messages
+ *                         already printed out
+ *
+ * iSCSI connection structure
+ */
+struct bnx2i_conn {
+       struct iscsi_cls_conn *cls_conn;
+       struct bnx2i_hba *hba;
+       struct completion cmd_cleanup_cmpl;
+       int is_bound;
+
+       u32 iscsi_conn_cid;
+#define BNX2I_CID_RESERVED     0x5AFF
+       u32 fw_cid;
+
+       struct timer_list poll_timer;
+       /*
+        * Queue Pair (QP) related structure elements.
+        */
+       struct bnx2i_endpoint *ep;
+
+       /*
+        * Buffer for login negotiation process
+        */
+       struct generic_pdu_resc gen_pdu;
+       u64 violation_notified;
+};
+
+
+
+/**
+ * struct iscsi_cid_queue - Per adapter iscsi cid queue
+ *
+ * @cid_que_base:           queue base memory
+ * @cid_que:                queue memory pointer
+ * @cid_q_prod_idx:         produce index
+ * @cid_q_cons_idx:         consumer index
+ * @cid_q_max_idx:          max index. used to detect wrap around condition
+ * @cid_free_cnt:           queue size
+ * @conn_cid_tbl:           iscsi cid to conn structure mapping table
+ *
+ * Per adapter iSCSI CID Queue
+ */
+struct iscsi_cid_queue {
+       void *cid_que_base;
+       u32 *cid_que;
+       u32 cid_q_prod_idx;
+       u32 cid_q_cons_idx;
+       u32 cid_q_max_idx;
+       u32 cid_free_cnt;
+       struct bnx2i_conn **conn_cid_tbl;
+};
+
+/**
+ * struct bnx2i_hba - bnx2i adapter structure
+ *
+ * @link:                  list head to link elements
+ * @cnic:                  pointer to cnic device
+ * @pcidev:                pointer to pci dev
+ * @netdev:                pointer to netdev structure
+ * @regview:               mapped PCI register space
+ * @age:                   age, incremented by every recovery
+ * @cnic_dev_type:         cnic device type, 5706/5708/5709/57710
+ * @mail_queue_access:     mailbox queue access mode, applicable to 5709 only
+ * @reg_with_cnic:         indicates whether the device is register with CNIC
+ * @adapter_state:         adapter state, UP, GOING_DOWN, LINK_DOWN
+ * @mtu_supported:         Ethernet MTU supported
+ * @shost:                 scsi host pointer
+ * @max_sqes:              SQ size
+ * @max_rqes:              RQ size
+ * @max_cqes:              CQ size
+ * @num_ccell:             number of command cells per connection
+ * @ofld_conns_active:     active connection list
+ * @max_active_conns:      max offload connections supported by this device
+ * @cid_que:               iscsi cid queue
+ * @ep_rdwr_lock:          read / write lock to synchronize various ep lists
+ * @ep_ofld_list:          connection list for pending offload completion
+ * @ep_destroy_list:       connection list for pending offload completion
+ * @mp_bd_tbl:             BD table to be used with middle path requests
+ * @mp_bd_dma:             DMA address of 'mp_bd_tbl' memory buffer
+ * @dummy_buffer:          Dummy buffer to be used with zero length scsicmd reqs
+ * @dummy_buf_dma:         DMA address of 'dummy_buffer' memory buffer
+ * @lock:                         lock to synchonize access to hba structure
+ * @pci_did:               PCI device ID
+ * @pci_vid:               PCI vendor ID
+ * @pci_sdid:              PCI subsystem device ID
+ * @pci_svid:              PCI subsystem vendor ID
+ * @pci_func:              PCI function number in system pci tree
+ * @pci_devno:             PCI device number in system pci tree
+ * @num_wqe_sent:          statistic counter, total wqe's sent
+ * @num_cqe_rcvd:          statistic counter, total cqe's received
+ * @num_intr_claimed:      statistic counter, total interrupts claimed
+ * @link_changed_count:    statistic counter, num of link change notifications
+ *                         received
+ * @ipaddr_changed_count:  statistic counter, num times IP address changed while
+ *                         at least one connection is offloaded
+ * @num_sess_opened:       statistic counter, total num sessions opened
+ * @num_conn_opened:       statistic counter, total num conns opened on this hba
+ * @ctx_ccell_tasks:       captures number of ccells and tasks supported by
+ *                         currently offloaded connection, used to decode
+ *                         context memory
+ *
+ * Adapter Data Structure
+ */
+struct bnx2i_hba {
+       struct list_head link;
+       struct cnic_dev *cnic;
+       struct pci_dev *pcidev;
+       struct net_device *netdev;
+       void __iomem *regview;
+
+       u32 age;
+       unsigned long cnic_dev_type;
+               #define BNX2I_NX2_DEV_5706              0x0
+               #define BNX2I_NX2_DEV_5708              0x1
+               #define BNX2I_NX2_DEV_5709              0x2
+               #define BNX2I_NX2_DEV_57710             0x3
+       u32 mail_queue_access;
+               #define BNX2I_MQ_KERNEL_MODE            0x0
+               #define BNX2I_MQ_KERNEL_BYPASS_MODE     0x1
+               #define BNX2I_MQ_BIN_MODE               0x2
+       unsigned long  reg_with_cnic;
+               #define BNX2I_CNIC_REGISTERED           1
+
+       unsigned long  adapter_state;
+               #define ADAPTER_STATE_UP                0
+               #define ADAPTER_STATE_GOING_DOWN        1
+               #define ADAPTER_STATE_LINK_DOWN         2
+               #define ADAPTER_STATE_INIT_FAILED       31
+       unsigned int mtu_supported;
+               #define BNX2I_MAX_MTU_SUPPORTED         1500
+
+       struct Scsi_Host *shost;
+
+       u32 max_sqes;
+       u32 max_rqes;
+       u32 max_cqes;
+       u32 num_ccell;
+
+       int ofld_conns_active;
+
+       int max_active_conns;
+       struct iscsi_cid_queue cid_que;
+
+       rwlock_t ep_rdwr_lock;
+       struct list_head ep_ofld_list;
+       struct list_head ep_destroy_list;
+
+       /*
+        * BD table to be used with MP (Middle Path requests.
+        */
+       char *mp_bd_tbl;
+       dma_addr_t mp_bd_dma;
+       char *dummy_buffer;
+       dma_addr_t dummy_buf_dma;
+
+       spinlock_t lock;        /* protects hba structure access */
+       struct mutex net_dev_lock;/* sync net device access */
+
+       /*
+        * PCI related info.
+        */
+       u16 pci_did;
+       u16 pci_vid;
+       u16 pci_sdid;
+       u16 pci_svid;
+       u16 pci_func;
+       u16 pci_devno;
+
+       /*
+        * Following are a bunch of statistics useful during development
+        * and later stage for score boarding.
+        */
+       u32 num_wqe_sent;
+       u32 num_cqe_rcvd;
+       u32 num_intr_claimed;
+       u32 link_changed_count;
+       u32 ipaddr_changed_count;
+       u32 num_sess_opened;
+       u32 num_conn_opened;
+       unsigned int ctx_ccell_tasks;
+};
+
+
+/*******************************************************************************
+ *     QP [ SQ / RQ / CQ ] info.
+ ******************************************************************************/
+
+/*
+ * SQ/RQ/CQ generic structure definition
+ */
+struct         sqe {
+       u8 sqe_byte[BNX2I_SQ_WQE_SIZE];
+};
+
+struct         rqe {
+       u8 rqe_byte[BNX2I_RQ_WQE_SIZE];
+};
+
+struct         cqe {
+       u8 cqe_byte[BNX2I_CQE_SIZE];
+};
+
+
+enum {
+#if defined(__LITTLE_ENDIAN)
+       CNIC_EVENT_COAL_INDEX   = 0x0,
+       CNIC_SEND_DOORBELL      = 0x4,
+       CNIC_EVENT_CQ_ARM       = 0x7,
+       CNIC_RECV_DOORBELL      = 0x8
+#elif defined(__BIG_ENDIAN)
+       CNIC_EVENT_COAL_INDEX   = 0x2,
+       CNIC_SEND_DOORBELL      = 0x6,
+       CNIC_EVENT_CQ_ARM       = 0x4,
+       CNIC_RECV_DOORBELL      = 0xa
+#endif
+};
+
+
+/*
+ * CQ DB
+ */
+struct bnx2x_iscsi_cq_pend_cmpl {
+       /* CQ producer, updated by Ustorm */
+       u16 ustrom_prod;
+       /* CQ pending completion counter */
+       u16 pend_cntr;
+};
+
+
+struct bnx2i_5771x_cq_db {
+       struct bnx2x_iscsi_cq_pend_cmpl qp_pend_cmpl[BNX2X_MAX_CQS];
+       /* CQ pending completion ITT array */
+       u16 itt[BNX2X_MAX_CQS];
+       /* Cstorm CQ sequence to notify array, updated by driver */;
+       u16 sqn[BNX2X_MAX_CQS];
+       u32 reserved[4] /* 16 byte allignment */;
+};
+
+
+struct bnx2i_5771x_sq_rq_db {
+       u16 prod_idx;
+       u8 reserved0[14]; /* Pad structure size to 16 bytes */
+};
+
+
+struct bnx2i_5771x_dbell_hdr {
+       u8 header;
+       /* 1 for rx doorbell, 0 for tx doorbell */
+#define B577XX_DOORBELL_HDR_RX                         (0x1<<0)
+#define B577XX_DOORBELL_HDR_RX_SHIFT                   0
+       /* 0 for normal doorbell, 1 for advertise wnd doorbell */
+#define B577XX_DOORBELL_HDR_DB_TYPE                    (0x1<<1)
+#define B577XX_DOORBELL_HDR_DB_TYPE_SHIFT              1
+       /* rdma tx only: DPM transaction size specifier (64/128/256/512B) */
+#define B577XX_DOORBELL_HDR_DPM_SIZE                   (0x3<<2)
+#define B577XX_DOORBELL_HDR_DPM_SIZE_SHIFT             2
+       /* connection type */
+#define B577XX_DOORBELL_HDR_CONN_TYPE                  (0xF<<4)
+#define B577XX_DOORBELL_HDR_CONN_TYPE_SHIFT            4
+};
+
+struct bnx2i_5771x_dbell {
+       struct bnx2i_5771x_dbell_hdr dbell;
+       u8 pad[3];
+
+};
+
+/**
+ * struct qp_info - QP (share queue region) atrributes structure
+ *
+ * @ctx_base:           ioremapped pci register base to access doorbell register
+ *                      pertaining to this offloaded connection
+ * @sq_virt:            virtual address of send queue (SQ) region
+ * @sq_phys:            DMA address of SQ memory region
+ * @sq_mem_size:        SQ size
+ * @sq_prod_qe:         SQ producer entry pointer
+ * @sq_cons_qe:         SQ consumer entry pointer
+ * @sq_first_qe:        virtaul address of first entry in SQ
+ * @sq_last_qe:         virtaul address of last entry in SQ
+ * @sq_prod_idx:        SQ producer index
+ * @sq_cons_idx:        SQ consumer index
+ * @sqe_left:           number sq entry left
+ * @sq_pgtbl_virt:      page table describing buffer consituting SQ region
+ * @sq_pgtbl_phys:      dma address of 'sq_pgtbl_virt'
+ * @sq_pgtbl_size:      SQ page table size
+ * @cq_virt:            virtual address of completion queue (CQ) region
+ * @cq_phys:            DMA address of RQ memory region
+ * @cq_mem_size:        CQ size
+ * @cq_prod_qe:         CQ producer entry pointer
+ * @cq_cons_qe:         CQ consumer entry pointer
+ * @cq_first_qe:        virtaul address of first entry in CQ
+ * @cq_last_qe:         virtaul address of last entry in CQ
+ * @cq_prod_idx:        CQ producer index
+ * @cq_cons_idx:        CQ consumer index
+ * @cqe_left:           number cq entry left
+ * @cqe_size:           size of each CQ entry
+ * @cqe_exp_seq_sn:     next expected CQE sequence number
+ * @cq_pgtbl_virt:      page table describing buffer consituting CQ region
+ * @cq_pgtbl_phys:      dma address of 'cq_pgtbl_virt'
+ * @cq_pgtbl_size:     CQ page table size
+ * @rq_virt:            virtual address of receive queue (RQ) region
+ * @rq_phys:            DMA address of RQ memory region
+ * @rq_mem_size:        RQ size
+ * @rq_prod_qe:         RQ producer entry pointer
+ * @rq_cons_qe:         RQ consumer entry pointer
+ * @rq_first_qe:        virtaul address of first entry in RQ
+ * @rq_last_qe:         virtaul address of last entry in RQ
+ * @rq_prod_idx:        RQ producer index
+ * @rq_cons_idx:        RQ consumer index
+ * @rqe_left:           number rq entry left
+ * @rq_pgtbl_virt:      page table describing buffer consituting RQ region
+ * @rq_pgtbl_phys:      dma address of 'rq_pgtbl_virt'
+ * @rq_pgtbl_size:      RQ page table size
+ *
+ * queue pair (QP) is a per connection shared data structure which is used
+ *     to send work requests (SQ), receive completion notifications (CQ)
+ *     and receive asynchoronous / scsi sense info (RQ). 'qp_info' structure
+ *     below holds queue memory, consumer/producer indexes and page table
+ *     information
+ */
+struct qp_info {
+       void __iomem *ctx_base;
+#define DPM_TRIGER_TYPE                        0x40
+
+#define BNX2I_570x_QUE_DB_SIZE         0
+#define BNX2I_5771x_QUE_DB_SIZE                16
+       struct sqe *sq_virt;
+       dma_addr_t sq_phys;
+       u32 sq_mem_size;
+
+       struct sqe *sq_prod_qe;
+       struct sqe *sq_cons_qe;
+       struct sqe *sq_first_qe;
+       struct sqe *sq_last_qe;
+       u16 sq_prod_idx;
+       u16 sq_cons_idx;
+       u32 sqe_left;
+
+       void *sq_pgtbl_virt;
+       dma_addr_t sq_pgtbl_phys;
+       u32 sq_pgtbl_size;      /* set to PAGE_SIZE for 5708 & 5709 */
+
+       struct cqe *cq_virt;
+       dma_addr_t cq_phys;
+       u32 cq_mem_size;
+
+       struct cqe *cq_prod_qe;
+       struct cqe *cq_cons_qe;
+       struct cqe *cq_first_qe;
+       struct cqe *cq_last_qe;
+       u16 cq_prod_idx;
+       u16 cq_cons_idx;
+       u32 cqe_left;
+       u32 cqe_size;
+       u32 cqe_exp_seq_sn;
+
+       void *cq_pgtbl_virt;
+       dma_addr_t cq_pgtbl_phys;
+       u32 cq_pgtbl_size;      /* set to PAGE_SIZE for 5708 & 5709 */
+
+       struct rqe *rq_virt;
+       dma_addr_t rq_phys;
+       u32 rq_mem_size;
+
+       struct rqe *rq_prod_qe;
+       struct rqe *rq_cons_qe;
+       struct rqe *rq_first_qe;
+       struct rqe *rq_last_qe;
+       u16 rq_prod_idx;
+       u16 rq_cons_idx;
+       u32 rqe_left;
+
+       void *rq_pgtbl_virt;
+       dma_addr_t rq_pgtbl_phys;
+       u32 rq_pgtbl_size;      /* set to PAGE_SIZE for 5708 & 5709 */
+};
+
+
+
+/*
+ * CID handles
+ */
+struct ep_handles {
+       u32 fw_cid;
+       u32 drv_iscsi_cid;
+       u16 pg_cid;
+       u16 rsvd;
+};
+
+
+enum {
+       EP_STATE_IDLE                   = 0x0,
+       EP_STATE_PG_OFLD_START          = 0x1,
+       EP_STATE_PG_OFLD_COMPL          = 0x2,
+       EP_STATE_OFLD_START             = 0x4,
+       EP_STATE_OFLD_COMPL             = 0x8,
+       EP_STATE_CONNECT_START          = 0x10,
+       EP_STATE_CONNECT_COMPL          = 0x20,
+       EP_STATE_ULP_UPDATE_START       = 0x40,
+       EP_STATE_ULP_UPDATE_COMPL       = 0x80,
+       EP_STATE_DISCONN_START          = 0x100,
+       EP_STATE_DISCONN_COMPL          = 0x200,
+       EP_STATE_CLEANUP_START          = 0x400,
+       EP_STATE_CLEANUP_CMPL           = 0x800,
+       EP_STATE_TCP_FIN_RCVD           = 0x1000,
+       EP_STATE_TCP_RST_RCVD           = 0x2000,
+       EP_STATE_PG_OFLD_FAILED         = 0x1000000,
+       EP_STATE_ULP_UPDATE_FAILED      = 0x2000000,
+       EP_STATE_CLEANUP_FAILED         = 0x4000000,
+       EP_STATE_OFLD_FAILED            = 0x8000000,
+       EP_STATE_CONNECT_FAILED         = 0x10000000,
+       EP_STATE_DISCONN_TIMEDOUT       = 0x20000000,
+};
+
+/**
+ * struct bnx2i_endpoint - representation of tcp connection in NX2 world
+ *
+ * @link:               list head to link elements
+ * @hba:                adapter to which this connection belongs
+ * @conn:               iscsi connection this EP is linked to
+ * @sess:               iscsi session this EP is linked to
+ * @cm_sk:              cnic sock struct
+ * @hba_age:            age to detect if 'iscsid' issues ep_disconnect()
+ *                      after HBA reset is completed by bnx2i/cnic/bnx2
+ *                      modules
+ * @state:              tracks offload connection state machine
+ * @teardown_mode:      indicates if conn teardown is abortive or orderly
+ * @qp:                 QP information
+ * @ids:                contains chip allocated *context id* & driver assigned
+ *                      *iscsi cid*
+ * @ofld_timer:         offload timer to detect timeout
+ * @ofld_wait:          wait queue
+ *
+ * Endpoint Structure - equivalent of tcp socket structure
+ */
+struct bnx2i_endpoint {
+       struct list_head link;
+       struct bnx2i_hba *hba;
+       struct bnx2i_conn *conn;
+       struct cnic_sock *cm_sk;
+       u32 hba_age;
+       u32 state;
+       unsigned long timestamp;
+       int num_active_cmds;
+
+       struct qp_info qp;
+       struct ep_handles ids;
+               #define ep_iscsi_cid    ids.drv_iscsi_cid
+               #define ep_cid          ids.fw_cid
+               #define ep_pg_cid       ids.pg_cid
+       struct timer_list ofld_timer;
+       wait_queue_head_t ofld_wait;
+};
+
+
+
+/* Global variables */
+extern unsigned int error_mask1, error_mask2;
+extern u64 iscsi_error_mask;
+extern unsigned int en_tcp_dack;
+extern unsigned int event_coal_div;
+
+extern struct scsi_transport_template *bnx2i_scsi_xport_template;
+extern struct iscsi_transport bnx2i_iscsi_transport;
+extern struct cnic_ulp_ops bnx2i_cnic_cb;
+
+extern unsigned int sq_size;
+extern unsigned int rq_size;
+
+extern struct device_attribute *bnx2i_dev_attributes[];
+
+
+
+/*
+ * Function Prototypes
+ */
+extern void bnx2i_identify_device(struct bnx2i_hba *hba);
+extern void bnx2i_register_device(struct bnx2i_hba *hba);
+
+extern void bnx2i_ulp_init(struct cnic_dev *dev);
+extern void bnx2i_ulp_exit(struct cnic_dev *dev);
+extern void bnx2i_start(void *handle);
+extern void bnx2i_stop(void *handle);
+extern void bnx2i_reg_dev_all(void);
+extern void bnx2i_unreg_dev_all(void);
+extern struct bnx2i_hba *get_adapter_list_head(void);
+
+struct bnx2i_conn *bnx2i_get_conn_from_id(struct bnx2i_hba *hba,
+                                         u16 iscsi_cid);
+
+int bnx2i_alloc_ep_pool(void);
+void bnx2i_release_ep_pool(void);
+struct bnx2i_endpoint *bnx2i_ep_ofld_list_next(struct bnx2i_hba *hba);
+struct bnx2i_endpoint *bnx2i_ep_destroy_list_next(struct bnx2i_hba *hba);
+
+struct bnx2i_hba *bnx2i_find_hba_for_cnic(struct cnic_dev *cnic);
+
+struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic);
+void bnx2i_free_hba(struct bnx2i_hba *hba);
+
+void bnx2i_get_rq_buf(struct bnx2i_conn *conn, char *ptr, int len);
+void bnx2i_put_rq_buf(struct bnx2i_conn *conn, int count);
+
+void bnx2i_iscsi_unmap_sg_list(struct bnx2i_cmd *cmd);
+
+void bnx2i_drop_session(struct iscsi_cls_session *session);
+
+extern int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba);
+extern int bnx2i_send_iscsi_login(struct bnx2i_conn *conn,
+                                 struct iscsi_task *mtask);
+extern int bnx2i_send_iscsi_tmf(struct bnx2i_conn *conn,
+                                 struct iscsi_task *mtask);
+extern int bnx2i_send_iscsi_scsicmd(struct bnx2i_conn *conn,
+                                   struct bnx2i_cmd *cmnd);
+extern int bnx2i_send_iscsi_nopout(struct bnx2i_conn *conn,
+                                  struct iscsi_task *mtask, u32 ttt,
+                                  char *datap, int data_len, int unsol);
+extern int bnx2i_send_iscsi_logout(struct bnx2i_conn *conn,
+                                  struct iscsi_task *mtask);
+extern void bnx2i_send_cmd_cleanup_req(struct bnx2i_hba *hba,
+                                      struct bnx2i_cmd *cmd);
+extern void bnx2i_send_conn_ofld_req(struct bnx2i_hba *hba,
+                                    struct bnx2i_endpoint *ep);
+extern void bnx2i_update_iscsi_conn(struct iscsi_conn *conn);
+extern void bnx2i_send_conn_destroy(struct bnx2i_hba *hba,
+                                   struct bnx2i_endpoint *ep);
+
+extern int bnx2i_alloc_qp_resc(struct bnx2i_hba *hba,
+                              struct bnx2i_endpoint *ep);
+extern void bnx2i_free_qp_resc(struct bnx2i_hba *hba,
+                              struct bnx2i_endpoint *ep);
+extern void bnx2i_ep_ofld_timer(unsigned long data);
+extern struct bnx2i_endpoint *bnx2i_find_ep_in_ofld_list(
+               struct bnx2i_hba *hba, u32 iscsi_cid);
+extern struct bnx2i_endpoint *bnx2i_find_ep_in_destroy_list(
+               struct bnx2i_hba *hba, u32 iscsi_cid);
+
+extern int bnx2i_map_ep_dbell_regs(struct bnx2i_endpoint *ep);
+extern void bnx2i_arm_cq_event_coalescing(struct bnx2i_endpoint *ep, u8 action);
+
+/* Debug related function prototypes */
+extern void bnx2i_print_pend_cmd_queue(struct bnx2i_conn *conn);
+extern void bnx2i_print_active_cmd_queue(struct bnx2i_conn *conn);
+extern void bnx2i_print_xmit_pdu_queue(struct bnx2i_conn *conn);
+extern void bnx2i_print_recv_state(struct bnx2i_conn *conn);
+
+#endif
diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c
new file mode 100644 (file)
index 0000000..906cef5
--- /dev/null
@@ -0,0 +1,2405 @@
+/* bnx2i_hwi.c: Broadcom NetXtreme II iSCSI driver.
+ *
+ * Copyright (c) 2006 - 2009 Broadcom Corporation
+ * Copyright (c) 2007, 2008 Red Hat, Inc.  All rights reserved.
+ * Copyright (c) 2007, 2008 Mike Christie
+ *
+ * This program is free software; 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: Anil Veerabhadrappa (anilgv@broadcom.com)
+ */
+
+#include <scsi/scsi_tcq.h>
+#include <scsi/libiscsi.h>
+#include "bnx2i.h"
+
+/**
+ * bnx2i_get_cid_num - get cid from ep
+ * @ep:        endpoint pointer
+ *
+ * Only applicable to 57710 family of devices
+ */
+static u32 bnx2i_get_cid_num(struct bnx2i_endpoint *ep)
+{
+       u32 cid;
+
+       if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type))
+               cid = ep->ep_cid;
+       else
+               cid = GET_CID_NUM(ep->ep_cid);
+       return cid;
+}
+
+
+/**
+ * bnx2i_adjust_qp_size - Adjust SQ/RQ/CQ size for 57710 device type
+ * @hba:               Adapter for which adjustments is to be made
+ *
+ * Only applicable to 57710 family of devices
+ */
+static void bnx2i_adjust_qp_size(struct bnx2i_hba *hba)
+{
+       u32 num_elements_per_pg;
+
+       if (test_bit(BNX2I_NX2_DEV_5706, &hba->cnic_dev_type) ||
+           test_bit(BNX2I_NX2_DEV_5708, &hba->cnic_dev_type) ||
+           test_bit(BNX2I_NX2_DEV_5709, &hba->cnic_dev_type)) {
+               if (!is_power_of_2(hba->max_sqes))
+                       hba->max_sqes = rounddown_pow_of_two(hba->max_sqes);
+
+               if (!is_power_of_2(hba->max_rqes))
+                       hba->max_rqes = rounddown_pow_of_two(hba->max_rqes);
+       }
+
+       /* Adjust each queue size if the user selection does not
+        * yield integral num of page buffers
+        */
+       /* adjust SQ */
+       num_elements_per_pg = PAGE_SIZE / BNX2I_SQ_WQE_SIZE;
+       if (hba->max_sqes < num_elements_per_pg)
+               hba->max_sqes = num_elements_per_pg;
+       else if (hba->max_sqes % num_elements_per_pg)
+               hba->max_sqes = (hba->max_sqes + num_elements_per_pg - 1) &
+                                ~(num_elements_per_pg - 1);
+
+       /* adjust CQ */
+       num_elements_per_pg = PAGE_SIZE / BNX2I_CQE_SIZE;
+       if (hba->max_cqes < num_elements_per_pg)
+               hba->max_cqes = num_elements_per_pg;
+       else if (hba->max_cqes % num_elements_per_pg)
+               hba->max_cqes = (hba->max_cqes + num_elements_per_pg - 1) &
+                                ~(num_elements_per_pg - 1);
+
+       /* adjust RQ */
+       num_elements_per_pg = PAGE_SIZE / BNX2I_RQ_WQE_SIZE;
+       if (hba->max_rqes < num_elements_per_pg)
+               hba->max_rqes = num_elements_per_pg;
+       else if (hba->max_rqes % num_elements_per_pg)
+               hba->max_rqes = (hba->max_rqes + num_elements_per_pg - 1) &
+                                ~(num_elements_per_pg - 1);
+}
+
+
+/**
+ * bnx2i_get_link_state - get network interface link state
+ * @hba:       adapter instance pointer
+ *
+ * updates adapter structure flag based on netdev state
+ */
+static void bnx2i_get_link_state(struct bnx2i_hba *hba)
+{
+       if (test_bit(__LINK_STATE_NOCARRIER, &hba->netdev->state))
+               set_bit(ADAPTER_STATE_LINK_DOWN, &hba->adapter_state);
+       else
+               clear_bit(ADAPTER_STATE_LINK_DOWN, &hba->adapter_state);
+}
+
+
+/**
+ * bnx2i_iscsi_license_error - displays iscsi license related error message
+ * @hba:               adapter instance pointer
+ * @error_code:                error classification
+ *
+ * Puts out an error log when driver is unable to offload iscsi connection
+ *     due to license restrictions
+ */
+static void bnx2i_iscsi_license_error(struct bnx2i_hba *hba, u32 error_code)
+{
+       if (error_code == ISCSI_KCQE_COMPLETION_STATUS_ISCSI_NOT_SUPPORTED)
+               /* iSCSI offload not supported on this device */
+               printk(KERN_ERR "bnx2i: iSCSI not supported, dev=%s\n",
+                               hba->netdev->name);
+       if (error_code == ISCSI_KCQE_COMPLETION_STATUS_LOM_ISCSI_NOT_ENABLED)
+               /* iSCSI offload not supported on this LOM device */
+               printk(KERN_ERR "bnx2i: LOM is not enable to "
+                               "offload iSCSI connections, dev=%s\n",
+                               hba->netdev->name);
+       set_bit(ADAPTER_STATE_INIT_FAILED, &hba->adapter_state);
+}
+
+
+/**
+ * bnx2i_arm_cq_event_coalescing - arms CQ to enable EQ notification
+ * @ep:                endpoint (transport indentifier) structure
+ * @action:    action, ARM or DISARM. For now only ARM_CQE is used
+ *
+ * Arm'ing CQ will enable chip to generate global EQ events inorder to interrupt
+ *     the driver. EQ event is generated CQ index is hit or at least 1 CQ is
+ *     outstanding and on chip timer expires
+ */
+void bnx2i_arm_cq_event_coalescing(struct bnx2i_endpoint *ep, u8 action)
+{
+       struct bnx2i_5771x_cq_db *cq_db;
+       u16 cq_index;
+
+       if (!test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type))
+               return;
+
+       if (action == CNIC_ARM_CQE) {
+               cq_index = ep->qp.cqe_exp_seq_sn +
+                          ep->num_active_cmds / event_coal_div;
+               cq_index %= (ep->qp.cqe_size * 2 + 1);
+               if (!cq_index) {
+                       cq_index = 1;
+                       cq_db = (struct bnx2i_5771x_cq_db *)
+                                       ep->qp.cq_pgtbl_virt;
+                       cq_db->sqn[0] = cq_index;
+               }
+       }
+}
+
+
+/**
+ * bnx2i_get_rq_buf - copy RQ buffer contents to driver buffer
+ * @conn:              iscsi connection on which RQ event occured
+ * @ptr:               driver buffer to which RQ buffer contents is to
+ *                     be copied
+ * @len:               length of valid data inside RQ buf
+ *
+ * Copies RQ buffer contents from shared (DMA'able) memory region to
+ *     driver buffer. RQ is used to DMA unsolicitated iscsi pdu's and
+ *     scsi sense info
+ */
+void bnx2i_get_rq_buf(struct bnx2i_conn *bnx2i_conn, char *ptr, int len)
+{
+       if (!bnx2i_conn->ep->qp.rqe_left)
+               return;
+
+       bnx2i_conn->ep->qp.rqe_left--;
+       memcpy(ptr, (u8 *) bnx2i_conn->ep->qp.rq_cons_qe, len);
+       if (bnx2i_conn->ep->qp.rq_cons_qe == bnx2i_conn->ep->qp.rq_last_qe) {
+               bnx2i_conn->ep->qp.rq_cons_qe = bnx2i_conn->ep->qp.rq_first_qe;
+               bnx2i_conn->ep->qp.rq_cons_idx = 0;
+       } else {
+               bnx2i_conn->ep->qp.rq_cons_qe++;
+               bnx2i_conn->ep->qp.rq_cons_idx++;
+       }
+}
+
+
+static void bnx2i_ring_577xx_doorbell(struct bnx2i_conn *conn)
+{
+       struct bnx2i_5771x_dbell dbell;
+       u32 msg;
+
+       memset(&dbell, 0, sizeof(dbell));
+       dbell.dbell.header = (B577XX_ISCSI_CONNECTION_TYPE <<
+                             B577XX_DOORBELL_HDR_CONN_TYPE_SHIFT);
+       msg = *((u32 *)&dbell);
+       /* TODO : get doorbell register mapping */
+       writel(cpu_to_le32(msg), conn->ep->qp.ctx_base);
+}
+
+
+/**
+ * bnx2i_put_rq_buf - Replenish RQ buffer, if required ring on chip doorbell
+ * @conn:      iscsi connection on which event to post
+ * @count:     number of RQ buffer being posted to chip
+ *
+ * No need to ring hardware doorbell for 57710 family of devices
+ */
+void bnx2i_put_rq_buf(struct bnx2i_conn *bnx2i_conn, int count)
+{
+       struct bnx2i_5771x_sq_rq_db *rq_db;
+       u16 hi_bit = (bnx2i_conn->ep->qp.rq_prod_idx & 0x8000);
+       struct bnx2i_endpoint *ep = bnx2i_conn->ep;
+
+       ep->qp.rqe_left += count;
+       ep->qp.rq_prod_idx &= 0x7FFF;
+       ep->qp.rq_prod_idx += count;
+
+       if (ep->qp.rq_prod_idx > bnx2i_conn->hba->max_rqes) {
+               ep->qp.rq_prod_idx %= bnx2i_conn->hba->max_rqes;
+               if (!hi_bit)
+                       ep->qp.rq_prod_idx |= 0x8000;
+       } else
+               ep->qp.rq_prod_idx |= hi_bit;
+
+       if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) {
+               rq_db = (struct bnx2i_5771x_sq_rq_db *) ep->qp.rq_pgtbl_virt;
+               rq_db->prod_idx = ep->qp.rq_prod_idx;
+               /* no need to ring hardware doorbell for 57710 */
+       } else {
+               writew(ep->qp.rq_prod_idx,
+                      ep->qp.ctx_base + CNIC_RECV_DOORBELL);
+       }
+       mmiowb();
+}
+
+
+/**
+ * bnx2i_ring_sq_dbell - Ring SQ doorbell to wake-up the processing engine
+ * @conn:              iscsi connection to which new SQ entries belong
+ * @count:             number of SQ WQEs to post
+ *
+ * SQ DB is updated in host memory and TX Doorbell is rung for 57710 family
+ *     of devices. For 5706/5708/5709 new SQ WQE count is written into the
+ *     doorbell register
+ */
+static void bnx2i_ring_sq_dbell(struct bnx2i_conn *bnx2i_conn, int count)
+{
+       struct bnx2i_5771x_sq_rq_db *sq_db;
+       struct bnx2i_endpoint *ep = bnx2i_conn->ep;
+
+       ep->num_active_cmds++;
+       wmb();  /* flush SQ WQE memory before the doorbell is rung */
+       if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) {
+               sq_db = (struct bnx2i_5771x_sq_rq_db *) ep->qp.sq_pgtbl_virt;
+               sq_db->prod_idx = ep->qp.sq_prod_idx;
+               bnx2i_ring_577xx_doorbell(bnx2i_conn);
+       } else
+               writew(count, ep->qp.ctx_base + CNIC_SEND_DOORBELL);
+
+       mmiowb(); /* flush posted PCI writes */
+}
+
+
+/**
+ * bnx2i_ring_dbell_update_sq_params - update SQ driver parameters
+ * @conn:      iscsi connection to which new SQ entries belong
+ * @count:     number of SQ WQEs to post
+ *
+ * this routine will update SQ driver parameters and ring the doorbell
+ */
+static void bnx2i_ring_dbell_update_sq_params(struct bnx2i_conn *bnx2i_conn,
+                                             int count)
+{
+       int tmp_cnt;
+
+       if (count == 1) {
+               if (bnx2i_conn->ep->qp.sq_prod_qe ==
+                   bnx2i_conn->ep->qp.sq_last_qe)
+                       bnx2i_conn->ep->qp.sq_prod_qe =
+                                               bnx2i_conn->ep->qp.sq_first_qe;
+               else
+                       bnx2i_conn->ep->qp.sq_prod_qe++;
+       } else {
+               if ((bnx2i_conn->ep->qp.sq_prod_qe + count) <=
+                   bnx2i_conn->ep->qp.sq_last_qe)
+                       bnx2i_conn->ep->qp.sq_prod_qe += count;
+               else {
+                       tmp_cnt = bnx2i_conn->ep->qp.sq_last_qe -
+                               bnx2i_conn->ep->qp.sq_prod_qe;
+                       bnx2i_conn->ep->qp.sq_prod_qe =
+                               &bnx2i_conn->ep->qp.sq_first_qe[count -
+                                                               (tmp_cnt + 1)];
+               }
+       }
+       bnx2i_conn->ep->qp.sq_prod_idx += count;
+       /* Ring the doorbell */
+       bnx2i_ring_sq_dbell(bnx2i_conn, bnx2i_conn->ep->qp.sq_prod_idx);
+}
+
+
+/**
+ * bnx2i_send_iscsi_login - post iSCSI login request MP WQE to hardware
+ * @conn:      iscsi connection
+ * @cmd:       driver command structure which is requesting
+ *             a WQE to sent to chip for further processing
+ *
+ * prepare and post an iSCSI Login request WQE to CNIC firmware
+ */
+int bnx2i_send_iscsi_login(struct bnx2i_conn *bnx2i_conn,
+                          struct iscsi_task *task)
+{
+       struct bnx2i_cmd *bnx2i_cmd;
+       struct bnx2i_login_request *login_wqe;
+       struct iscsi_login *login_hdr;
+       u32 dword;
+
+       bnx2i_cmd = (struct bnx2i_cmd *)task->dd_data;
+       login_hdr = (struct iscsi_login *)task->hdr;
+       login_wqe = (struct bnx2i_login_request *)
+                                               bnx2i_conn->ep->qp.sq_prod_qe;
+
+       login_wqe->op_code = login_hdr->opcode;
+       login_wqe->op_attr = login_hdr->flags;
+       login_wqe->version_max = login_hdr->max_version;
+       login_wqe->version_min = login_hdr->min_version;
+       login_wqe->data_length = ntoh24(login_hdr->dlength);
+       login_wqe->isid_lo = *((u32 *) login_hdr->isid);
+       login_wqe->isid_hi = *((u16 *) login_hdr->isid + 2);
+       login_wqe->tsih = login_hdr->tsih;
+       login_wqe->itt = task->itt |
+               (ISCSI_TASK_TYPE_MPATH << ISCSI_LOGIN_REQUEST_TYPE_SHIFT);
+       login_wqe->cid = login_hdr->cid;
+
+       login_wqe->cmd_sn = be32_to_cpu(login_hdr->cmdsn);
+       login_wqe->exp_stat_sn = be32_to_cpu(login_hdr->exp_statsn);
+
+       login_wqe->resp_bd_list_addr_lo = (u32) bnx2i_conn->gen_pdu.resp_bd_dma;
+       login_wqe->resp_bd_list_addr_hi =
+               (u32) ((u64) bnx2i_conn->gen_pdu.resp_bd_dma >> 32);
+
+       dword = ((1 << ISCSI_LOGIN_REQUEST_NUM_RESP_BDS_SHIFT) |
+                (bnx2i_conn->gen_pdu.resp_buf_size <<
+                 ISCSI_LOGIN_REQUEST_RESP_BUFFER_LENGTH_SHIFT));
+       login_wqe->resp_buffer = dword;
+       login_wqe->flags = 0;
+       login_wqe->bd_list_addr_lo = (u32) bnx2i_conn->gen_pdu.req_bd_dma;
+       login_wqe->bd_list_addr_hi =
+               (u32) ((u64) bnx2i_conn->gen_pdu.req_bd_dma >> 32);
+       login_wqe->num_bds = 1;
+       login_wqe->cq_index = 0; /* CQ# used for completion, 5771x only */
+
+       bnx2i_ring_dbell_update_sq_params(bnx2i_conn, 1);
+       return 0;
+}
+
+/**
+ * bnx2i_send_iscsi_tmf - post iSCSI task management request MP WQE to hardware
+ * @conn:      iscsi connection
+ * @mtask:     driver command structure which is requesting
+ *             a WQE to sent to chip for further processing
+ *
+ * prepare and post an iSCSI Login request WQE to CNIC firmware
+ */
+int bnx2i_send_iscsi_tmf(struct bnx2i_conn *bnx2i_conn,
+                        struct iscsi_task *mtask)
+{
+       struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+       struct iscsi_tm *tmfabort_hdr;
+       struct scsi_cmnd *ref_sc;
+       struct iscsi_task *ctask;
+       struct bnx2i_cmd *bnx2i_cmd;
+       struct bnx2i_tmf_request *tmfabort_wqe;
+       u32 dword;
+
+       bnx2i_cmd = (struct bnx2i_cmd *)mtask->dd_data;
+       tmfabort_hdr = (struct iscsi_tm *)mtask->hdr;
+       tmfabort_wqe = (struct bnx2i_tmf_request *)
+                                               bnx2i_conn->ep->qp.sq_prod_qe;
+
+       tmfabort_wqe->op_code = tmfabort_hdr->opcode;
+       tmfabort_wqe->op_attr = 0;
+       tmfabort_wqe->op_attr =
+               ISCSI_TMF_REQUEST_ALWAYS_ONE | ISCSI_TM_FUNC_ABORT_TASK;
+       tmfabort_wqe->lun[0] = be32_to_cpu(tmfabort_hdr->lun[0]);
+       tmfabort_wqe->lun[1] = be32_to_cpu(tmfabort_hdr->lun[1]);
+
+       tmfabort_wqe->itt = (mtask->itt | (ISCSI_TASK_TYPE_MPATH << 14));
+       tmfabort_wqe->reserved2 = 0;
+       tmfabort_wqe->cmd_sn = be32_to_cpu(tmfabort_hdr->cmdsn);
+
+       ctask = iscsi_itt_to_task(conn, tmfabort_hdr->rtt);
+       if (!ctask || ctask->sc)
+               /*
+                * the iscsi layer must have completed the cmd while this
+                * was starting up.
+                */
+               return 0;
+       ref_sc = ctask->sc;
+
+       if (ref_sc->sc_data_direction == DMA_TO_DEVICE)
+               dword = (ISCSI_TASK_TYPE_WRITE << ISCSI_CMD_REQUEST_TYPE_SHIFT);
+       else
+               dword = (ISCSI_TASK_TYPE_READ << ISCSI_CMD_REQUEST_TYPE_SHIFT);
+       tmfabort_wqe->ref_itt = (dword | tmfabort_hdr->rtt);
+       tmfabort_wqe->ref_cmd_sn = be32_to_cpu(tmfabort_hdr->refcmdsn);
+
+       tmfabort_wqe->bd_list_addr_lo = (u32) bnx2i_conn->hba->mp_bd_dma;
+       tmfabort_wqe->bd_list_addr_hi = (u32)
+                               ((u64) bnx2i_conn->hba->mp_bd_dma >> 32);
+       tmfabort_wqe->num_bds = 1;
+       tmfabort_wqe->cq_index = 0; /* CQ# used for completion, 5771x only */
+
+       bnx2i_ring_dbell_update_sq_params(bnx2i_conn, 1);
+       return 0;
+}
+
+/**
+ * bnx2i_send_iscsi_scsicmd - post iSCSI scsicmd request WQE to hardware
+ * @conn:      iscsi connection
+ * @cmd:       driver command structure which is requesting
+ *             a WQE to sent to chip for further processing
+ *
+ * prepare and post an iSCSI SCSI-CMD request WQE to CNIC firmware
+ */
+int bnx2i_send_iscsi_scsicmd(struct bnx2i_conn *bnx2i_conn,
+                            struct bnx2i_cmd *cmd)
+{
+       struct bnx2i_cmd_request *scsi_cmd_wqe;
+
+       scsi_cmd_wqe = (struct bnx2i_cmd_request *)
+                                               bnx2i_conn->ep->qp.sq_prod_qe;
+       memcpy(scsi_cmd_wqe, &cmd->req, sizeof(struct bnx2i_cmd_request));
+       scsi_cmd_wqe->cq_index = 0; /* CQ# used for completion, 5771x only */
+
+       bnx2i_ring_dbell_update_sq_params(bnx2i_conn, 1);
+       return 0;
+}
+
+/**
+ * bnx2i_send_iscsi_nopout - post iSCSI NOPOUT request WQE to hardware
+ * @conn:              iscsi connection
+ * @cmd:               driver command structure which is requesting
+ *                     a WQE to sent to chip for further processing
+ * @ttt:               TTT to be used when building pdu header
+ * @datap:             payload buffer pointer
+ * @data_len:          payload data length
+ * @unsol:             indicated whether nopout pdu is unsolicited pdu or
+ *                     in response to target's NOPIN w/ TTT != FFFFFFFF
+ *
+ * prepare and post a nopout request WQE to CNIC firmware
+ */
+int bnx2i_send_iscsi_nopout(struct bnx2i_conn *bnx2i_conn,
+                           struct iscsi_task *task, u32 ttt,
+                           char *datap, int data_len, int unsol)
+{
+       struct bnx2i_endpoint *ep = bnx2i_conn->ep;
+       struct bnx2i_cmd *bnx2i_cmd;
+       struct bnx2i_nop_out_request *nopout_wqe;
+       struct iscsi_nopout *nopout_hdr;
+
+       bnx2i_cmd = (struct bnx2i_cmd *)task->dd_data;
+       nopout_hdr = (struct iscsi_nopout *)task->hdr;
+       nopout_wqe = (struct bnx2i_nop_out_request *)ep->qp.sq_prod_qe;
+       nopout_wqe->op_code = nopout_hdr->opcode;
+       nopout_wqe->op_attr = ISCSI_FLAG_CMD_FINAL;
+       memcpy(nopout_wqe->lun, nopout_hdr->lun, 8);
+
+       if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) {
+               u32 tmp = nopout_hdr->lun[0];
+               /* 57710 requires LUN field to be swapped */
+               nopout_hdr->lun[0] = nopout_hdr->lun[1];
+               nopout_hdr->lun[1] = tmp;
+       }
+
+       nopout_wqe->itt = ((u16)task->itt |
+                          (ISCSI_TASK_TYPE_MPATH <<
+                           ISCSI_TMF_REQUEST_TYPE_SHIFT));
+       nopout_wqe->ttt = ttt;
+       nopout_wqe->flags = 0;
+       if (!unsol)
+               nopout_wqe->flags = ISCSI_NOP_OUT_REQUEST_LOCAL_COMPLETION;
+       else if (nopout_hdr->itt == RESERVED_ITT)
+               nopout_wqe->flags = ISCSI_NOP_OUT_REQUEST_LOCAL_COMPLETION;
+
+       nopout_wqe->cmd_sn = be32_to_cpu(nopout_hdr->cmdsn);
+       nopout_wqe->data_length = data_len;
+       if (data_len) {
+               /* handle payload data, not required in first release */
+               printk(KERN_ALERT "NOPOUT: WARNING!! payload len != 0\n");
+       } else {
+               nopout_wqe->bd_list_addr_lo = (u32)
+                                       bnx2i_conn->hba->mp_bd_dma;
+               nopout_wqe->bd_list_addr_hi =
+                       (u32) ((u64) bnx2i_conn->hba->mp_bd_dma >> 32);
+               nopout_wqe->num_bds = 1;
+       }
+       nopout_wqe->cq_index = 0; /* CQ# used for completion, 5771x only */
+
+       bnx2i_ring_dbell_update_sq_params(bnx2i_conn, 1);
+       return 0;
+}
+
+
+/**
+ * bnx2i_send_iscsi_logout - post iSCSI logout request WQE to hardware
+ * @conn:      iscsi connection
+ * @cmd:       driver command structure which is requesting
+ *             a WQE to sent to chip for further processing
+ *
+ * prepare and post logout request WQE to CNIC firmware
+ */
+int bnx2i_send_iscsi_logout(struct bnx2i_conn *bnx2i_conn,
+                           struct iscsi_task *task)
+{
+       struct bnx2i_cmd *bnx2i_cmd;
+       struct bnx2i_logout_request *logout_wqe;
+       struct iscsi_logout *logout_hdr;
+
+       bnx2i_cmd = (struct bnx2i_cmd *)task->dd_data;
+       logout_hdr = (struct iscsi_logout *)task->hdr;
+
+       logout_wqe = (struct bnx2i_logout_request *)
+                                               bnx2i_conn->ep->qp.sq_prod_qe;
+       memset(logout_wqe, 0x00, sizeof(struct bnx2i_logout_request));
+
+       logout_wqe->op_code = logout_hdr->opcode;
+       logout_wqe->cmd_sn = be32_to_cpu(logout_hdr->cmdsn);
+       logout_wqe->op_attr =
+                       logout_hdr->flags | ISCSI_LOGOUT_REQUEST_ALWAYS_ONE;
+       logout_wqe->itt = ((u16)task->itt |
+                          (ISCSI_TASK_TYPE_MPATH <<
+                           ISCSI_LOGOUT_REQUEST_TYPE_SHIFT));
+       logout_wqe->data_length = 0;
+       logout_wqe->cid = 0;
+
+       logout_wqe->bd_list_addr_lo = (u32) bnx2i_conn->hba->mp_bd_dma;
+       logout_wqe->bd_list_addr_hi = (u32)
+                               ((u64) bnx2i_conn->hba->mp_bd_dma >> 32);
+       logout_wqe->num_bds = 1;
+       logout_wqe->cq_index = 0; /* CQ# used for completion, 5771x only */
+
+       bnx2i_ring_dbell_update_sq_params(bnx2i_conn, 1);
+       return 0;
+}
+
+
+/**
+ * bnx2i_update_iscsi_conn - post iSCSI logout request WQE to hardware
+ * @conn:      iscsi connection which requires iscsi parameter update
+ *
+ * sends down iSCSI Conn Update request to move iSCSI conn to FFP
+ */
+void bnx2i_update_iscsi_conn(struct iscsi_conn *conn)
+{
+       struct bnx2i_conn *bnx2i_conn = conn->dd_data;
+       struct bnx2i_hba *hba = bnx2i_conn->hba;
+       struct kwqe *kwqe_arr[2];
+       struct iscsi_kwqe_conn_update *update_wqe;
+       struct iscsi_kwqe_conn_update conn_update_kwqe;
+
+       update_wqe = &conn_update_kwqe;
+
+       update_wqe->hdr.op_code = ISCSI_KWQE_OPCODE_UPDATE_CONN;
+       update_wqe->hdr.flags =
+               (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT);
+
+       /* 5771x requires conn context id to be passed as is */
+       if (test_bit(BNX2I_NX2_DEV_57710, &bnx2i_conn->ep->hba->cnic_dev_type))
+               update_wqe->context_id = bnx2i_conn->ep->ep_cid;
+       else
+               update_wqe->context_id = (bnx2i_conn->ep->ep_cid >> 7);
+       update_wqe->conn_flags = 0;
+       if (conn->hdrdgst_en)
+               update_wqe->conn_flags |= ISCSI_KWQE_CONN_UPDATE_HEADER_DIGEST;
+       if (conn->datadgst_en)
+               update_wqe->conn_flags |= ISCSI_KWQE_CONN_UPDATE_DATA_DIGEST;
+       if (conn->session->initial_r2t_en)
+               update_wqe->conn_flags |= ISCSI_KWQE_CONN_UPDATE_INITIAL_R2T;
+       if (conn->session->imm_data_en)
+               update_wqe->conn_flags |= ISCSI_KWQE_CONN_UPDATE_IMMEDIATE_DATA;
+
+       update_wqe->max_send_pdu_length = conn->max_xmit_dlength;
+       update_wqe->max_recv_pdu_length = conn->max_recv_dlength;
+       update_wqe->first_burst_length = conn->session->first_burst;
+       update_wqe->max_burst_length = conn->session->max_burst;
+       update_wqe->exp_stat_sn = conn->exp_statsn;
+       update_wqe->max_outstanding_r2ts = conn->session->max_r2t;
+       update_wqe->session_error_recovery_level = conn->session->erl;
+       iscsi_conn_printk(KERN_ALERT, conn,
+                         "bnx2i: conn update - MBL 0x%x FBL 0x%x"
+                         "MRDSL_I 0x%x MRDSL_T 0x%x \n",
+                         update_wqe->max_burst_length,
+                         update_wqe->first_burst_length,
+                         update_wqe->max_recv_pdu_length,
+                         update_wqe->max_send_pdu_length);
+
+       kwqe_arr[0] = (struct kwqe *) update_wqe;
+       if (hba->cnic && hba->cnic->submit_kwqes)
+               hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, 1);
+}
+
+
+/**
+ * bnx2i_ep_ofld_timer - post iSCSI logout request WQE to hardware
+ * @data:      endpoint (transport handle) structure pointer
+ *
+ * routine to handle connection offload/destroy request timeout
+ */
+void bnx2i_ep_ofld_timer(unsigned long data)
+{
+       struct bnx2i_endpoint *ep = (struct bnx2i_endpoint *) data;
+
+       if (ep->state == EP_STATE_OFLD_START) {
+               printk(KERN_ALERT "ofld_timer: CONN_OFLD timeout\n");
+               ep->state = EP_STATE_OFLD_FAILED;
+       } else if (ep->state == EP_STATE_DISCONN_START) {
+               printk(KERN_ALERT "ofld_timer: CONN_DISCON timeout\n");
+               ep->state = EP_STATE_DISCONN_TIMEDOUT;
+       } else if (ep->state == EP_STATE_CLEANUP_START) {
+               printk(KERN_ALERT "ofld_timer: CONN_CLEANUP timeout\n");
+               ep->state = EP_STATE_CLEANUP_FAILED;
+       }
+
+       wake_up_interruptible(&ep->ofld_wait);
+}
+
+
+static int bnx2i_power_of2(u32 val)
+{
+       u32 power = 0;
+       if (val & (val - 1))
+               return power;
+       val--;
+       while (val) {
+               val = val >> 1;
+               power++;
+       }
+       return power;
+}
+
+
+/**
+ * bnx2i_send_cmd_cleanup_req - send iscsi cmd context clean-up request
+ * @hba:       adapter structure pointer
+ * @cmd:       driver command structure which is requesting
+ *             a WQE to sent to chip for further processing
+ *
+ * prepares and posts CONN_OFLD_REQ1/2 KWQE
+ */
+void bnx2i_send_cmd_cleanup_req(struct bnx2i_hba *hba, struct bnx2i_cmd *cmd)
+{
+       struct bnx2i_cleanup_request *cmd_cleanup;
+
+       cmd_cleanup =
+               (struct bnx2i_cleanup_request *)cmd->conn->ep->qp.sq_prod_qe;
+       memset(cmd_cleanup, 0x00, sizeof(struct bnx2i_cleanup_request));
+
+       cmd_cleanup->op_code = ISCSI_OPCODE_CLEANUP_REQUEST;
+       cmd_cleanup->itt = cmd->req.itt;
+       cmd_cleanup->cq_index = 0; /* CQ# used for completion, 5771x only */
+
+       bnx2i_ring_dbell_update_sq_params(cmd->conn, 1);
+}
+
+
+/**
+ * bnx2i_send_conn_destroy - initiates iscsi connection teardown process
+ * @hba:       adapter structure pointer
+ * @ep:                endpoint (transport indentifier) structure
+ *
+ * this routine prepares and posts CONN_OFLD_REQ1/2 KWQE to initiate
+ *     iscsi connection context clean-up process
+ */
+void bnx2i_send_conn_destroy(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep)
+{
+       struct kwqe *kwqe_arr[2];
+       struct iscsi_kwqe_conn_destroy conn_cleanup;
+
+       memset(&conn_cleanup, 0x00, sizeof(struct iscsi_kwqe_conn_destroy));
+
+       conn_cleanup.hdr.op_code = ISCSI_KWQE_OPCODE_DESTROY_CONN;
+       conn_cleanup.hdr.flags =
+               (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT);
+       /* 5771x requires conn context id to be passed as is */
+       if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type))
+               conn_cleanup.context_id = ep->ep_cid;
+       else
+               conn_cleanup.context_id = (ep->ep_cid >> 7);
+
+       conn_cleanup.reserved0 = (u16)ep->ep_iscsi_cid;
+
+       kwqe_arr[0] = (struct kwqe *) &conn_cleanup;
+       if (hba->cnic && hba->cnic->submit_kwqes)
+               hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, 1);
+}
+
+
+/**
+ * bnx2i_570x_send_conn_ofld_req - initiates iscsi conn context setup process
+ * @hba:               adapter structure pointer
+ * @ep:                endpoint (transport indentifier) structure
+ *
+ * 5706/5708/5709 specific - prepares and posts CONN_OFLD_REQ1/2 KWQE
+ */
+static void bnx2i_570x_send_conn_ofld_req(struct bnx2i_hba *hba,
+                                         struct bnx2i_endpoint *ep)
+{
+       struct kwqe *kwqe_arr[2];
+       struct iscsi_kwqe_conn_offload1 ofld_req1;
+       struct iscsi_kwqe_conn_offload2 ofld_req2;
+       dma_addr_t dma_addr;
+       int num_kwqes = 2;
+       u32 *ptbl;
+
+       ofld_req1.hdr.op_code = ISCSI_KWQE_OPCODE_OFFLOAD_CONN1;
+       ofld_req1.hdr.flags =
+               (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT);
+
+       ofld_req1.iscsi_conn_id = (u16) ep->ep_iscsi_cid;
+
+       dma_addr = ep->qp.sq_pgtbl_phys;
+       ofld_req1.sq_page_table_addr_lo = (u32) dma_addr;
+       ofld_req1.sq_page_table_addr_hi = (u32) ((u64) dma_addr >> 32);
+
+       dma_addr = ep->qp.cq_pgtbl_phys;
+       ofld_req1.cq_page_table_addr_lo = (u32) dma_addr;
+       ofld_req1.cq_page_table_addr_hi = (u32) ((u64) dma_addr >> 32);
+
+       ofld_req2.hdr.op_code = ISCSI_KWQE_OPCODE_OFFLOAD_CONN2;
+       ofld_req2.hdr.flags =
+               (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT);
+
+       dma_addr = ep->qp.rq_pgtbl_phys;
+       ofld_req2.rq_page_table_addr_lo = (u32) dma_addr;
+       ofld_req2.rq_page_table_addr_hi = (u32) ((u64) dma_addr >> 32);
+
+       ptbl = (u32 *) ep->qp.sq_pgtbl_virt;
+
+       ofld_req2.sq_first_pte.hi = *ptbl++;
+       ofld_req2.sq_first_pte.lo = *ptbl;
+
+       ptbl = (u32 *) ep->qp.cq_pgtbl_virt;
+       ofld_req2.cq_first_pte.hi = *ptbl++;
+       ofld_req2.cq_first_pte.lo = *ptbl;
+
+       kwqe_arr[0] = (struct kwqe *) &ofld_req1;
+       kwqe_arr[1] = (struct kwqe *) &ofld_req2;
+       ofld_req2.num_additional_wqes = 0;
+
+       if (hba->cnic && hba->cnic->submit_kwqes)
+               hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, num_kwqes);
+}
+
+
+/**
+ * bnx2i_5771x_send_conn_ofld_req - initiates iscsi connection context creation
+ * @hba:               adapter structure pointer
+ * @ep:                endpoint (transport indentifier) structure
+ *
+ * 57710 specific - prepares and posts CONN_OFLD_REQ1/2 KWQE
+ */
+static void bnx2i_5771x_send_conn_ofld_req(struct bnx2i_hba *hba,
+                                          struct bnx2i_endpoint *ep)
+{
+       struct kwqe *kwqe_arr[5];
+       struct iscsi_kwqe_conn_offload1 ofld_req1;
+       struct iscsi_kwqe_conn_offload2 ofld_req2;
+       struct iscsi_kwqe_conn_offload3 ofld_req3[1];
+       dma_addr_t dma_addr;
+       int num_kwqes = 2;
+       u32 *ptbl;
+
+       ofld_req1.hdr.op_code = ISCSI_KWQE_OPCODE_OFFLOAD_CONN1;
+       ofld_req1.hdr.flags =
+               (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT);
+
+       ofld_req1.iscsi_conn_id = (u16) ep->ep_iscsi_cid;
+
+       dma_addr = ep->qp.sq_pgtbl_phys + ISCSI_SQ_DB_SIZE;
+       ofld_req1.sq_page_table_addr_lo = (u32) dma_addr;
+       ofld_req1.sq_page_table_addr_hi = (u32) ((u64) dma_addr >> 32);
+
+       dma_addr = ep->qp.cq_pgtbl_phys + ISCSI_CQ_DB_SIZE;
+       ofld_req1.cq_page_table_addr_lo = (u32) dma_addr;
+       ofld_req1.cq_page_table_addr_hi = (u32) ((u64) dma_addr >> 32);
+
+       ofld_req2.hdr.op_code = ISCSI_KWQE_OPCODE_OFFLOAD_CONN2;
+       ofld_req2.hdr.flags =
+               (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT);
+
+       dma_addr = ep->qp.rq_pgtbl_phys + ISCSI_RQ_DB_SIZE;
+       ofld_req2.rq_page_table_addr_lo = (u32) dma_addr;
+       ofld_req2.rq_page_table_addr_hi = (u32) ((u64) dma_addr >> 32);
+
+       ptbl = (u32 *)((u8 *)ep->qp.sq_pgtbl_virt + ISCSI_SQ_DB_SIZE);
+       ofld_req2.sq_first_pte.hi = *ptbl++;
+       ofld_req2.sq_first_pte.lo = *ptbl;
+
+       ptbl = (u32 *)((u8 *)ep->qp.cq_pgtbl_virt + ISCSI_CQ_DB_SIZE);
+       ofld_req2.cq_first_pte.hi = *ptbl++;
+       ofld_req2.cq_first_pte.lo = *ptbl;
+
+       kwqe_arr[0] = (struct kwqe *) &ofld_req1;
+       kwqe_arr[1] = (struct kwqe *) &ofld_req2;
+
+       ofld_req2.num_additional_wqes = 1;
+       memset(ofld_req3, 0x00, sizeof(ofld_req3[0]));
+       ptbl = (u32 *)((u8 *)ep->qp.rq_pgtbl_virt + ISCSI_RQ_DB_SIZE);
+       ofld_req3[0].qp_first_pte[0].hi = *ptbl++;
+       ofld_req3[0].qp_first_pte[0].lo = *ptbl;
+
+       kwqe_arr[2] = (struct kwqe *) ofld_req3;
+       /* need if we decide to go with multiple KCQE's per conn */
+       num_kwqes += 1;
+
+       if (hba->cnic && hba->cnic->submit_kwqes)
+               hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, num_kwqes);
+}
+
+/**
+ * bnx2i_send_conn_ofld_req - initiates iscsi connection context setup process
+ *
+ * @hba:               adapter structure pointer
+ * @ep:                endpoint (transport indentifier) structure
+ *
+ * this routine prepares and posts CONN_OFLD_REQ1/2 KWQE
+ */
+void bnx2i_send_conn_ofld_req(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep)
+{
+       if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type))
+               bnx2i_5771x_send_conn_ofld_req(hba, ep);
+       else
+               bnx2i_570x_send_conn_ofld_req(hba, ep);
+}
+
+
+/**
+ * setup_qp_page_tables - iscsi QP page table setup function
+ * @ep:                endpoint (transport indentifier) structure
+ *
+ * Sets up page tables for SQ/RQ/CQ, 1G/sec (5706/5708/5709) devices requires
+ *     64-bit address in big endian format. Whereas 10G/sec (57710) requires
+ *     PT in little endian format
+ */
+static void setup_qp_page_tables(struct bnx2i_endpoint *ep)
+{
+       int num_pages;
+       u32 *ptbl;
+       dma_addr_t page;
+       int cnic_dev_10g;
+
+       if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type))
+               cnic_dev_10g = 1;
+       else
+               cnic_dev_10g = 0;
+
+       /* SQ page table */
+       memset(ep->qp.sq_pgtbl_virt, 0, ep->qp.sq_pgtbl_size);
+       num_pages = ep->qp.sq_mem_size / PAGE_SIZE;
+       page = ep->qp.sq_phys;
+
+       if (cnic_dev_10g)
+               ptbl = (u32 *)((u8 *)ep->qp.sq_pgtbl_virt + ISCSI_SQ_DB_SIZE);
+       else
+               ptbl = (u32 *) ep->qp.sq_pgtbl_virt;
+       while (num_pages--) {
+               if (cnic_dev_10g) {
+                       /* PTE is written in little endian format for 57710 */
+                       *ptbl = (u32) page;
+                       ptbl++;
+                       *ptbl = (u32) ((u64) page >> 32);
+                       ptbl++;
+                       page += PAGE_SIZE;
+               } else {
+                       /* PTE is written in big endian format for
+                        * 5706/5708/5709 devices */
+                       *ptbl = (u32) ((u64) page >> 32);
+                       ptbl++;
+                       *ptbl = (u32) page;
+                       ptbl++;
+                       page += PAGE_SIZE;
+               }
+       }
+
+       /* RQ page table */
+       memset(ep->qp.rq_pgtbl_virt, 0, ep->qp.rq_pgtbl_size);
+       num_pages = ep->qp.rq_mem_size / PAGE_SIZE;
+       page = ep->qp.rq_phys;
+
+       if (cnic_dev_10g)
+               ptbl = (u32 *)((u8 *)ep->qp.rq_pgtbl_virt + ISCSI_RQ_DB_SIZE);
+       else
+               ptbl = (u32 *) ep->qp.rq_pgtbl_virt;
+       while (num_pages--) {
+               if (cnic_dev_10g) {
+                       /* PTE is written in little endian format for 57710 */
+                       *ptbl = (u32) page;
+                       ptbl++;
+                       *ptbl = (u32) ((u64) page >> 32);
+                       ptbl++;
+                       page += PAGE_SIZE;
+               } else {
+                       /* PTE is written in big endian format for
+                        * 5706/5708/5709 devices */
+                       *ptbl = (u32) ((u64) page >> 32);
+                       ptbl++;
+                       *ptbl = (u32) page;
+                       ptbl++;
+                       page += PAGE_SIZE;
+               }
+       }
+
+       /* CQ page table */
+       memset(ep->qp.cq_pgtbl_virt, 0, ep->qp.cq_pgtbl_size);
+       num_pages = ep->qp.cq_mem_size / PAGE_SIZE;
+       page = ep->qp.cq_phys;
+
+       if (cnic_dev_10g)
+               ptbl = (u32 *)((u8 *)ep->qp.cq_pgtbl_virt + ISCSI_CQ_DB_SIZE);
+       else
+               ptbl = (u32 *) ep->qp.cq_pgtbl_virt;
+       while (num_pages--) {
+               if (cnic_dev_10g) {
+                       /* PTE is written in little endian format for 57710 */
+                       *ptbl = (u32) page;
+                       ptbl++;
+                       *ptbl = (u32) ((u64) page >> 32);
+                       ptbl++;
+                       page += PAGE_SIZE;
+               } else {
+                       /* PTE is written in big endian format for
+                        * 5706/5708/5709 devices */
+                       *ptbl = (u32) ((u64) page >> 32);
+                       ptbl++;
+                       *ptbl = (u32) page;
+                       ptbl++;
+                       page += PAGE_SIZE;
+               }
+       }
+}
+
+
+/**
+ * bnx2i_alloc_qp_resc - allocates required resources for QP.
+ * @hba:       adapter structure pointer
+ * @ep:                endpoint (transport indentifier) structure
+ *
+ * Allocate QP (transport layer for iSCSI connection) resources, DMA'able
+ *     memory for SQ/RQ/CQ and page tables. EP structure elements such
+ *     as producer/consumer indexes/pointers, queue sizes and page table
+ *     contents are setup
+ */
+int bnx2i_alloc_qp_resc(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep)
+{
+       struct bnx2i_5771x_cq_db *cq_db;
+
+       ep->hba = hba;
+       ep->conn = NULL;
+       ep->ep_cid = ep->ep_iscsi_cid = ep->ep_pg_cid = 0;
+
+       /* Allocate page table memory for SQ which is page aligned */
+       ep->qp.sq_mem_size = hba->max_sqes * BNX2I_SQ_WQE_SIZE;
+       ep->qp.sq_mem_size =
+               (ep->qp.sq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+       ep->qp.sq_pgtbl_size =
+               (ep->qp.sq_mem_size / PAGE_SIZE) * sizeof(void *);
+       ep->qp.sq_pgtbl_size =
+               (ep->qp.sq_pgtbl_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+
+       ep->qp.sq_pgtbl_virt =
+               dma_alloc_coherent(&hba->pcidev->dev, ep->qp.sq_pgtbl_size,
+                                  &ep->qp.sq_pgtbl_phys, GFP_KERNEL);
+       if (!ep->qp.sq_pgtbl_virt) {
+               printk(KERN_ALERT "bnx2i: unable to alloc SQ PT mem (%d)\n",
+                                 ep->qp.sq_pgtbl_size);
+               goto mem_alloc_err;
+       }
+
+       /* Allocate memory area for actual SQ element */
+       ep->qp.sq_virt =
+               dma_alloc_coherent(&hba->pcidev->dev, ep->qp.sq_mem_size,
+                                  &ep->qp.sq_phys, GFP_KERNEL);
+       if (!ep->qp.sq_virt) {
+               printk(KERN_ALERT "bnx2i: unable to alloc SQ BD memory %d\n",
+                                 ep->qp.sq_mem_size);
+               goto mem_alloc_err;
+       }
+
+       memset(ep->qp.sq_virt, 0x00, ep->qp.sq_mem_size);
+       ep->qp.sq_first_qe = ep->qp.sq_virt;
+       ep->qp.sq_prod_qe = ep->qp.sq_first_qe;
+       ep->qp.sq_cons_qe = ep->qp.sq_first_qe;
+       ep->qp.sq_last_qe = &ep->qp.sq_first_qe[hba->max_sqes - 1];
+       ep->qp.sq_prod_idx = 0;
+       ep->qp.sq_cons_idx = 0;
+       ep->qp.sqe_left = hba->max_sqes;
+
+       /* Allocate page table memory for CQ which is page aligned */
+       ep->qp.cq_mem_size = hba->max_cqes * BNX2I_CQE_SIZE;
+       ep->qp.cq_mem_size =
+               (ep->qp.cq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+       ep->qp.cq_pgtbl_size =
+               (ep->qp.cq_mem_size / PAGE_SIZE) * sizeof(void *);
+       ep->qp.cq_pgtbl_size =
+               (ep->qp.cq_pgtbl_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+
+       ep->qp.cq_pgtbl_virt =
+               dma_alloc_coherent(&hba->pcidev->dev, ep->qp.cq_pgtbl_size,
+                                  &ep->qp.cq_pgtbl_phys, GFP_KERNEL);
+       if (!ep->qp.cq_pgtbl_virt) {
+               printk(KERN_ALERT "bnx2i: unable to alloc CQ PT memory %d\n",
+                                 ep->qp.cq_pgtbl_size);
+               goto mem_alloc_err;
+       }
+
+       /* Allocate memory area for actual CQ element */
+       ep->qp.cq_virt =
+               dma_alloc_coherent(&hba->pcidev->dev, ep->qp.cq_mem_size,
+                                  &ep->qp.cq_phys, GFP_KERNEL);
+       if (!ep->qp.cq_virt) {
+               printk(KERN_ALERT "bnx2i: unable to alloc CQ BD memory %d\n",
+                                 ep->qp.cq_mem_size);
+               goto mem_alloc_err;
+       }
+       memset(ep->qp.cq_virt, 0x00, ep->qp.cq_mem_size);
+
+       ep->qp.cq_first_qe = ep->qp.cq_virt;
+       ep->qp.cq_prod_qe = ep->qp.cq_first_qe;
+       ep->qp.cq_cons_qe = ep->qp.cq_first_qe;
+       ep->qp.cq_last_qe = &ep->qp.cq_first_qe[hba->max_cqes - 1];
+       ep->qp.cq_prod_idx = 0;
+       ep->qp.cq_cons_idx = 0;
+       ep->qp.cqe_left = hba->max_cqes;
+       ep->qp.cqe_exp_seq_sn = ISCSI_INITIAL_SN;
+       ep->qp.cqe_size = hba->max_cqes;
+
+       /* Invalidate all EQ CQE index, req only for 57710 */
+       cq_db = (struct bnx2i_5771x_cq_db *) ep->qp.cq_pgtbl_virt;
+       memset(cq_db->sqn, 0xFF, sizeof(cq_db->sqn[0]) * BNX2X_MAX_CQS);
+
+       /* Allocate page table memory for RQ which is page aligned */
+       ep->qp.rq_mem_size = hba->max_rqes * BNX2I_RQ_WQE_SIZE;
+       ep->qp.rq_mem_size =
+               (ep->qp.rq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+       ep->qp.rq_pgtbl_size =
+               (ep->qp.rq_mem_size / PAGE_SIZE) * sizeof(void *);
+       ep->qp.rq_pgtbl_size =
+               (ep->qp.rq_pgtbl_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+
+       ep->qp.rq_pgtbl_virt =
+               dma_alloc_coherent(&hba->pcidev->dev, ep->qp.rq_pgtbl_size,
+                                  &ep->qp.rq_pgtbl_phys, GFP_KERNEL);
+       if (!ep->qp.rq_pgtbl_virt) {
+               printk(KERN_ALERT "bnx2i: unable to alloc RQ PT mem %d\n",
+                                 ep->qp.rq_pgtbl_size);
+               goto mem_alloc_err;
+       }
+
+       /* Allocate memory area for actual RQ element */
+       ep->qp.rq_virt =
+               dma_alloc_coherent(&hba->pcidev->dev, ep->qp.rq_mem_size,
+                                  &ep->qp.rq_phys, GFP_KERNEL);
+       if (!ep->qp.rq_virt) {
+               printk(KERN_ALERT "bnx2i: unable to alloc RQ BD memory %d\n",
+                                 ep->qp.rq_mem_size);
+               goto mem_alloc_err;
+       }
+
+       ep->qp.rq_first_qe = ep->qp.rq_virt;
+       ep->qp.rq_prod_qe = ep->qp.rq_first_qe;
+       ep->qp.rq_cons_qe = ep->qp.rq_first_qe;
+       ep->qp.rq_last_qe = &ep->qp.rq_first_qe[hba->max_rqes - 1];
+       ep->qp.rq_prod_idx = 0x8000;
+       ep->qp.rq_cons_idx = 0;
+       ep->qp.rqe_left = hba->max_rqes;
+
+       setup_qp_page_tables(ep);
+
+       return 0;
+
+mem_alloc_err:
+       bnx2i_free_qp_resc(hba, ep);
+       return -ENOMEM;
+}
+
+
+
+/**
+ * bnx2i_free_qp_resc - free memory resources held by QP
+ * @hba:       adapter structure pointer
+ * @ep:        endpoint (transport indentifier) structure
+ *
+ * Free QP resources - SQ/RQ/CQ memory and page tables.
+ */
+void bnx2i_free_qp_resc(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep)
+{
+       if (ep->qp.ctx_base) {
+               iounmap(ep->qp.ctx_base);
+               ep->qp.ctx_base = NULL;
+       }
+       /* Free SQ mem */
+       if (ep->qp.sq_pgtbl_virt) {
+               dma_free_coherent(&hba->pcidev->dev, ep->qp.sq_pgtbl_size,
+                                 ep->qp.sq_pgtbl_virt, ep->qp.sq_pgtbl_phys);
+               ep->qp.sq_pgtbl_virt = NULL;
+               ep->qp.sq_pgtbl_phys = 0;
+       }
+       if (ep->qp.sq_virt) {
+               dma_free_coherent(&hba->pcidev->dev, ep->qp.sq_mem_size,
+                                 ep->qp.sq_virt, ep->qp.sq_phys);
+               ep->qp.sq_virt = NULL;
+               ep->qp.sq_phys = 0;
+       }
+
+       /* Free RQ mem */
+       if (ep->qp.rq_pgtbl_virt) {
+               dma_free_coherent(&hba->pcidev->dev, ep->qp.rq_pgtbl_size,
+                                 ep->qp.rq_pgtbl_virt, ep->qp.rq_pgtbl_phys);
+               ep->qp.rq_pgtbl_virt = NULL;
+               ep->qp.rq_pgtbl_phys = 0;
+       }
+       if (ep->qp.rq_virt) {
+               dma_free_coherent(&hba->pcidev->dev, ep->qp.rq_mem_size,
+                                 ep->qp.rq_virt, ep->qp.rq_phys);
+               ep->qp.rq_virt = NULL;
+               ep->qp.rq_phys = 0;
+       }
+
+       /* Free CQ mem */
+       if (ep->qp.cq_pgtbl_virt) {
+               dma_free_coherent(&hba->pcidev->dev, ep->qp.cq_pgtbl_size,
+                                 ep->qp.cq_pgtbl_virt, ep->qp.cq_pgtbl_phys);
+               ep->qp.cq_pgtbl_virt = NULL;
+               ep->qp.cq_pgtbl_phys = 0;
+       }
+       if (ep->qp.cq_virt) {
+               dma_free_coherent(&hba->pcidev->dev, ep->qp.cq_mem_size,
+                                 ep->qp.cq_virt, ep->qp.cq_phys);
+               ep->qp.cq_virt = NULL;
+               ep->qp.cq_phys = 0;
+       }
+}
+
+
+/**
+ * bnx2i_send_fw_iscsi_init_msg - initiates initial handshake with iscsi f/w
+ * @hba:       adapter structure pointer
+ *
+ * Send down iscsi_init KWQEs which initiates the initial handshake with the f/w
+ *     This results in iSCSi support validation and on-chip context manager
+ *     initialization.  Firmware completes this handshake with a CQE carrying
+ *     the result of iscsi support validation. Parameter carried by
+ *     iscsi init request determines the number of offloaded connection and
+ *     tolerance level for iscsi protocol violation this hba/chip can support
+ */
+int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba)
+{
+       struct kwqe *kwqe_arr[3];
+       struct iscsi_kwqe_init1 iscsi_init;
+       struct iscsi_kwqe_init2 iscsi_init2;
+       int rc = 0;
+       u64 mask64;
+
+       bnx2i_adjust_qp_size(hba);
+
+       iscsi_init.flags =
+               ISCSI_PAGE_SIZE_4K << ISCSI_KWQE_INIT1_PAGE_SIZE_SHIFT;
+       if (en_tcp_dack)
+               iscsi_init.flags |= ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE;
+       iscsi_init.reserved0 = 0;
+       iscsi_init.num_cqs = 1;
+       iscsi_init.hdr.op_code = ISCSI_KWQE_OPCODE_INIT1;
+       iscsi_init.hdr.flags =
+               (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT);
+
+       iscsi_init.dummy_buffer_addr_lo = (u32) hba->dummy_buf_dma;
+       iscsi_init.dummy_buffer_addr_hi =
+               (u32) ((u64) hba->dummy_buf_dma >> 32);
+
+       hba->ctx_ccell_tasks =
+                       ((hba->num_ccell & 0xFFFF) | (hba->max_sqes << 16));
+       iscsi_init.num_ccells_per_conn = hba->num_ccell;
+       iscsi_init.num_tasks_per_conn = hba->max_sqes;
+       iscsi_init.sq_wqes_per_page = PAGE_SIZE / BNX2I_SQ_WQE_SIZE;
+       iscsi_init.sq_num_wqes = hba->max_sqes;
+       iscsi_init.cq_log_wqes_per_page =
+               (u8) bnx2i_power_of2(PAGE_SIZE / BNX2I_CQE_SIZE);
+       iscsi_init.cq_num_wqes = hba->max_cqes;
+       iscsi_init.cq_num_pages = (hba->max_cqes * BNX2I_CQE_SIZE +
+                                  (PAGE_SIZE - 1)) / PAGE_SIZE;
+       iscsi_init.sq_num_pages = (hba->max_sqes * BNX2I_SQ_WQE_SIZE +
+                                  (PAGE_SIZE - 1)) / PAGE_SIZE;
+       iscsi_init.rq_buffer_size = BNX2I_RQ_WQE_SIZE;
+       iscsi_init.rq_num_wqes = hba->max_rqes;
+
+
+       iscsi_init2.hdr.op_code = ISCSI_KWQE_OPCODE_INIT2;
+       iscsi_init2.hdr.flags =
+               (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT);
+       iscsi_init2.max_cq_sqn = hba->max_cqes * 2 + 1;
+       mask64 = 0x0ULL;
+       mask64 |= (
+               /* CISCO MDS */
+               (1UL <<
+                 ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_TTT_NOT_RSRV) |
+               /* HP MSA1510i */
+               (1UL <<
+                 ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_EXP_DATASN) |
+               /* EMC */
+               (1ULL << ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_LUN));
+       if (error_mask1)
+               iscsi_init2.error_bit_map[0] = error_mask1;
+       else
+               iscsi_init2.error_bit_map[0] = (u32) mask64;
+
+       if (error_mask2)
+               iscsi_init2.error_bit_map[1] = error_mask2;
+       else
+               iscsi_init2.error_bit_map[1] = (u32) (mask64 >> 32);
+
+       iscsi_error_mask = mask64;
+
+       kwqe_arr[0] = (struct kwqe *) &iscsi_init;
+       kwqe_arr[1] = (struct kwqe *) &iscsi_init2;
+
+       if (hba->cnic && hba->cnic->submit_kwqes)
+               rc = hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, 2);
+       return rc;
+}
+
+
+/**
+ * bnx2i_process_scsi_cmd_resp - this function handles scsi cmd completion.
+ * @conn:      iscsi connection
+ * @cqe:       pointer to newly DMA'ed CQE entry for processing
+ *
+ * process SCSI CMD Response CQE & complete the request to SCSI-ML
+ */
+static int bnx2i_process_scsi_cmd_resp(struct iscsi_session *session,
+                                      struct bnx2i_conn *bnx2i_conn,
+                                      struct cqe *cqe)
+{
+       struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+       struct bnx2i_cmd_response *resp_cqe;
+       struct bnx2i_cmd *bnx2i_cmd;
+       struct iscsi_task *task;
+       struct iscsi_cmd_rsp *hdr;
+       u32 datalen = 0;
+
+       resp_cqe = (struct bnx2i_cmd_response *)cqe;
+       spin_lock(&session->lock);
+       task = iscsi_itt_to_task(conn,
+                                resp_cqe->itt & ISCSI_CMD_RESPONSE_INDEX);
+       if (!task)
+               goto fail;
+
+       bnx2i_cmd = task->dd_data;
+
+       if (bnx2i_cmd->req.op_attr & ISCSI_CMD_REQUEST_READ) {
+               conn->datain_pdus_cnt +=
+                       resp_cqe->task_stat.read_stat.num_data_outs;
+               conn->rxdata_octets +=
+                       bnx2i_cmd->req.total_data_transfer_length;
+       } else {
+               conn->dataout_pdus_cnt +=
+                       resp_cqe->task_stat.read_stat.num_data_outs;
+               conn->r2t_pdus_cnt +=
+                       resp_cqe->task_stat.read_stat.num_r2ts;
+               conn->txdata_octets +=
+                       bnx2i_cmd->req.total_data_transfer_length;
+       }
+       bnx2i_iscsi_unmap_sg_list(bnx2i_cmd);
+
+       hdr = (struct iscsi_cmd_rsp *)task->hdr;
+       resp_cqe = (struct bnx2i_cmd_response *)cqe;
+       hdr->opcode = resp_cqe->op_code;
+       hdr->max_cmdsn = cpu_to_be32(resp_cqe->max_cmd_sn);
+       hdr->exp_cmdsn = cpu_to_be32(resp_cqe->exp_cmd_sn);
+       hdr->response = resp_cqe->response;
+       hdr->cmd_status = resp_cqe->status;
+       hdr->flags = resp_cqe->response_flags;
+       hdr->residual_count = cpu_to_be32(resp_cqe->residual_count);
+
+       if (resp_cqe->op_code == ISCSI_OP_SCSI_DATA_IN)
+               goto done;
+
+       if (resp_cqe->status == SAM_STAT_CHECK_CONDITION) {
+               datalen = resp_cqe->data_length;
+               if (datalen < 2)
+                       goto done;
+
+               if (datalen > BNX2I_RQ_WQE_SIZE) {
+                       iscsi_conn_printk(KERN_ERR, conn,
+                                         "sense data len %d > RQ sz\n",
+                                         datalen);
+                       datalen = BNX2I_RQ_WQE_SIZE;
+               } else if (datalen > ISCSI_DEF_MAX_RECV_SEG_LEN) {
+                       iscsi_conn_printk(KERN_ERR, conn,
+                                         "sense data len %d > conn data\n",
+                                         datalen);
+                       datalen = ISCSI_DEF_MAX_RECV_SEG_LEN;
+               }
+
+               bnx2i_get_rq_buf(bnx2i_cmd->conn, conn->data, datalen);
+               bnx2i_put_rq_buf(bnx2i_cmd->conn, 1);
+       }
+
+done:
+       __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr,
+                            conn->data, datalen);
+fail:
+       spin_unlock(&session->lock);
+       return 0;
+}
+
+
+/**
+ * bnx2i_process_login_resp - this function handles iscsi login response
+ * @session:           iscsi session pointer
+ * @bnx2i_conn:                iscsi connection pointer
+ * @cqe:               pointer to newly DMA'ed CQE entry for processing
+ *
+ * process Login Response CQE & complete it to open-iscsi user daemon
+ */
+static int bnx2i_process_login_resp(struct iscsi_session *session,
+                                   struct bnx2i_conn *bnx2i_conn,
+                                   struct cqe *cqe)
+{
+       struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+       struct iscsi_task *task;
+       struct bnx2i_login_response *login;
+       struct iscsi_login_rsp *resp_hdr;
+       int pld_len;
+       int pad_len;
+
+       login = (struct bnx2i_login_response *) cqe;
+       spin_lock(&session->lock);
+       task = iscsi_itt_to_task(conn,
+                                login->itt & ISCSI_LOGIN_RESPONSE_INDEX);
+       if (!task)
+               goto done;
+
+       resp_hdr = (struct iscsi_login_rsp *) &bnx2i_conn->gen_pdu.resp_hdr;
+       memset(resp_hdr, 0, sizeof(struct iscsi_hdr));
+       resp_hdr->opcode = login->op_code;
+       resp_hdr->flags = login->response_flags;
+       resp_hdr->max_version = login->version_max;
+       resp_hdr->active_version = login->version_active;;
+       resp_hdr->hlength = 0;
+
+       hton24(resp_hdr->dlength, login->data_length);
+       memcpy(resp_hdr->isid, &login->isid_lo, 6);
+       resp_hdr->tsih = cpu_to_be16(login->tsih);
+       resp_hdr->itt = task->hdr->itt;
+       resp_hdr->statsn = cpu_to_be32(login->stat_sn);
+       resp_hdr->exp_cmdsn = cpu_to_be32(login->exp_cmd_sn);
+       resp_hdr->max_cmdsn = cpu_to_be32(login->max_cmd_sn);
+       resp_hdr->status_class = login->status_class;
+       resp_hdr->status_detail = login->status_detail;
+       pld_len = login->data_length;
+       bnx2i_conn->gen_pdu.resp_wr_ptr =
+                                       bnx2i_conn->gen_pdu.resp_buf + pld_len;
+
+       pad_len = 0;
+       if (pld_len & 0x3)
+               pad_len = 4 - (pld_len % 4);
+
+       if (pad_len) {
+               int i = 0;
+               for (i = 0; i < pad_len; i++) {
+                       bnx2i_conn->gen_pdu.resp_wr_ptr[0] = 0;
+                       bnx2i_conn->gen_pdu.resp_wr_ptr++;
+               }
+       }
+
+       __iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr,
+               bnx2i_conn->gen_pdu.resp_buf,
+               bnx2i_conn->gen_pdu.resp_wr_ptr - bnx2i_conn->gen_pdu.resp_buf);
+done:
+       spin_unlock(&session->lock);
+       return 0;
+}
+
+/**
+ * bnx2i_process_tmf_resp - this function handles iscsi TMF response
+ * @session:           iscsi session pointer
+ * @bnx2i_conn:                iscsi connection pointer
+ * @cqe:               pointer to newly DMA'ed CQE entry for processing
+ *
+ * process iSCSI TMF Response CQE and wake up the driver eh thread.
+ */
+static int bnx2i_process_tmf_resp(struct iscsi_session *session,
+                                 struct bnx2i_conn *bnx2i_conn,
+                                 struct cqe *cqe)
+{
+       struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+       struct iscsi_task *task;
+       struct bnx2i_tmf_response *tmf_cqe;
+       struct iscsi_tm_rsp *resp_hdr;
+
+       tmf_cqe = (struct bnx2i_tmf_response *)cqe;
+       spin_lock(&session->lock);
+       task = iscsi_itt_to_task(conn,
+                                tmf_cqe->itt & ISCSI_TMF_RESPONSE_INDEX);
+       if (!task)
+               goto done;
+
+       resp_hdr = (struct iscsi_tm_rsp *) &bnx2i_conn->gen_pdu.resp_hdr;
+       memset(resp_hdr, 0, sizeof(struct iscsi_hdr));
+       resp_hdr->opcode = tmf_cqe->op_code;
+       resp_hdr->max_cmdsn = cpu_to_be32(tmf_cqe->max_cmd_sn);
+       resp_hdr->exp_cmdsn = cpu_to_be32(tmf_cqe->exp_cmd_sn);
+       resp_hdr->itt = task->hdr->itt;
+       resp_hdr->response = tmf_cqe->response;
+
+       __iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr, NULL, 0);
+done:
+       spin_unlock(&session->lock);
+       return 0;
+}
+
+/**
+ * bnx2i_process_logout_resp - this function handles iscsi logout response
+ * @session:           iscsi session pointer
+ * @bnx2i_conn:                iscsi connection pointer
+ * @cqe:               pointer to newly DMA'ed CQE entry for processing
+ *
+ * process iSCSI Logout Response CQE & make function call to
+ * notify the user daemon.
+ */
+static int bnx2i_process_logout_resp(struct iscsi_session *session,
+                                    struct bnx2i_conn *bnx2i_conn,
+                                    struct cqe *cqe)
+{
+       struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+       struct iscsi_task *task;
+       struct bnx2i_logout_response *logout;
+       struct iscsi_logout_rsp *resp_hdr;
+
+       logout = (struct bnx2i_logout_response *) cqe;
+       spin_lock(&session->lock);
+       task = iscsi_itt_to_task(conn,
+                                logout->itt & ISCSI_LOGOUT_RESPONSE_INDEX);
+       if (!task)
+               goto done;
+
+       resp_hdr = (struct iscsi_logout_rsp *) &bnx2i_conn->gen_pdu.resp_hdr;
+       memset(resp_hdr, 0, sizeof(struct iscsi_hdr));
+       resp_hdr->opcode = logout->op_code;
+       resp_hdr->flags = logout->response;
+       resp_hdr->hlength = 0;
+
+       resp_hdr->itt = task->hdr->itt;
+       resp_hdr->statsn = task->hdr->exp_statsn;
+       resp_hdr->exp_cmdsn = cpu_to_be32(logout->exp_cmd_sn);
+       resp_hdr->max_cmdsn = cpu_to_be32(logout->max_cmd_sn);
+
+       resp_hdr->t2wait = cpu_to_be32(logout->time_to_wait);
+       resp_hdr->t2retain = cpu_to_be32(logout->time_to_retain);
+
+       __iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr, NULL, 0);
+done:
+       spin_unlock(&session->lock);
+       return 0;
+}
+
+/**
+ * bnx2i_process_nopin_local_cmpl - this function handles iscsi nopin CQE
+ * @session:           iscsi session pointer
+ * @bnx2i_conn:                iscsi connection pointer
+ * @cqe:               pointer to newly DMA'ed CQE entry for processing
+ *
+ * process iSCSI NOPIN local completion CQE, frees IIT and command structures
+ */
+static void bnx2i_process_nopin_local_cmpl(struct iscsi_session *session,
+                                          struct bnx2i_conn *bnx2i_conn,
+                                          struct cqe *cqe)
+{
+       struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+       struct bnx2i_nop_in_msg *nop_in;
+       struct iscsi_task *task;
+
+       nop_in = (struct bnx2i_nop_in_msg *)cqe;
+       spin_lock(&session->lock);
+       task = iscsi_itt_to_task(conn,
+                                nop_in->itt & ISCSI_NOP_IN_MSG_INDEX);
+       if (task)
+               iscsi_put_task(task);
+       spin_unlock(&session->lock);
+}
+
+/**
+ * bnx2i_unsol_pdu_adjust_rq - makes adjustments to RQ after unsol pdu is recvd
+ * @conn:      iscsi connection
+ *
+ * Firmware advances RQ producer index for every unsolicited PDU even if
+ *     payload data length is '0'. This function makes corresponding
+ *     adjustments on the driver side to match this f/w behavior
+ */
+static void bnx2i_unsol_pdu_adjust_rq(struct bnx2i_conn *bnx2i_conn)
+{
+       char dummy_rq_data[2];
+       bnx2i_get_rq_buf(bnx2i_conn, dummy_rq_data, 1);
+       bnx2i_put_rq_buf(bnx2i_conn, 1);
+}
+
+
+/**
+ * bnx2i_process_nopin_mesg - this function handles iscsi nopin CQE
+ * @session:           iscsi session pointer
+ * @bnx2i_conn:                iscsi connection pointer
+ * @cqe:               pointer to newly DMA'ed CQE entry for processing
+ *
+ * process iSCSI target's proactive iSCSI NOPIN request
+ */
+static int bnx2i_process_nopin_mesg(struct iscsi_session *session,
+                                    struct bnx2i_conn *bnx2i_conn,
+                                    struct cqe *cqe)
+{
+       struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+       struct iscsi_task *task;
+       struct bnx2i_nop_in_msg *nop_in;
+       struct iscsi_nopin *hdr;
+       u32 itt;
+       int tgt_async_nop = 0;
+
+       nop_in = (struct bnx2i_nop_in_msg *)cqe;
+       itt = nop_in->itt & ISCSI_NOP_IN_MSG_INDEX;
+
+       spin_lock(&session->lock);
+       hdr = (struct iscsi_nopin *)&bnx2i_conn->gen_pdu.resp_hdr;
+       memset(hdr, 0, sizeof(struct iscsi_hdr));
+       hdr->opcode = nop_in->op_code;
+       hdr->max_cmdsn = cpu_to_be32(nop_in->max_cmd_sn);
+       hdr->exp_cmdsn = cpu_to_be32(nop_in->exp_cmd_sn);
+       hdr->ttt = cpu_to_be32(nop_in->ttt);
+
+       if (itt == (u16) RESERVED_ITT) {
+               bnx2i_unsol_pdu_adjust_rq(bnx2i_conn);
+               hdr->itt = RESERVED_ITT;
+               tgt_async_nop = 1;
+               goto done;
+       }
+
+       /* this is a response to one of our nop-outs */
+       task = iscsi_itt_to_task(conn, itt);
+       if (task) {
+               hdr->flags = ISCSI_FLAG_CMD_FINAL;
+               hdr->itt = task->hdr->itt;
+               hdr->ttt = cpu_to_be32(nop_in->ttt);
+               memcpy(hdr->lun, nop_in->lun, 8);
+       }
+done:
+       __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
+       spin_unlock(&session->lock);
+
+       return tgt_async_nop;
+}
+
+
+/**
+ * bnx2i_process_async_mesg - this function handles iscsi async message
+ * @session:           iscsi session pointer
+ * @bnx2i_conn:                iscsi connection pointer
+ * @cqe:               pointer to newly DMA'ed CQE entry for processing
+ *
+ * process iSCSI ASYNC Message
+ */
+static void bnx2i_process_async_mesg(struct iscsi_session *session,
+                                    struct bnx2i_conn *bnx2i_conn,
+                                    struct cqe *cqe)
+{
+       struct bnx2i_async_msg *async_cqe;
+       struct iscsi_async *resp_hdr;
+       u8 async_event;
+
+       bnx2i_unsol_pdu_adjust_rq(bnx2i_conn);
+
+       async_cqe = (struct bnx2i_async_msg *)cqe;
+       async_event = async_cqe->async_event;
+
+       if (async_event == ISCSI_ASYNC_MSG_SCSI_EVENT) {
+               iscsi_conn_printk(KERN_ALERT, bnx2i_conn->cls_conn->dd_data,
+                                 "async: scsi events not supported\n");
+               return;
+       }
+
+       spin_lock(&session->lock);
+       resp_hdr = (struct iscsi_async *) &bnx2i_conn->gen_pdu.resp_hdr;
+       memset(resp_hdr, 0, sizeof(struct iscsi_hdr));
+       resp_hdr->opcode = async_cqe->op_code;
+       resp_hdr->flags = 0x80;
+
+       memcpy(resp_hdr->lun, async_cqe->lun, 8);
+       resp_hdr->exp_cmdsn = cpu_to_be32(async_cqe->exp_cmd_sn);
+       resp_hdr->max_cmdsn = cpu_to_be32(async_cqe->max_cmd_sn);
+
+       resp_hdr->async_event = async_cqe->async_event;
+       resp_hdr->async_vcode = async_cqe->async_vcode;
+
+       resp_hdr->param1 = cpu_to_be16(async_cqe->param1);
+       resp_hdr->param2 = cpu_to_be16(async_cqe->param2);
+       resp_hdr->param3 = cpu_to_be16(async_cqe->param3);
+
+       __iscsi_complete_pdu(bnx2i_conn->cls_conn->dd_data,
+                            (struct iscsi_hdr *)resp_hdr, NULL, 0);
+       spin_unlock(&session->lock);
+}
+
+
+/**
+ * bnx2i_process_reject_mesg - process iscsi reject pdu
+ * @session:           iscsi session pointer
+ * @bnx2i_conn:                iscsi connection pointer
+ * @cqe:               pointer to newly DMA'ed CQE entry for processing
+ *
+ * process iSCSI REJECT message
+ */
+static void bnx2i_process_reject_mesg(struct iscsi_session *session,
+                                     struct bnx2i_conn *bnx2i_conn,
+                                     struct cqe *cqe)
+{
+       struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+       struct bnx2i_reject_msg *reject;
+       struct iscsi_reject *hdr;
+
+       reject = (struct bnx2i_reject_msg *) cqe;
+       if (reject->data_length) {
+               bnx2i_get_rq_buf(bnx2i_conn, conn->data, reject->data_length);
+               bnx2i_put_rq_buf(bnx2i_conn, 1);
+       } else
+               bnx2i_unsol_pdu_adjust_rq(bnx2i_conn);
+
+       spin_lock(&session->lock);
+       hdr = (struct iscsi_reject *) &bnx2i_conn->gen_pdu.resp_hdr;
+       memset(hdr, 0, sizeof(struct iscsi_hdr));
+       hdr->opcode = reject->op_code;
+       hdr->reason = reject->reason;
+       hton24(hdr->dlength, reject->data_length);
+       hdr->max_cmdsn = cpu_to_be32(reject->max_cmd_sn);
+       hdr->exp_cmdsn = cpu_to_be32(reject->exp_cmd_sn);
+       hdr->ffffffff = cpu_to_be32(RESERVED_ITT);
+       __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, conn->data,
+                            reject->data_length);
+       spin_unlock(&session->lock);
+}
+
+/**
+ * bnx2i_process_cmd_cleanup_resp - process scsi command clean-up completion
+ * @session:           iscsi session pointer
+ * @bnx2i_conn:                iscsi connection pointer
+ * @cqe:               pointer to newly DMA'ed CQE entry for processing
+ *
+ * process command cleanup response CQE during conn shutdown or error recovery
+ */
+static void bnx2i_process_cmd_cleanup_resp(struct iscsi_session *session,
+                                          struct bnx2i_conn *bnx2i_conn,
+                                          struct cqe *cqe)
+{
+       struct bnx2i_cleanup_response *cmd_clean_rsp;
+       struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+       struct iscsi_task *task;
+
+       cmd_clean_rsp = (struct bnx2i_cleanup_response *)cqe;
+       spin_lock(&session->lock);
+       task = iscsi_itt_to_task(conn,
+                       cmd_clean_rsp->itt & ISCSI_CLEANUP_RESPONSE_INDEX);
+       if (!task)
+               printk(KERN_ALERT "bnx2i: cmd clean ITT %x not active\n",
+                       cmd_clean_rsp->itt & ISCSI_CLEANUP_RESPONSE_INDEX);
+       spin_unlock(&session->lock);
+       complete(&bnx2i_conn->cmd_cleanup_cmpl);
+}
+
+
+
+/**
+ * bnx2i_process_new_cqes - process newly DMA'ed CQE's
+ * @bnx2i_conn:                iscsi connection
+ *
+ * this function is called by generic KCQ handler to process all pending CQE's
+ */
+static void bnx2i_process_new_cqes(struct bnx2i_conn *bnx2i_conn)
+{
+       struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
+       struct iscsi_session *session = conn->session;
+       struct qp_info *qp = &bnx2i_conn->ep->qp;
+       struct bnx2i_nop_in_msg *nopin;
+       int tgt_async_msg;
+
+       while (1) {
+               nopin = (struct bnx2i_nop_in_msg *) qp->cq_cons_qe;
+               if (nopin->cq_req_sn != qp->cqe_exp_seq_sn)
+                       break;
+
+               if (unlikely(test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx)))
+                       break;
+
+               tgt_async_msg = 0;
+
+               switch (nopin->op_code) {
+               case ISCSI_OP_SCSI_CMD_RSP:
+               case ISCSI_OP_SCSI_DATA_IN:
+                       bnx2i_process_scsi_cmd_resp(session, bnx2i_conn,
+                                                   qp->cq_cons_qe);
+                       break;
+               case ISCSI_OP_LOGIN_RSP:
+                       bnx2i_process_login_resp(session, bnx2i_conn,
+                                                qp->cq_cons_qe);
+                       break;
+               case ISCSI_OP_SCSI_TMFUNC_RSP:
+                       bnx2i_process_tmf_resp(session, bnx2i_conn,
+                                              qp->cq_cons_qe);
+                       break;
+               case ISCSI_OP_LOGOUT_RSP:
+                       bnx2i_process_logout_resp(session, bnx2i_conn,
+                                                 qp->cq_cons_qe);
+                       break;
+               case ISCSI_OP_NOOP_IN:
+                       if (bnx2i_process_nopin_mesg(session, bnx2i_conn,
+                                                    qp->cq_cons_qe))
+                               tgt_async_msg = 1;
+                       break;
+               case ISCSI_OPCODE_NOPOUT_LOCAL_COMPLETION:
+                       bnx2i_process_nopin_local_cmpl(session, bnx2i_conn,
+                                                      qp->cq_cons_qe);
+                       break;
+               case ISCSI_OP_ASYNC_EVENT:
+                       bnx2i_process_async_mesg(session, bnx2i_conn,
+                                                qp->cq_cons_qe);
+                       tgt_async_msg = 1;
+                       break;
+               case ISCSI_OP_REJECT:
+                       bnx2i_process_reject_mesg(session, bnx2i_conn,
+                                                 qp->cq_cons_qe);
+                       break;
+               case ISCSI_OPCODE_CLEANUP_RESPONSE:
+                       bnx2i_process_cmd_cleanup_resp(session, bnx2i_conn,
+                                                      qp->cq_cons_qe);
+                       break;
+               default:
+                       printk(KERN_ALERT "bnx2i: unknown opcode 0x%x\n",
+                                         nopin->op_code);
+               }
+
+               if (!tgt_async_msg)
+                       bnx2i_conn->ep->num_active_cmds--;
+
+               /* clear out in production version only, till beta keep opcode
+                * field intact, will be helpful in debugging (context dump)
+                * nopin->op_code = 0;
+                */
+               qp->cqe_exp_seq_sn++;
+               if (qp->cqe_exp_seq_sn == (qp->cqe_size * 2 + 1))
+                       qp->cqe_exp_seq_sn = ISCSI_INITIAL_SN;
+
+               if (qp->cq_cons_qe == qp->cq_last_qe) {
+                       qp->cq_cons_qe = qp->cq_first_qe;
+                       qp->cq_cons_idx = 0;
+               } else {
+                       qp->cq_cons_qe++;
+                       qp->cq_cons_idx++;
+               }
+       }
+       bnx2i_arm_cq_event_coalescing(bnx2i_conn->ep, CNIC_ARM_CQE);
+}
+
+/**
+ * bnx2i_fastpath_notification - process global event queue (KCQ)
+ * @hba:               adapter structure pointer
+ * @new_cqe_kcqe:      pointer to newly DMA'ed KCQE entry
+ *
+ * Fast path event notification handler, KCQ entry carries context id
+ *     of the connection that has 1 or more pending CQ entries
+ */
+static void bnx2i_fastpath_notification(struct bnx2i_hba *hba,
+                                       struct iscsi_kcqe *new_cqe_kcqe)
+{
+       struct bnx2i_conn *conn;
+       u32 iscsi_cid;
+
+       iscsi_cid = new_cqe_kcqe->iscsi_conn_id;
+       conn = bnx2i_get_conn_from_id(hba, iscsi_cid);
+
+       if (!conn) {
+               printk(KERN_ALERT "cid #%x not valid\n", iscsi_cid);
+               return;
+       }
+       if (!conn->ep) {
+               printk(KERN_ALERT "cid #%x - ep not bound\n", iscsi_cid);
+               return;
+       }
+
+       bnx2i_process_new_cqes(conn);
+}
+
+
+/**
+ * bnx2i_process_update_conn_cmpl - process iscsi conn update completion KCQE
+ * @hba:               adapter structure pointer
+ * @update_kcqe:       kcqe pointer
+ *
+ * CONN_UPDATE completion handler, this completes iSCSI connection FFP migration
+ */
+static void bnx2i_process_update_conn_cmpl(struct bnx2i_hba *hba,
+                                          struct iscsi_kcqe *update_kcqe)
+{
+       struct bnx2i_conn *conn;
+       u32 iscsi_cid;
+
+       iscsi_cid = update_kcqe->iscsi_conn_id;
+       conn = bnx2i_get_conn_from_id(hba, iscsi_cid);
+
+       if (!conn) {
+               printk(KERN_ALERT "conn_update: cid %x not valid\n", iscsi_cid);
+               return;
+       }
+       if (!conn->ep) {
+               printk(KERN_ALERT "cid %x does not have ep bound\n", iscsi_cid);
+               return;
+       }
+
+       if (update_kcqe->completion_status) {
+               printk(KERN_ALERT "request failed cid %x\n", iscsi_cid);
+               conn->ep->state = EP_STATE_ULP_UPDATE_FAILED;
+       } else
+               conn->ep->state = EP_STATE_ULP_UPDATE_COMPL;
+
+       wake_up_interruptible(&conn->ep->ofld_wait);
+}
+
+
+/**
+ * bnx2i_recovery_que_add_conn - add connection to recovery queue
+ * @hba:               adapter structure pointer
+ * @bnx2i_conn:                iscsi connection
+ *
+ * Add connection to recovery queue and schedule adapter eh worker
+ */
+static void bnx2i_recovery_que_add_conn(struct bnx2i_hba *hba,
+                                       struct bnx2i_conn *bnx2i_conn)
+{
+       iscsi_conn_failure(bnx2i_conn->cls_conn->dd_data,
+                          ISCSI_ERR_CONN_FAILED);
+}
+
+
+/**
+ * bnx2i_process_tcp_error - process error notification on a given connection
+ *
+ * @hba:               adapter structure pointer
+ * @tcp_err:           tcp error kcqe pointer
+ *
+ * handles tcp level error notifications from FW.
+ */
+static void bnx2i_process_tcp_error(struct bnx2i_hba *hba,
+                                   struct iscsi_kcqe *tcp_err)
+{
+       struct bnx2i_conn *bnx2i_conn;
+       u32 iscsi_cid;
+
+       iscsi_cid = tcp_err->iscsi_conn_id;
+       bnx2i_conn = bnx2i_get_conn_from_id(hba, iscsi_cid);
+
+       if (!bnx2i_conn) {
+               printk(KERN_ALERT "bnx2i - cid 0x%x not valid\n", iscsi_cid);
+               return;
+       }
+
+       printk(KERN_ALERT "bnx2i - cid 0x%x had TCP errors, error code 0x%x\n",
+                         iscsi_cid, tcp_err->completion_status);
+       bnx2i_recovery_que_add_conn(bnx2i_conn->hba, bnx2i_conn);
+}
+
+
+/**
+ * bnx2i_process_iscsi_error - process error notification on a given connection
+ * @hba:               adapter structure pointer
+ * @iscsi_err:         iscsi error kcqe pointer
+ *
+ * handles iscsi error notifications from the FW. Firmware based in initial
+ *     handshake classifies iscsi protocol / TCP rfc violation into either
+ *     warning or error indications. If indication is of "Error" type, driver
+ *     will initiate session recovery for that connection/session. For
+ *     "Warning" type indication, driver will put out a system log message
+ *     (there will be only one message for each type for the life of the
+ *     session, this is to avoid un-necessarily overloading the system)
+ */
+static void bnx2i_process_iscsi_error(struct bnx2i_hba *hba,
+                                     struct iscsi_kcqe *iscsi_err)
+{
+       struct bnx2i_conn *bnx2i_conn;
+       u32 iscsi_cid;
+       char warn_notice[] = "iscsi_warning";
+       char error_notice[] = "iscsi_error";
+       char additional_notice[64];
+       char *message;
+       int need_recovery;
+       u64 err_mask64;
+
+       iscsi_cid = iscsi_err->iscsi_conn_id;
+       bnx2i_conn = bnx2i_get_conn_from_id(hba, iscsi_cid);
+       if (!bnx2i_conn) {
+               printk(KERN_ALERT "bnx2i - cid 0x%x not valid\n", iscsi_cid);
+               return;
+       }
+
+       err_mask64 = (0x1ULL << iscsi_err->completion_status);
+
+       if (err_mask64 & iscsi_error_mask) {
+               need_recovery = 0;
+               message = warn_notice;
+       } else {
+               need_recovery = 1;
+               message = error_notice;
+       }
+
+       switch (iscsi_err->completion_status) {
+       case ISCSI_KCQE_COMPLETION_STATUS_HDR_DIG_ERR:
+               strcpy(additional_notice, "hdr digest err");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_DATA_DIG_ERR:
+               strcpy(additional_notice, "data digest err");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_OPCODE:
+               strcpy(additional_notice, "wrong opcode rcvd");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_AHS_LEN:
+               strcpy(additional_notice, "AHS len > 0 rcvd");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_ITT:
+               strcpy(additional_notice, "invalid ITT rcvd");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_STATSN:
+               strcpy(additional_notice, "wrong StatSN rcvd");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_EXP_DATASN:
+               strcpy(additional_notice, "wrong DataSN rcvd");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T:
+               strcpy(additional_notice, "pend R2T violation");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_0:
+               strcpy(additional_notice, "ERL0, UO");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_1:
+               strcpy(additional_notice, "ERL0, U1");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_2:
+               strcpy(additional_notice, "ERL0, U2");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_3:
+               strcpy(additional_notice, "ERL0, U3");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_4:
+               strcpy(additional_notice, "ERL0, U4");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_5:
+               strcpy(additional_notice, "ERL0, U5");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_6:
+               strcpy(additional_notice, "ERL0, U6");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REMAIN_RCV_LEN:
+               strcpy(additional_notice, "invalid resi len");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_MAX_RCV_PDU_LEN:
+               strcpy(additional_notice, "MRDSL violation");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_F_BIT_ZERO:
+               strcpy(additional_notice, "F-bit not set");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_TTT_NOT_RSRV:
+               strcpy(additional_notice, "invalid TTT");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DATASN:
+               strcpy(additional_notice, "invalid DataSN");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REMAIN_BURST_LEN:
+               strcpy(additional_notice, "burst len violation");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_BUFFER_OFF:
+               strcpy(additional_notice, "buf offset violation");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_LUN:
+               strcpy(additional_notice, "invalid LUN field");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_R2TSN:
+               strcpy(additional_notice, "invalid R2TSN field");
+               break;
+#define BNX2I_ERR_DESIRED_DATA_TRNS_LEN_0      \
+       ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DESIRED_DATA_TRNS_LEN_0
+       case BNX2I_ERR_DESIRED_DATA_TRNS_LEN_0:
+               strcpy(additional_notice, "invalid cmd len1");
+               break;
+#define BNX2I_ERR_DESIRED_DATA_TRNS_LEN_1      \
+       ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DESIRED_DATA_TRNS_LEN_1
+       case BNX2I_ERR_DESIRED_DATA_TRNS_LEN_1:
+               strcpy(additional_notice, "invalid cmd len2");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T_EXCEED:
+               strcpy(additional_notice,
+                      "pend r2t exceeds MaxOutstandingR2T value");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_TTT_IS_RSRV:
+               strcpy(additional_notice, "TTT is rsvd");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_MAX_BURST_LEN:
+               strcpy(additional_notice, "MBL violation");
+               break;
+#define BNX2I_ERR_DATA_SEG_LEN_NOT_ZERO        \
+       ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DATA_SEG_LEN_NOT_ZERO
+       case BNX2I_ERR_DATA_SEG_LEN_NOT_ZERO:
+               strcpy(additional_notice, "data seg len != 0");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REJECT_PDU_LEN:
+               strcpy(additional_notice, "reject pdu len error");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_ASYNC_PDU_LEN:
+               strcpy(additional_notice, "async pdu len error");
+               break;
+       case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_NOPIN_PDU_LEN:
+               strcpy(additional_notice, "nopin pdu len error");
+               break;
+#define BNX2_ERR_PEND_R2T_IN_CLEANUP                   \
+       ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T_IN_CLEANUP
+       case BNX2_ERR_PEND_R2T_IN_CLEANUP:
+               strcpy(additional_notice, "pend r2t in cleanup");
+               break;
+
+       case ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_IP_FRAGMENT:
+               strcpy(additional_notice, "IP fragments rcvd");
+               break;
+       case ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_IP_OPTIONS:
+               strcpy(additional_notice, "IP options error");
+               break;
+       case ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_URGENT_FLAG:
+               strcpy(additional_notice, "urgent flag error");
+               break;
+       default:
+               printk(KERN_ALERT "iscsi_err - unknown err %x\n",
+                                 iscsi_err->completion_status);
+       }
+
+       if (need_recovery) {
+               iscsi_conn_printk(KERN_ALERT,
+                                 bnx2i_conn->cls_conn->dd_data,
+                                 "bnx2i: %s - %s\n",
+                                 message, additional_notice);
+
+               iscsi_conn_printk(KERN_ALERT,
+                                 bnx2i_conn->cls_conn->dd_data,
+                                 "conn_err - hostno %d conn %p, "
+                                 "iscsi_cid %x cid %x\n",
+                                 bnx2i_conn->hba->shost->host_no,
+                                 bnx2i_conn, bnx2i_conn->ep->ep_iscsi_cid,
+                                 bnx2i_conn->ep->ep_cid);
+               bnx2i_recovery_que_add_conn(bnx2i_conn->hba, bnx2i_conn);
+       } else
+               if (!test_and_set_bit(iscsi_err->completion_status,
+                                     (void *) &bnx2i_conn->violation_notified))
+                       iscsi_conn_printk(KERN_ALERT,
+                                         bnx2i_conn->cls_conn->dd_data,
+                                         "bnx2i: %s - %s\n",
+                                         message, additional_notice);
+}
+
+
+/**
+ * bnx2i_process_conn_destroy_cmpl - process iscsi conn destroy completion
+ * @hba:               adapter structure pointer
+ * @conn_destroy:      conn destroy kcqe pointer
+ *
+ * handles connection destroy completion request.
+ */
+static void bnx2i_process_conn_destroy_cmpl(struct bnx2i_hba *hba,
+                                           struct iscsi_kcqe *conn_destroy)
+{
+       struct bnx2i_endpoint *ep;
+
+       ep = bnx2i_find_ep_in_destroy_list(hba, conn_destroy->iscsi_conn_id);
+       if (!ep) {
+               printk(KERN_ALERT "bnx2i_conn_destroy_cmpl: no pending "
+                                 "offload request, unexpected complection\n");
+               return;
+       }
+
+       if (hba != ep->hba) {
+               printk(KERN_ALERT "conn destroy- error hba mis-match\n");
+               return;
+       }
+
+       if (conn_destroy->completion_status) {
+               printk(KERN_ALERT "conn_destroy_cmpl: op failed\n");
+               ep->state = EP_STATE_CLEANUP_FAILED;
+       } else
+               ep->state = EP_STATE_CLEANUP_CMPL;
+       wake_up_interruptible(&ep->ofld_wait);
+}
+
+
+/**
+ * bnx2i_process_ofld_cmpl - process initial iscsi conn offload completion
+ * @hba:               adapter structure pointer
+ * @ofld_kcqe:         conn offload kcqe pointer
+ *
+ * handles initial connection offload completion, ep_connect() thread is
+ *     woken-up to continue with LLP connect process
+ */
+static void bnx2i_process_ofld_cmpl(struct bnx2i_hba *hba,
+                                   struct iscsi_kcqe *ofld_kcqe)
+{
+       u32 cid_addr;
+       struct bnx2i_endpoint *ep;
+       u32 cid_num;
+
+       ep = bnx2i_find_ep_in_ofld_list(hba, ofld_kcqe->iscsi_conn_id);
+       if (!ep) {
+               printk(KERN_ALERT "ofld_cmpl: no pend offload request\n");
+               return;
+       }
+
+       if (hba != ep->hba) {
+               printk(KERN_ALERT "ofld_cmpl: error hba mis-match\n");
+               return;
+       }
+
+       if (ofld_kcqe->completion_status) {
+               if (ofld_kcqe->completion_status ==
+                   ISCSI_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAILURE)
+                       printk(KERN_ALERT "bnx2i: unable to allocate"
+                                         " iSCSI context resources\n");
+               ep->state = EP_STATE_OFLD_FAILED;
+       } else {
+               ep->state = EP_STATE_OFLD_COMPL;
+               cid_addr = ofld_kcqe->iscsi_conn_context_id;
+               cid_num = bnx2i_get_cid_num(ep);
+               ep->ep_cid = cid_addr;
+               ep->qp.ctx_base = NULL;
+       }
+       wake_up_interruptible(&ep->ofld_wait);
+}
+
+/**
+ * bnx2i_indicate_kcqe - process iscsi conn update completion KCQE
+ * @hba:               adapter structure pointer
+ * @update_kcqe:       kcqe pointer
+ *
+ * Generic KCQ event handler/dispatcher
+ */
+static void bnx2i_indicate_kcqe(void *context, struct kcqe *kcqe[],
+                               u32 num_cqe)
+{
+       struct bnx2i_hba *hba = context;
+       int i = 0;
+       struct iscsi_kcqe *ikcqe = NULL;
+
+       while (i < num_cqe) {
+               ikcqe = (struct iscsi_kcqe *) kcqe[i++];
+
+               if (ikcqe->op_code ==
+                   ISCSI_KCQE_OPCODE_CQ_EVENT_NOTIFICATION)
+                       bnx2i_fastpath_notification(hba, ikcqe);
+               else if (ikcqe->op_code == ISCSI_KCQE_OPCODE_OFFLOAD_CONN)
+                       bnx2i_process_ofld_cmpl(hba, ikcqe);
+               else if (ikcqe->op_code == ISCSI_KCQE_OPCODE_UPDATE_CONN)
+                       bnx2i_process_update_conn_cmpl(hba, ikcqe);
+               else if (ikcqe->op_code == ISCSI_KCQE_OPCODE_INIT) {
+                       if (ikcqe->completion_status !=
+                           ISCSI_KCQE_COMPLETION_STATUS_SUCCESS)
+                               bnx2i_iscsi_license_error(hba, ikcqe->\
+                                                         completion_status);
+                       else {
+                               set_bit(ADAPTER_STATE_UP, &hba->adapter_state);
+                               bnx2i_get_link_state(hba);
+                               printk(KERN_INFO "bnx2i [%.2x:%.2x.%.2x]: "
+                                                "ISCSI_INIT passed\n",
+                                                (u8)hba->pcidev->bus->number,
+                                                hba->pci_devno,
+                                                (u8)hba->pci_func);
+
+
+                       }
+               } else if (ikcqe->op_code == ISCSI_KCQE_OPCODE_DESTROY_CONN)
+                       bnx2i_process_conn_destroy_cmpl(hba, ikcqe);
+               else if (ikcqe->op_code == ISCSI_KCQE_OPCODE_ISCSI_ERROR)
+                       bnx2i_process_iscsi_error(hba, ikcqe);
+               else if (ikcqe->op_code == ISCSI_KCQE_OPCODE_TCP_ERROR)
+                       bnx2i_process_tcp_error(hba, ikcqe);
+               else
+                       printk(KERN_ALERT "bnx2i: unknown opcode 0x%x\n",
+                                         ikcqe->op_code);
+       }
+}
+
+
+/**
+ * bnx2i_indicate_netevent - Generic netdev event handler
+ * @context:   adapter structure pointer
+ * @event:     event type
+ *
+ * Handles four netdev events, NETDEV_UP, NETDEV_DOWN,
+ *     NETDEV_GOING_DOWN and NETDEV_CHANGE
+ */
+static void bnx2i_indicate_netevent(void *context, unsigned long event)
+{
+       struct bnx2i_hba *hba = context;
+
+       switch (event) {
+       case NETDEV_UP:
+               if (!test_bit(ADAPTER_STATE_UP, &hba->adapter_state))
+                       bnx2i_send_fw_iscsi_init_msg(hba);
+               break;
+       case NETDEV_DOWN:
+               clear_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state);
+               clear_bit(ADAPTER_STATE_UP, &hba->adapter_state);
+               break;
+       case NETDEV_GOING_DOWN:
+               set_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state);
+               iscsi_host_for_each_session(hba->shost,
+                                           bnx2i_drop_session);
+               break;
+       case NETDEV_CHANGE:
+               bnx2i_get_link_state(hba);
+               break;
+       default:
+               ;
+       }
+}
+
+
+/**
+ * bnx2i_cm_connect_cmpl - process iscsi conn establishment completion
+ * @cm_sk:             cnic sock structure pointer
+ *
+ * function callback exported via bnx2i - cnic driver interface to
+ *     indicate completion of option-2 TCP connect request.
+ */
+static void bnx2i_cm_connect_cmpl(struct cnic_sock *cm_sk)
+{
+       struct bnx2i_endpoint *ep = (struct bnx2i_endpoint *) cm_sk->context;
+
+       if (test_bit(ADAPTER_STATE_GOING_DOWN, &ep->hba->adapter_state))
+               ep->state = EP_STATE_CONNECT_FAILED;
+       else if (test_bit(SK_F_OFFLD_COMPLETE, &cm_sk->flags))
+               ep->state = EP_STATE_CONNECT_COMPL;
+       else
+               ep->state = EP_STATE_CONNECT_FAILED;
+
+       wake_up_interruptible(&ep->ofld_wait);
+}
+
+
+/**
+ * bnx2i_cm_close_cmpl - process tcp conn close completion
+ * @cm_sk:     cnic sock structure pointer
+ *
+ * function callback exported via bnx2i - cnic driver interface to
+ *     indicate completion of option-2 graceful TCP connect shutdown
+ */
+static void bnx2i_cm_close_cmpl(struct cnic_sock *cm_sk)
+{
+       struct bnx2i_endpoint *ep = (struct bnx2i_endpoint *) cm_sk->context;
+
+       ep->state = EP_STATE_DISCONN_COMPL;
+       wake_up_interruptible(&ep->ofld_wait);
+}
+
+
+/**
+ * bnx2i_cm_abort_cmpl - process abortive tcp conn teardown completion
+ * @cm_sk:     cnic sock structure pointer
+ *
+ * function callback exported via bnx2i - cnic driver interface to
+ *     indicate completion of option-2 abortive TCP connect termination
+ */
+static void bnx2i_cm_abort_cmpl(struct cnic_sock *cm_sk)
+{
+       struct bnx2i_endpoint *ep = (struct bnx2i_endpoint *) cm_sk->context;
+
+       ep->state = EP_STATE_DISCONN_COMPL;
+       wake_up_interruptible(&ep->ofld_wait);
+}
+
+
+/**
+ * bnx2i_cm_remote_close - process received TCP FIN
+ * @hba:               adapter structure pointer
+ * @update_kcqe:       kcqe pointer
+ *
+ * function callback exported via bnx2i - cnic driver interface to indicate
+ *     async TCP events such as FIN
+ */
+static void bnx2i_cm_remote_close(struct cnic_sock *cm_sk)
+{
+       struct bnx2i_endpoint *ep = (struct bnx2i_endpoint *) cm_sk->context;
+
+       ep->state = EP_STATE_TCP_FIN_RCVD;
+       if (ep->conn)
+               bnx2i_recovery_que_add_conn(ep->hba, ep->conn);
+}
+
+/**
+ * bnx2i_cm_remote_abort - process TCP RST and start conn cleanup
+ * @hba:               adapter structure pointer
+ * @update_kcqe:       kcqe pointer
+ *
+ * function callback exported via bnx2i - cnic driver interface to
+ *     indicate async TCP events (RST) sent by the peer.
+ */
+static void bnx2i_cm_remote_abort(struct cnic_sock *cm_sk)
+{
+       struct bnx2i_endpoint *ep = (struct bnx2i_endpoint *) cm_sk->context;
+
+       ep->state = EP_STATE_TCP_RST_RCVD;
+       if (ep->conn)
+               bnx2i_recovery_que_add_conn(ep->hba, ep->conn);
+}
+
+
+static void bnx2i_send_nl_mesg(struct cnic_dev *dev, u32 msg_type,
+                              char *buf, u16 buflen)
+{
+       struct bnx2i_hba *hba;
+
+       hba = bnx2i_find_hba_for_cnic(dev);
+       if (!hba)
+               return;
+
+       if (iscsi_offload_mesg(hba->shost, &bnx2i_iscsi_transport,
+                                  msg_type, buf, buflen))
+               printk(KERN_ALERT "bnx2i: private nl message send error\n");
+
+}
+
+
+/**
+ * bnx2i_cnic_cb - global template of bnx2i - cnic driver interface structure
+ *                     carrying callback function pointers
+ *
+ */
+struct cnic_ulp_ops bnx2i_cnic_cb = {
+       .cnic_init = bnx2i_ulp_init,
+       .cnic_exit = bnx2i_ulp_exit,
+       .cnic_start = bnx2i_start,
+       .cnic_stop = bnx2i_stop,
+       .indicate_kcqes = bnx2i_indicate_kcqe,
+       .indicate_netevent = bnx2i_indicate_netevent,
+       .cm_connect_complete = bnx2i_cm_connect_cmpl,
+       .cm_close_complete = bnx2i_cm_close_cmpl,
+       .cm_abort_complete = bnx2i_cm_abort_cmpl,
+       .cm_remote_close = bnx2i_cm_remote_close,
+       .cm_remote_abort = bnx2i_cm_remote_abort,
+       .iscsi_nl_send_msg = bnx2i_send_nl_mesg,
+       .owner = THIS_MODULE
+};
+
+
+/**
+ * bnx2i_map_ep_dbell_regs - map connection doorbell registers
+ * @ep: bnx2i endpoint
+ *
+ * maps connection's SQ and RQ doorbell registers, 5706/5708/5709 hosts these
+ *     register in BAR #0. Whereas in 57710 these register are accessed by
+ *     mapping BAR #1
+ */
+int bnx2i_map_ep_dbell_regs(struct bnx2i_endpoint *ep)
+{
+       u32 cid_num;
+       u32 reg_off;
+       u32 first_l4l5;
+       u32 ctx_sz;
+       u32 config2;
+       resource_size_t reg_base;
+
+       cid_num = bnx2i_get_cid_num(ep);
+
+       if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) {
+               reg_base = pci_resource_start(ep->hba->pcidev,
+                                             BNX2X_DOORBELL_PCI_BAR);
+               reg_off = PAGE_SIZE * (cid_num & 0x1FFFF) + DPM_TRIGER_TYPE;
+               ep->qp.ctx_base = ioremap_nocache(reg_base + reg_off, 4);
+               goto arm_cq;
+       }
+
+       reg_base = ep->hba->netdev->base_addr;
+       if ((test_bit(BNX2I_NX2_DEV_5709, &ep->hba->cnic_dev_type)) &&
+           (ep->hba->mail_queue_access == BNX2I_MQ_BIN_MODE)) {
+               config2 = REG_RD(ep->hba, BNX2_MQ_CONFIG2);
+               first_l4l5 = config2 & BNX2_MQ_CONFIG2_FIRST_L4L5;
+               ctx_sz = (config2 & BNX2_MQ_CONFIG2_CONT_SZ) >> 3;
+               if (ctx_sz)
+                       reg_off = CTX_OFFSET + MAX_CID_CNT * MB_KERNEL_CTX_SIZE
+                                 + PAGE_SIZE *
+                                 (((cid_num - first_l4l5) / ctx_sz) + 256);
+               else
+                       reg_off = CTX_OFFSET + (MB_KERNEL_CTX_SIZE * cid_num);
+       } else
+               /* 5709 device in normal node and 5706/5708 devices */
+               reg_off = CTX_OFFSET + (MB_KERNEL_CTX_SIZE * cid_num);
+
+       ep->qp.ctx_base = ioremap_nocache(reg_base + reg_off,
+                                         MB_KERNEL_CTX_SIZE);
+       if (!ep->qp.ctx_base)
+               return -ENOMEM;
+
+arm_cq:
+       bnx2i_arm_cq_event_coalescing(ep, CNIC_ARM_CQE);
+       return 0;
+}
diff --git a/drivers/scsi/bnx2i/bnx2i_init.c b/drivers/scsi/bnx2i/bnx2i_init.c
new file mode 100644 (file)
index 0000000..ae4b2d5
--- /dev/null
@@ -0,0 +1,438 @@
+/* bnx2i.c: Broadcom NetXtreme II iSCSI driver.
+ *
+ * Copyright (c) 2006 - 2009 Broadcom Corporation
+ * Copyright (c) 2007, 2008 Red Hat, Inc.  All rights reserved.
+ * Copyright (c) 2007, 2008 Mike Christie
+ *
+ * This program is free software; 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: Anil Veerabhadrappa (anilgv@broadcom.com)
+ */
+
+#include "bnx2i.h"
+
+static struct list_head adapter_list = LIST_HEAD_INIT(adapter_list);
+static u32 adapter_count;
+static int bnx2i_reg_device;
+
+#define DRV_MODULE_NAME                "bnx2i"
+#define DRV_MODULE_VERSION     "2.0.1d"
+#define DRV_MODULE_RELDATE     "Mar 25, 2009"
+
+static char version[] __devinitdata =
+               "Broadcom NetXtreme II iSCSI Driver " DRV_MODULE_NAME \
+               " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
+
+
+MODULE_AUTHOR("Anil Veerabhadrappa <anilgv@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom NetXtreme II BCM5706/5708/5709 iSCSI Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_MODULE_VERSION);
+
+static DEFINE_RWLOCK(bnx2i_dev_lock);
+
+unsigned int event_coal_div = 1;
+module_param(event_coal_div, int, 0664);
+MODULE_PARM_DESC(event_coal_div, "Event Coalescing Divide Factor");
+
+unsigned int en_tcp_dack = 1;
+module_param(en_tcp_dack, int, 0664);
+MODULE_PARM_DESC(en_tcp_dack, "Enable TCP Delayed ACK");
+
+unsigned int error_mask1 = 0x00;
+module_param(error_mask1, int, 0664);
+MODULE_PARM_DESC(error_mask1, "Config FW iSCSI Error Mask #1");
+
+unsigned int error_mask2 = 0x00;
+module_param(error_mask2, int, 0664);
+MODULE_PARM_DESC(error_mask2, "Config FW iSCSI Error Mask #2");
+
+unsigned int sq_size;
+module_param(sq_size, int, 0664);
+MODULE_PARM_DESC(sq_size, "Configure SQ size");
+
+unsigned int rq_size = BNX2I_RQ_WQES_DEFAULT;
+module_param(rq_size, int, 0664);
+MODULE_PARM_DESC(rq_size, "Configure RQ size");
+
+u64 iscsi_error_mask = 0x00;
+
+static void bnx2i_unreg_one_device(struct bnx2i_hba *hba) ;
+
+
+/**
+ * bnx2i_identify_device - identifies NetXtreme II device type
+ * @hba:               Adapter structure pointer
+ *
+ * This function identifies the NX2 device type and sets appropriate
+ *     queue mailbox register access method, 5709 requires driver to
+ *     access MBOX regs using *bin* mode
+ */
+void bnx2i_identify_device(struct bnx2i_hba *hba)
+{
+       hba->cnic_dev_type = 0;
+       if ((hba->pci_did == PCI_DEVICE_ID_NX2_5706) ||
+           (hba->pci_did == PCI_DEVICE_ID_NX2_5706S))
+               set_bit(BNX2I_NX2_DEV_5706, &hba->cnic_dev_type);
+       else if ((hba->pci_did == PCI_DEVICE_ID_NX2_5708) ||
+           (hba->pci_did == PCI_DEVICE_ID_NX2_5708S))
+               set_bit(BNX2I_NX2_DEV_5708, &hba->cnic_dev_type);
+       else if ((hba->pci_did == PCI_DEVICE_ID_NX2_5709) ||
+           (hba->pci_did == PCI_DEVICE_ID_NX2_5709S)) {
+               set_bit(BNX2I_NX2_DEV_5709, &hba->cnic_dev_type);
+               hba->mail_queue_access = BNX2I_MQ_BIN_MODE;
+       } else if (hba->pci_did == PCI_DEVICE_ID_NX2_57710 ||
+                  hba->pci_did == PCI_DEVICE_ID_NX2_57711)
+               set_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type);
+}
+
+
+/**
+ * get_adapter_list_head - returns head of adapter list
+ */
+struct bnx2i_hba *get_adapter_list_head(void)
+{
+       struct bnx2i_hba *hba = NULL;
+       struct bnx2i_hba *tmp_hba;
+
+       if (!adapter_count)
+               goto hba_not_found;
+
+       read_lock(&bnx2i_dev_lock);
+       list_for_each_entry(tmp_hba, &adapter_list, link) {
+               if (tmp_hba->cnic && tmp_hba->cnic->cm_select_dev) {
+                       hba = tmp_hba;
+                       break;
+               }
+       }
+       read_unlock(&bnx2i_dev_lock);
+hba_not_found:
+       return hba;
+}
+
+
+/**
+ * bnx2i_find_hba_for_cnic - maps cnic device instance to bnx2i adapter instance
+ * @cnic:      pointer to cnic device instance
+ *
+ */
+struct bnx2i_hba *bnx2i_find_hba_for_cnic(struct cnic_dev *cnic)
+{
+       struct bnx2i_hba *hba, *temp;
+
+       read_lock(&bnx2i_dev_lock);
+       list_for_each_entry_safe(hba, temp, &adapter_list, link) {
+               if (hba->cnic == cnic) {
+                       read_unlock(&bnx2i_dev_lock);
+                       return hba;
+               }
+       }
+       read_unlock(&bnx2i_dev_lock);
+       return NULL;
+}
+
+
+/**
+ * bnx2i_start - cnic callback to initialize & start adapter instance
+ * @handle:    transparent handle pointing to adapter structure
+ *
+ * This function maps adapter structure to pcidev structure and initiates
+ *     firmware handshake to enable/initialize on chip iscsi components
+ *     This bnx2i - cnic interface api callback is issued after following
+ *     2 conditions are met -
+ *       a) underlying network interface is up (marked by event 'NETDEV_UP'
+ *             from netdev
+ *       b) bnx2i adapter instance is registered
+ */
+void bnx2i_start(void *handle)
+{
+#define BNX2I_INIT_POLL_TIME   (1000 / HZ)
+       struct bnx2i_hba *hba = handle;
+       int i = HZ;
+
+       bnx2i_send_fw_iscsi_init_msg(hba);
+       while (!test_bit(ADAPTER_STATE_UP, &hba->adapter_state) && i--)
+               msleep(BNX2I_INIT_POLL_TIME);
+}
+
+
+/**
+ * bnx2i_stop - cnic callback to shutdown adapter instance
+ * @handle:    transparent handle pointing to adapter structure
+ *
+ * driver checks if adapter is already in shutdown mode, if not start
+ *     the shutdown process
+ */
+void bnx2i_stop(void *handle)
+{
+       struct bnx2i_hba *hba = handle;
+
+       /* check if cleanup happened in GOING_DOWN context */
+       clear_bit(ADAPTER_STATE_UP, &hba->adapter_state);
+       if (!test_and_clear_bit(ADAPTER_STATE_GOING_DOWN,
+                               &hba->adapter_state))
+               iscsi_host_for_each_session(hba->shost,
+                                           bnx2i_drop_session);
+}
+
+/**
+ * bnx2i_register_device - register bnx2i adapter instance with the cnic driver
+ * @hba:       Adapter instance to register
+ *
+ * registers bnx2i adapter instance with the cnic driver while holding the
+ *     adapter structure lock
+ */
+void bnx2i_register_device(struct bnx2i_hba *hba)
+{
+       if (test_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state) ||
+           test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) {
+               return;
+       }
+
+       hba->cnic->register_device(hba->cnic, CNIC_ULP_ISCSI, hba);
+
+       spin_lock(&hba->lock);
+       bnx2i_reg_device++;
+       spin_unlock(&hba->lock);
+
+       set_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic);
+}
+
+
+/**
+ * bnx2i_reg_dev_all - registers all adapter instances with the cnic driver
+ *
+ * registers all bnx2i adapter instances with the cnic driver while holding
+ *     the global resource lock
+ */
+void bnx2i_reg_dev_all(void)
+{
+       struct bnx2i_hba *hba, *temp;
+
+       read_lock(&bnx2i_dev_lock);
+       list_for_each_entry_safe(hba, temp, &adapter_list, link)
+               bnx2i_register_device(hba);
+       read_unlock(&bnx2i_dev_lock);
+}
+
+
+/**
+ * bnx2i_unreg_one_device - unregister adapter instance with the cnic driver
+ * @hba:       Adapter instance to unregister
+ *
+ * registers bnx2i adapter instance with the cnic driver while holding
+ *     the adapter structure lock
+ */
+static void bnx2i_unreg_one_device(struct bnx2i_hba *hba)
+{
+       if (hba->ofld_conns_active ||
+           !test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic) ||
+           test_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state))
+               return;
+
+       hba->cnic->unregister_device(hba->cnic, CNIC_ULP_ISCSI);
+
+       spin_lock(&hba->lock);
+       bnx2i_reg_device--;
+       spin_unlock(&hba->lock);
+
+       /* ep_disconnect could come before NETDEV_DOWN, driver won't
+        * see NETDEV_DOWN as it already unregistered itself.
+        */
+       hba->adapter_state = 0;
+       clear_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic);
+}
+
+/**
+ * bnx2i_unreg_dev_all - unregisters all bnx2i instances with the cnic driver
+ *
+ * unregisters all bnx2i adapter instances with the cnic driver while holding
+ *     the global resource lock
+ */
+void bnx2i_unreg_dev_all(void)
+{
+       struct bnx2i_hba *hba, *temp;
+
+       read_lock(&bnx2i_dev_lock);
+       list_for_each_entry_safe(hba, temp, &adapter_list, link)
+               bnx2i_unreg_one_device(hba);
+       read_unlock(&bnx2i_dev_lock);
+}
+
+
+/**
+ * bnx2i_init_one - initialize an adapter instance and allocate memory resources
+ * @hba:       bnx2i adapter instance
+ * @cnic:      cnic device handle
+ *
+ * Global resource lock and host adapter lock is held during critical sections
+ *     below. This routine is called from cnic_register_driver() context and
+ *     work horse thread which does majority of device specific initialization
+ */
+static int bnx2i_init_one(struct bnx2i_hba *hba, struct cnic_dev *cnic)
+{
+       int rc;
+
+       read_lock(&bnx2i_dev_lock);
+       if (bnx2i_reg_device &&
+           !test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) {
+               rc = cnic->register_device(cnic, CNIC_ULP_ISCSI, hba);
+               if (rc)         /* duplicate registration */
+                       printk(KERN_ERR "bnx2i- dev reg failed\n");
+
+               spin_lock(&hba->lock);
+               bnx2i_reg_device++;
+               hba->age++;
+               spin_unlock(&hba->lock);
+
+               set_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic);
+       }
+       read_unlock(&bnx2i_dev_lock);
+
+       write_lock(&bnx2i_dev_lock);
+       list_add_tail(&hba->link, &adapter_list);
+       adapter_count++;
+       write_unlock(&bnx2i_dev_lock);
+       return 0;
+}
+
+
+/**
+ * bnx2i_ulp_init - initialize an adapter instance
+ * @dev:       cnic device handle
+ *
+ * Called from cnic_register_driver() context to initialize all enumerated
+ *     cnic devices. This routine allocate adapter structure and other
+ *     device specific resources.
+ */
+void bnx2i_ulp_init(struct cnic_dev *dev)
+{
+       struct bnx2i_hba *hba;
+
+       /* Allocate a HBA structure for this device */
+       hba = bnx2i_alloc_hba(dev);
+       if (!hba) {
+               printk(KERN_ERR "bnx2i init: hba initialization failed\n");
+               return;
+       }
+
+       /* Get PCI related information and update hba struct members */
+       clear_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic);
+       if (bnx2i_init_one(hba, dev)) {
+               printk(KERN_ERR "bnx2i - hba %p init failed\n", hba);
+               bnx2i_free_hba(hba);
+       } else
+               hba->cnic = dev;
+}
+
+
+/**
+ * bnx2i_ulp_exit - shuts down adapter instance and frees all resources
+ * @dev:       cnic device handle
+ *
+ */
+void bnx2i_ulp_exit(struct cnic_dev *dev)
+{
+       struct bnx2i_hba *hba;
+
+       hba = bnx2i_find_hba_for_cnic(dev);
+       if (!hba) {
+               printk(KERN_INFO "bnx2i_ulp_exit: hba not "
+                                "found, dev 0x%p\n", dev);
+               return;
+       }
+       write_lock(&bnx2i_dev_lock);
+       list_del_init(&hba->link);
+       adapter_count--;
+
+       if (test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) {
+               hba->cnic->unregister_device(hba->cnic, CNIC_ULP_ISCSI);
+               clear_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic);
+
+               spin_lock(&hba->lock);
+               bnx2i_reg_device--;
+               spin_unlock(&hba->lock);
+       }
+       write_unlock(&bnx2i_dev_lock);
+
+       bnx2i_free_hba(hba);
+}
+
+
+/**
+ * bnx2i_mod_init - module init entry point
+ *
+ * initialize any driver wide global data structures such as endpoint pool,
+ *     tcp port manager/queue, sysfs. finally driver will register itself
+ *     with the cnic module
+ */
+static int __init bnx2i_mod_init(void)
+{
+       int err;
+
+       printk(KERN_INFO "%s", version);
+
+       if (!is_power_of_2(sq_size))
+               sq_size = roundup_pow_of_two(sq_size);
+
+       bnx2i_scsi_xport_template =
+                       iscsi_register_transport(&bnx2i_iscsi_transport);
+       if (!bnx2i_scsi_xport_template) {
+               printk(KERN_ERR "Could not register bnx2i transport.\n");
+               err = -ENOMEM;
+               goto out;
+       }
+
+       err = cnic_register_driver(CNIC_ULP_ISCSI, &bnx2i_cnic_cb);
+       if (err) {
+               printk(KERN_ERR "Could not register bnx2i cnic driver.\n");
+               goto unreg_xport;
+       }
+
+       return 0;
+
+unreg_xport:
+       iscsi_unregister_transport(&bnx2i_iscsi_transport);
+out:
+       return err;
+}
+
+
+/**
+ * bnx2i_mod_exit - module cleanup/exit entry point
+ *
+ * Global resource lock and host adapter lock is held during critical sections
+ *     in this function. Driver will browse through the adapter list, cleans-up
+ *     each instance, unregisters iscsi transport name and finally driver will
+ *     unregister itself with the cnic module
+ */
+static void __exit bnx2i_mod_exit(void)
+{
+       struct bnx2i_hba *hba;
+
+       write_lock(&bnx2i_dev_lock);
+       while (!list_empty(&adapter_list)) {
+               hba = list_entry(adapter_list.next, struct bnx2i_hba, link);
+               list_del(&hba->link);
+               adapter_count--;
+
+               if (test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) {
+                       hba->cnic->unregister_device(hba->cnic, CNIC_ULP_ISCSI);
+                       clear_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic);
+                       bnx2i_reg_device--;
+               }
+
+               write_unlock(&bnx2i_dev_lock);
+               bnx2i_free_hba(hba);
+               write_lock(&bnx2i_dev_lock);
+       }
+       write_unlock(&bnx2i_dev_lock);
+
+       iscsi_unregister_transport(&bnx2i_iscsi_transport);
+       cnic_unregister_driver(CNIC_ULP_ISCSI);
+}
+
+module_init(bnx2i_mod_init);
+module_exit(bnx2i_mod_exit);
diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c
new file mode 100644 (file)
index 0000000..f741219
--- /dev/null
@@ -0,0 +1,2064 @@
+/*
+ * bnx2i_iscsi.c: Broadcom NetXtreme II iSCSI driver.
+ *
+ * Copyright (c) 2006 - 2009 Broadcom Corporation
+ * Copyright (c) 2007, 2008 Red Hat, Inc.  All rights reserved.
+ * Copyright (c) 2007, 2008 Mike Christie
+ *
+ * This program is free software; 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: Anil Veerabhadrappa (anilgv@broadcom.com)
+ */
+
+#include <scsi/scsi_tcq.h>
+#include <scsi/libiscsi.h>
+#include "bnx2i.h"
+
+struct scsi_transport_template *bnx2i_scsi_xport_template;
+struct iscsi_transport bnx2i_iscsi_transport;
+static struct scsi_host_template bnx2i_host_template;
+
+/*
+ * Global endpoint resource info
+ */
+static DEFINE_SPINLOCK(bnx2i_resc_lock); /* protects global resources */
+
+
+static int bnx2i_adapter_ready(struct bnx2i_hba *hba)
+{
+       int retval = 0;
+
+       if (!hba || !test_bit(ADAPTER_STATE_UP, &hba->adapter_state) ||
+           test_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state) ||
+           test_bit(ADAPTER_STATE_LINK_DOWN, &hba->adapter_state))
+               retval = -EPERM;
+       return retval;
+}
+
+/**
+ * bnx2i_get_write_cmd_bd_idx - identifies various BD bookmarks
+ * @cmd:               iscsi cmd struct pointer
+ * @buf_off:           absolute buffer offset
+ * @start_bd_off:      u32 pointer to return the offset within the BD
+ *                     indicated by 'start_bd_idx' on which 'buf_off' falls
+ * @start_bd_idx:      index of the BD on which 'buf_off' falls
+ *
+ * identifies & marks various bd info for scsi command's imm data,
+ * unsolicited data and the first solicited data seq.
+ */
+static void bnx2i_get_write_cmd_bd_idx(struct bnx2i_cmd *cmd, u32 buf_off,
+                                      u32 *start_bd_off, u32 *start_bd_idx)
+{
+       struct iscsi_bd *bd_tbl = cmd->io_tbl.bd_tbl;
+       u32 cur_offset = 0;
+       u32 cur_bd_idx = 0;
+
+       if (buf_off) {
+               while (buf_off >= (cur_offset + bd_tbl->buffer_length)) {
+                       cur_offset += bd_tbl->buffer_length;
+                       cur_bd_idx++;
+                       bd_tbl++;
+               }
+       }
+
+       *start_bd_off = buf_off - cur_offset;
+       *start_bd_idx = cur_bd_idx;
+}
+
+/**
+ * bnx2i_setup_write_cmd_bd_info - sets up BD various information
+ * @task:      transport layer's cmd struct pointer
+ *
+ * identifies & marks various bd info for scsi command's immediate data,
+ * unsolicited data and first solicited data seq which includes BD start
+ * index & BD buf off. his function takes into account iscsi parameter such
+ * as immediate data and unsolicited data is support on this connection.
+ */
+static void bnx2i_setup_write_cmd_bd_info(struct iscsi_task *task)
+{
+       struct bnx2i_cmd *cmd = task->dd_data;
+       u32 start_bd_offset;
+       u32 start_bd_idx;
+       u32 buffer_offset = 0;
+       u32 cmd_len = cmd->req.total_data_transfer_length;
+
+       /* if ImmediateData is turned off & IntialR2T is turned on,
+        * there will be no immediate or unsolicited data, just return.
+        */
+       if (!iscsi_task_has_unsol_data(task) && !task->imm_count)
+               return;
+
+       /* Immediate data */
+       buffer_offset += task->imm_count;
+       if (task->imm_count == cmd_len)
+               return;
+
+       if (iscsi_task_has_unsol_data(task)) {
+               bnx2i_get_write_cmd_bd_idx(cmd, buffer_offset,
+                                          &start_bd_offset, &start_bd_idx);
+               cmd->req.ud_buffer_offset = start_bd_offset;
+               cmd->req.ud_start_bd_index = start_bd_idx;
+               buffer_offset += task->unsol_r2t.data_length;
+       }
+
+       if (buffer_offset != cmd_len) {
+               bnx2i_get_write_cmd_bd_idx(cmd, buffer_offset,
+                                          &start_bd_offset, &start_bd_idx);
+               if ((start_bd_offset > task->conn->session->first_burst) ||
+                   (start_bd_idx > scsi_sg_count(cmd->scsi_cmd))) {
+                       int i = 0;
+
+                       iscsi_conn_printk(KERN_ALERT, task->conn,
+                                         "bnx2i- error, buf offset 0x%x "
+                                         "bd_valid %d use_sg %d\n",
+                                         buffer_offset, cmd->io_tbl.bd_valid,
+                                         scsi_sg_count(cmd->scsi_cmd));
+                       for (i = 0; i < cmd->io_tbl.bd_valid; i++)
+                               iscsi_conn_printk(KERN_ALERT, task->conn,
+                                                 "bnx2i err, bd[%d]: len %x\n",
+                                                 i, cmd->io_tbl.bd_tbl[i].\
+                                                 buffer_length);
+               }
+               cmd->req.sd_buffer_offset = start_bd_offset;
+               cmd->req.sd_start_bd_index = start_bd_idx;
+       }
+}
+
+
+
+/**
+ * bnx2i_map_scsi_sg - maps IO buffer and prepares the BD table
+ * @hba:       adapter instance
+ * @cmd:       iscsi cmd struct pointer
+ *
+ * map SG list
+ */
+static int bnx2i_map_scsi_sg(struct bnx2i_hba *hba, struct bnx2i_cmd *cmd)
+{
+       struct scsi_cmnd *sc = cmd->scsi_cmd;
+       struct iscsi_bd *bd = cmd->io_tbl.bd_tbl;
+       struct scatterlist *sg;
+       int byte_count = 0;
+       int bd_count = 0;
+       int sg_count;
+       int sg_len;
+       u64 addr;
+       int i;
+
+       BUG_ON(scsi_sg_count(sc) > ISCSI_MAX_BDS_PER_CMD);
+
+       sg_count = scsi_dma_map(sc);
+
+       scsi_for_each_sg(sc, sg, sg_count, i) {
+               sg_len = sg_dma_len(sg);
+               addr = (u64) sg_dma_address(sg);
+               bd[bd_count].buffer_addr_lo = addr & 0xffffffff;
+               bd[bd_count].buffer_addr_hi = addr >> 32;
+               bd[bd_count].buffer_length = sg_len;
+               bd[bd_count].flags = 0;
+               if (bd_count == 0)
+                       bd[bd_count].flags = ISCSI_BD_FIRST_IN_BD_CHAIN;
+
+               byte_count += sg_len;
+               bd_count++;
+       }
+
+       if (bd_count)
+               bd[bd_count - 1].flags |= ISCSI_BD_LAST_IN_BD_CHAIN;
+
+       BUG_ON(byte_count != scsi_bufflen(sc));
+       return bd_count;
+}
+
+/**
+ * bnx2i_iscsi_map_sg_list - maps SG list
+ * @cmd:       iscsi cmd struct pointer
+ *
+ * creates BD list table for the command
+ */
+static void bnx2i_iscsi_map_sg_list(struct bnx2i_cmd *cmd)
+{
+       int bd_count;
+
+       bd_count  = bnx2i_map_scsi_sg(cmd->conn->hba, cmd);
+       if (!bd_count) {
+               struct iscsi_bd *bd = cmd->io_tbl.bd_tbl;
+
+               bd[0].buffer_addr_lo = bd[0].buffer_addr_hi = 0;
+               bd[0].buffer_length = bd[0].flags = 0;
+       }
+       cmd->io_tbl.bd_valid = bd_count;
+}
+
+
+/**
+ * bnx2i_iscsi_unmap_sg_list - unmaps SG list
+ * @cmd:       iscsi cmd struct pointer
+ *
+ * unmap IO buffers and invalidate the BD table
+ */
+void bnx2i_iscsi_unmap_sg_list(struct bnx2i_cmd *cmd)
+{
+       struct scsi_cmnd *sc = cmd->scsi_cmd;
+
+       if (cmd->io_tbl.bd_valid && sc) {
+               scsi_dma_unmap(sc);
+               cmd->io_tbl.bd_valid = 0;
+       }
+}
+
+static void bnx2i_setup_cmd_wqe_template(struct bnx2i_cmd *cmd)
+{
+       memset(&cmd->req, 0x00, sizeof(cmd->req));
+       cmd->req.op_code = 0xFF;
+       cmd->req.bd_list_addr_lo = (u32) cmd->io_tbl.bd_tbl_dma;
+       cmd->req.bd_list_addr_hi =
+               (u32) ((u64) cmd->io_tbl.bd_tbl_dma >> 32);
+
+}
+
+
+/**
+ * bnx2i_bind_conn_to_iscsi_cid - bind conn structure to 'iscsi_cid'
+ * @hba:       pointer to adapter instance
+ * @conn:      pointer to iscsi connection
+ * @iscsi_cid: iscsi context ID, range 0 - (MAX_CONN - 1)
+ *
+ * update iscsi cid table entry with connection pointer. This enables
+ *     driver to quickly get hold of connection structure pointer in
+ *     completion/interrupt thread using iscsi context ID
+ */
+static int bnx2i_bind_conn_to_iscsi_cid(struct bnx2i_hba *hba,
+                                       struct bnx2i_conn *bnx2i_conn,
+                                       u32 iscsi_cid)
+{
+       if (hba && hba->cid_que.conn_cid_tbl[iscsi_cid]) {
+               iscsi_conn_printk(KERN_ALERT, bnx2i_conn->cls_conn->dd_data,
+                                "conn bind - entry #%d not free\n", iscsi_cid);
+               return -EBUSY;
+       }
+
+       hba->cid_que.conn_cid_tbl[iscsi_cid] = bnx2i_conn;
+       return 0;
+}
+
+
+/**
+ * bnx2i_get_conn_from_id - maps an iscsi cid to corresponding conn ptr
+ * @hba:       pointer to adapter instance
+ * @iscsi_cid: iscsi context ID, range 0 - (MAX_CONN - 1)
+ */
+struct bnx2i_conn *bnx2i_get_conn_from_id(struct bnx2i_hba *hba,
+                                         u16 iscsi_cid)
+{
+       if (!hba->cid_que.conn_cid_tbl) {
+               printk(KERN_ERR "bnx2i: ERROR - missing conn<->cid table\n");
+               return NULL;
+
+       } else if (iscsi_cid >= hba->max_active_conns) {
+               printk(KERN_ERR "bnx2i: wrong cid #%d\n", iscsi_cid);
+               return NULL;
+       }
+       return hba->cid_que.conn_cid_tbl[iscsi_cid];
+}
+
+
+/**
+ * bnx2i_alloc_iscsi_cid - allocates a iscsi_cid from free pool
+ * @hba:       pointer to adapter instance
+ */
+static u32 bnx2i_alloc_iscsi_cid(struct bnx2i_hba *hba)
+{
+       int idx;
+
+       if (!hba->cid_que.cid_free_cnt)
+               return -1;
+
+       idx = hba->cid_que.cid_q_cons_idx;
+       hba->cid_que.cid_q_cons_idx++;
+       if (hba->cid_que.cid_q_cons_idx == hba->cid_que.cid_q_max_idx)
+               hba->cid_que.cid_q_cons_idx = 0;
+
+       hba->cid_que.cid_free_cnt--;
+       return hba->cid_que.cid_que[idx];
+}
+
+
+/**
+ * bnx2i_free_iscsi_cid - returns tcp port to free list
+ * @hba:               pointer to adapter instance
+ * @iscsi_cid:         iscsi context ID to free
+ */
+static void bnx2i_free_iscsi_cid(struct bnx2i_hba *hba, u16 iscsi_cid)
+{
+       int idx;
+
+       if (iscsi_cid == (u16) -1)
+               return;
+
+       hba->cid_que.cid_free_cnt++;
+
+       idx = hba->cid_que.cid_q_prod_idx;
+       hba->cid_que.cid_que[idx] = iscsi_cid;
+       hba->cid_que.conn_cid_tbl[iscsi_cid] = NULL;
+       hba->cid_que.cid_q_prod_idx++;
+       if (hba->cid_que.cid_q_prod_idx == hba->cid_que.cid_q_max_idx)
+               hba->cid_que.cid_q_prod_idx = 0;
+}
+
+
+/**
+ * bnx2i_setup_free_cid_que - sets up free iscsi cid queue
+ * @hba:       pointer to adapter instance
+ *
+ * allocates memory for iscsi cid queue & 'cid - conn ptr' mapping table,
+ *     and initialize table attributes
+ */
+static int bnx2i_setup_free_cid_que(struct bnx2i_hba *hba)
+{
+       int mem_size;
+       int i;
+
+       mem_size = hba->max_active_conns * sizeof(u32);
+       mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+
+       hba->cid_que.cid_que_base = kmalloc(mem_size, GFP_KERNEL);
+       if (!hba->cid_que.cid_que_base)
+               return -ENOMEM;
+
+       mem_size = hba->max_active_conns * sizeof(struct bnx2i_conn *);
+       mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+       hba->cid_que.conn_cid_tbl = kmalloc(mem_size, GFP_KERNEL);
+       if (!hba->cid_que.conn_cid_tbl) {
+               kfree(hba->cid_que.cid_que_base);
+               hba->cid_que.cid_que_base = NULL;
+               return -ENOMEM;
+       }
+
+       hba->cid_que.cid_que = (u32 *)hba->cid_que.cid_que_base;
+       hba->cid_que.cid_q_prod_idx = 0;
+       hba->cid_que.cid_q_cons_idx = 0;
+       hba->cid_que.cid_q_max_idx = hba->max_active_conns;
+       hba->cid_que.cid_free_cnt = hba->max_active_conns;
+
+       for (i = 0; i < hba->max_active_conns; i++) {
+               hba->cid_que.cid_que[i] = i;
+               hba->cid_que.conn_cid_tbl[i] = NULL;
+       }
+       return 0;
+}
+
+
+/**
+ * bnx2i_release_free_cid_que - releases 'iscsi_cid' queue resources
+ * @hba:       pointer to adapter instance
+ */
+static void bnx2i_release_free_cid_que(struct bnx2i_hba *hba)
+{
+       kfree(hba->cid_que.cid_que_base);
+       hba->cid_que.cid_que_base = NULL;
+
+       kfree(hba->cid_que.conn_cid_tbl);
+       hba->cid_que.conn_cid_tbl = NULL;
+}
+
+
+/**
+ * bnx2i_alloc_ep - allocates ep structure from global pool
+ * @hba:       pointer to adapter instance
+ *
+ * routine allocates a free endpoint structure from global pool and
+ *     a tcp port to be used for this connection.  Global resource lock,
+ *     'bnx2i_resc_lock' is held while accessing shared global data structures
+ */
+static struct iscsi_endpoint *bnx2i_alloc_ep(struct bnx2i_hba *hba)
+{
+       struct iscsi_endpoint *ep;
+       struct bnx2i_endpoint *bnx2i_ep;
+
+       ep = iscsi_create_endpoint(sizeof(*bnx2i_ep));
+       if (!ep) {
+               printk(KERN_ERR "bnx2i: Could not allocate ep\n");
+               return NULL;
+       }
+
+       bnx2i_ep = ep->dd_data;
+       INIT_LIST_HEAD(&bnx2i_ep->link);
+       bnx2i_ep->state = EP_STATE_IDLE;
+       bnx2i_ep->hba = hba;
+       bnx2i_ep->hba_age = hba->age;
+       hba->ofld_conns_active++;
+       init_waitqueue_head(&bnx2i_ep->ofld_wait);
+       return ep;
+}
+
+
+/**
+ * bnx2i_free_ep - free endpoint
+ * @ep:                pointer to iscsi endpoint structure
+ */
+static void bnx2i_free_ep(struct iscsi_endpoint *ep)
+{
+       struct bnx2i_endpoint *bnx2i_ep = ep->dd_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&bnx2i_resc_lock, flags);
+       bnx2i_ep->state = EP_STATE_IDLE;
+       bnx2i_ep->hba->ofld_conns_active--;
+
+       bnx2i_free_iscsi_cid(bnx2i_ep->hba, bnx2i_ep->ep_iscsi_cid);
+       if (bnx2i_ep->conn) {
+               bnx2i_ep->conn->ep = NULL;
+               bnx2i_ep->conn = NULL;
+       }
+
+       bnx2i_ep->hba = NULL;
+       spin_unlock_irqrestore(&bnx2i_resc_lock, flags);
+       iscsi_destroy_endpoint(ep);
+}
+
+
+/**
+ * bnx2i_alloc_bdt - allocates buffer descriptor (BD) table for the command
+ * @hba:       adapter instance pointer
+ * @session:   iscsi session pointer
+ * @cmd:       iscsi command structure
+ */
+static int bnx2i_alloc_bdt(struct bnx2i_hba *hba, struct iscsi_session *session,
+                          struct bnx2i_cmd *cmd)
+{
+       struct io_bdt *io = &cmd->io_tbl;
+       struct iscsi_bd *bd;
+
+       io->bd_tbl = dma_alloc_coherent(&hba->pcidev->dev,
+                                       ISCSI_MAX_BDS_PER_CMD * sizeof(*bd),
+                                       &io->bd_tbl_dma, GFP_KERNEL);
+       if (!io->bd_tbl) {
+               iscsi_session_printk(KERN_ERR, session, "Could not "
+                                    "allocate bdt.\n");
+               return -ENOMEM;
+       }
+       io->bd_valid = 0;
+       return 0;
+}
+
+/**
+ * bnx2i_destroy_cmd_pool - destroys iscsi command pool and release BD table
+ * @hba:       adapter instance pointer
+ * @session:   iscsi session pointer
+ * @cmd:       iscsi command structure
+ */
+static void bnx2i_destroy_cmd_pool(struct bnx2i_hba *hba,
+                                  struct iscsi_session *session)
+{
+       int i;
+
+       for (i = 0; i < session->cmds_max; i++) {
+               struct iscsi_task *task = session->cmds[i];
+               struct bnx2i_cmd *cmd = task->dd_data;
+
+               if (cmd->io_tbl.bd_tbl)
+                       dma_free_coherent(&hba->pcidev->dev,
+                                         ISCSI_MAX_BDS_PER_CMD *
+                                         sizeof(struct iscsi_bd),
+                                         cmd->io_tbl.bd_tbl,
+                                         cmd->io_tbl.bd_tbl_dma);
+       }
+
+}
+
+
+/**
+ * bnx2i_setup_cmd_pool - sets up iscsi command pool for the session
+ * @hba:       adapter instance pointer
+ * @session:   iscsi session pointer
+ */
+static int bnx2i_setup_cmd_pool(struct bnx2i_hba *hba,
+                               struct iscsi_session *session)
+{
+       int i;
+
+       for (i = 0; i < session->cmds_max; i++) {
+               struct iscsi_task *task = session->cmds[i];
+               struct bnx2i_cmd *cmd = task->dd_data;
+
+               /* Anil */
+               task->hdr = &cmd->hdr;
+               task->hdr_max = sizeof(struct iscsi_hdr);
+
+               if (bnx2i_alloc_bdt(hba, session, cmd))
+                       goto free_bdts;
+       }
+
+       return 0;
+
+free_bdts:
+       bnx2i_destroy_cmd_pool(hba, session);
+       return -ENOMEM;
+}
+
+
+/**
+ * bnx2i_setup_mp_bdt - allocate BD table resources
+ * @hba:       pointer to adapter structure
+ *
+ * Allocate memory for dummy buffer and associated BD
+ * table to be used by middle path (MP) requests
+ */
+static int bnx2i_setup_mp_bdt(struct bnx2i_hba *hba)
+{
+       int rc = 0;
+       struct iscsi_bd *mp_bdt;
+       u64 addr;
+
+       hba->mp_bd_tbl = dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE,
+                                           &hba->mp_bd_dma, GFP_KERNEL);
+       if (!hba->mp_bd_tbl) {
+               printk(KERN_ERR "unable to allocate Middle Path BDT\n");
+               rc = -1;
+               goto out;
+       }
+
+       hba->dummy_buffer = dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE,
+                                              &hba->dummy_buf_dma, GFP_KERNEL);
+       if (!hba->dummy_buffer) {
+               printk(KERN_ERR "unable to alloc Middle Path Dummy Buffer\n");
+               dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
+                                 hba->mp_bd_tbl, hba->mp_bd_dma);
+               hba->mp_bd_tbl = NULL;
+               rc = -1;
+               goto out;
+       }
+
+       mp_bdt = (struct iscsi_bd *) hba->mp_bd_tbl;
+       addr = (unsigned long) hba->dummy_buf_dma;
+       mp_bdt->buffer_addr_lo = addr & 0xffffffff;
+       mp_bdt->buffer_addr_hi = addr >> 32;
+       mp_bdt->buffer_length = PAGE_SIZE;
+       mp_bdt->flags = ISCSI_BD_LAST_IN_BD_CHAIN |
+                       ISCSI_BD_FIRST_IN_BD_CHAIN;
+out:
+       return rc;
+}
+
+
+/**
+ * bnx2i_free_mp_bdt - releases ITT back to free pool
+ * @hba:       pointer to adapter instance
+ *
+ * free MP dummy buffer and associated BD table
+ */
+static void bnx2i_free_mp_bdt(struct bnx2i_hba *hba)
+{
+       if (hba->mp_bd_tbl) {
+               dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
+                                 hba->mp_bd_tbl, hba->mp_bd_dma);
+               hba->mp_bd_tbl = NULL;
+       }
+       if (hba->dummy_buffer) {
+               dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
+                                 hba->dummy_buffer, hba->dummy_buf_dma);
+               hba->dummy_buffer = NULL;
+       }
+               return;
+}
+
+/**
+ * bnx2i_drop_session - notifies iscsid of connection error.
+ * @hba:       adapter instance pointer
+ * @session:   iscsi session pointer
+ *
+ * This notifies iscsid that there is a error, so it can initiate
+ * recovery.
+ *
+ * This relies on caller using the iscsi class iterator so the object
+ * is refcounted and does not disapper from under us.
+ */
+void bnx2i_drop_session(struct iscsi_cls_session *cls_session)
+{
+       iscsi_session_failure(cls_session->dd_data, ISCSI_ERR_CONN_FAILED);
+}
+
+/**
+ * bnx2i_ep_destroy_list_add - add an entry to EP destroy list
+ * @hba:       pointer to adapter instance
+ * @ep:                pointer to endpoint (transport indentifier) structure
+ *
+ * EP destroy queue manager
+ */
+static int bnx2i_ep_destroy_list_add(struct bnx2i_hba *hba,
+                                    struct bnx2i_endpoint *ep)
+{
+       write_lock_bh(&hba->ep_rdwr_lock);
+       list_add_tail(&ep->link, &hba->ep_destroy_list);
+       write_unlock_bh(&hba->ep_rdwr_lock);
+       return 0;
+}
+
+/**
+ * bnx2i_ep_destroy_list_del - add an entry to EP destroy list
+ *
+ * @hba:               pointer to adapter instance
+ * @ep:                pointer to endpoint (transport indentifier) structure
+ *
+ * EP destroy queue manager
+ */
+static int bnx2i_ep_destroy_list_del(struct bnx2i_hba *hba,
+                                    struct bnx2i_endpoint *ep)
+{
+       write_lock_bh(&hba->ep_rdwr_lock);
+       list_del_init(&ep->link);
+       write_unlock_bh(&hba->ep_rdwr_lock);
+
+       return 0;
+}
+
+/**
+ * bnx2i_ep_ofld_list_add - add an entry to ep offload pending list
+ * @hba:       pointer to adapter instance
+ * @ep:                pointer to endpoint (transport indentifier) structure
+ *
+ * pending conn offload completion queue manager
+ */
+static int bnx2i_ep_ofld_list_add(struct bnx2i_hba *hba,
+                                 struct bnx2i_endpoint *ep)
+{
+       write_lock_bh(&hba->ep_rdwr_lock);
+       list_add_tail(&ep->link, &hba->ep_ofld_list);
+       write_unlock_bh(&hba->ep_rdwr_lock);
+       return 0;
+}
+
+/**
+ * bnx2i_ep_ofld_list_del - add an entry to ep offload pending list
+ * @hba:               pointer to adapter instance
+ * @ep:                pointer to endpoint (transport indentifier) structure
+ *
+ * pending conn offload completion queue manager
+ */
+static int bnx2i_ep_ofld_list_del(struct bnx2i_hba *hba,
+                                 struct bnx2i_endpoint *ep)
+{
+       write_lock_bh(&hba->ep_rdwr_lock);
+       list_del_init(&ep->link);
+       write_unlock_bh(&hba->ep_rdwr_lock);
+       return 0;
+}
+
+
+/**
+ * bnx2i_find_ep_in_ofld_list - find iscsi_cid in pending list of endpoints
+ *
+ * @hba:               pointer to adapter instance
+ * @iscsi_cid:         iscsi context ID to find
+ *
+ */
+struct bnx2i_endpoint *
+bnx2i_find_ep_in_ofld_list(struct bnx2i_hba *hba, u32 iscsi_cid)
+{
+       struct list_head *list;
+       struct list_head *tmp;
+       struct bnx2i_endpoint *ep;
+
+       read_lock_bh(&hba->ep_rdwr_lock);
+       list_for_each_safe(list, tmp, &hba->ep_ofld_list) {
+               ep = (struct bnx2i_endpoint *)list;
+
+               if (ep->ep_iscsi_cid == iscsi_cid)
+                       break;
+               ep = NULL;
+       }
+       read_unlock_bh(&hba->ep_rdwr_lock);
+
+       if (!ep)
+               printk(KERN_ERR "l5 cid %d not found\n", iscsi_cid);
+       return ep;
+}
+
+
+/**
+ * bnx2i_find_ep_in_destroy_list - find iscsi_cid in destroy list
+ * @hba:               pointer to adapter instance
+ * @iscsi_cid:         iscsi context ID to find
+ *
+ */
+struct bnx2i_endpoint *
+bnx2i_find_ep_in_destroy_list(struct bnx2i_hba *hba, u32 iscsi_cid)
+{
+       struct list_head *list;
+       struct list_head *tmp;
+       struct bnx2i_endpoint *ep;
+
+       read_lock_bh(&hba->ep_rdwr_lock);
+       list_for_each_safe(list, tmp, &hba->ep_destroy_list) {
+               ep = (struct bnx2i_endpoint *)list;
+
+               if (ep->ep_iscsi_cid == iscsi_cid)
+                       break;
+               ep = NULL;
+       }
+       read_unlock_bh(&hba->ep_rdwr_lock);
+
+       if (!ep)
+               printk(KERN_ERR "l5 cid %d not found\n", iscsi_cid);
+
+       return ep;
+}
+
+/**
+ * bnx2i_setup_host_queue_size - assigns shost->can_queue param
+ * @hba:       pointer to adapter instance
+ * @shost:     scsi host pointer
+ *
+ * Initializes 'can_queue' parameter based on how many outstanding commands
+ *     the device can handle. Each device 5708/5709/57710 has different
+ *     capabilities
+ */
+static void bnx2i_setup_host_queue_size(struct bnx2i_hba *hba,
+                                       struct Scsi_Host *shost)
+{
+       if (test_bit(BNX2I_NX2_DEV_5708, &hba->cnic_dev_type))
+               shost->can_queue = ISCSI_MAX_CMDS_PER_HBA_5708;
+       else if (test_bit(BNX2I_NX2_DEV_5709, &hba->cnic_dev_type))
+               shost->can_queue = ISCSI_MAX_CMDS_PER_HBA_5709;
+       else if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type))
+               shost->can_queue = ISCSI_MAX_CMDS_PER_HBA_57710;
+       else
+               shost->can_queue = ISCSI_MAX_CMDS_PER_HBA_5708;
+}
+
+
+/**
+ * bnx2i_alloc_hba - allocate and init adapter instance
+ * @cnic:      cnic device pointer
+ *
+ * allocate & initialize adapter structure and call other
+ *     support routines to do per adapter initialization
+ */
+struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic)
+{
+       struct Scsi_Host *shost;
+       struct bnx2i_hba *hba;
+
+       shost = iscsi_host_alloc(&bnx2i_host_template, sizeof(*hba), 0);
+       if (!shost)
+               return NULL;
+       shost->dma_boundary = cnic->pcidev->dma_mask;
+       shost->transportt = bnx2i_scsi_xport_template;
+       shost->max_id = ISCSI_MAX_CONNS_PER_HBA;
+       shost->max_channel = 0;
+       shost->max_lun = 512;
+       shost->max_cmd_len = 16;
+
+       hba = iscsi_host_priv(shost);
+       hba->shost = shost;
+       hba->netdev = cnic->netdev;
+       /* Get PCI related information and update hba struct members */
+       hba->pcidev = cnic->pcidev;
+       pci_dev_get(hba->pcidev);
+       hba->pci_did = hba->pcidev->device;
+       hba->pci_vid = hba->pcidev->vendor;
+       hba->pci_sdid = hba->pcidev->subsystem_device;
+       hba->pci_svid = hba->pcidev->subsystem_vendor;
+       hba->pci_func = PCI_FUNC(hba->pcidev->devfn);
+       hba->pci_devno = PCI_SLOT(hba->pcidev->devfn);
+       bnx2i_identify_device(hba);
+
+       bnx2i_identify_device(hba);
+       bnx2i_setup_host_queue_size(hba, shost);
+
+       if (test_bit(BNX2I_NX2_DEV_5709, &hba->cnic_dev_type)) {
+               hba->regview = ioremap_nocache(hba->netdev->base_addr,
+                                              BNX2_MQ_CONFIG2);
+               if (!hba->regview)
+                       goto ioreg_map_err;
+       } else if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type)) {
+               hba->regview = ioremap_nocache(hba->netdev->base_addr, 4096);
+               if (!hba->regview)
+                       goto ioreg_map_err;
+       }
+
+       if (bnx2i_setup_mp_bdt(hba))
+               goto mp_bdt_mem_err;
+
+       INIT_LIST_HEAD(&hba->ep_ofld_list);
+       INIT_LIST_HEAD(&hba->ep_destroy_list);
+       rwlock_init(&hba->ep_rdwr_lock);
+
+       hba->mtu_supported = BNX2I_MAX_MTU_SUPPORTED;
+
+       /* different values for 5708/5709/57710 */
+       hba->max_active_conns = ISCSI_MAX_CONNS_PER_HBA;
+
+       if (bnx2i_setup_free_cid_que(hba))
+               goto cid_que_err;
+
+       /* SQ/RQ/CQ size can be changed via sysfx interface */
+       if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type)) {
+               if (sq_size && sq_size <= BNX2I_5770X_SQ_WQES_MAX)
+                       hba->max_sqes = sq_size;
+               else
+                       hba->max_sqes = BNX2I_5770X_SQ_WQES_DEFAULT;
+       } else {        /* 5706/5708/5709 */
+               if (sq_size && sq_size <= BNX2I_570X_SQ_WQES_MAX)
+                       hba->max_sqes = sq_size;
+               else
+                       hba->max_sqes = BNX2I_570X_SQ_WQES_DEFAULT;
+       }
+
+       hba->max_rqes = rq_size;
+       hba->max_cqes = hba->max_sqes + rq_size;
+       if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type)) {
+               if (hba->max_cqes > BNX2I_5770X_CQ_WQES_MAX)
+                       hba->max_cqes = BNX2I_5770X_CQ_WQES_MAX;
+       } else if (hba->max_cqes > BNX2I_570X_CQ_WQES_MAX)
+               hba->max_cqes = BNX2I_570X_CQ_WQES_MAX;
+
+       hba->num_ccell = hba->max_sqes / 2;
+
+       spin_lock_init(&hba->lock);
+       mutex_init(&hba->net_dev_lock);
+
+       if (iscsi_host_add(shost, &hba->pcidev->dev))
+               goto free_dump_mem;
+       return hba;
+
+free_dump_mem:
+       bnx2i_release_free_cid_que(hba);
+cid_que_err:
+       bnx2i_free_mp_bdt(hba);
+mp_bdt_mem_err:
+       if (hba->regview) {
+               iounmap(hba->regview);
+               hba->regview = NULL;
+       }
+ioreg_map_err:
+       pci_dev_put(hba->pcidev);
+       scsi_host_put(shost);
+       return NULL;
+}
+
+/**
+ * bnx2i_free_hba- releases hba structure and resources held by the adapter
+ * @hba:       pointer to adapter instance
+ *
+ * free adapter structure and call various cleanup routines.
+ */
+void bnx2i_free_hba(struct bnx2i_hba *hba)
+{
+       struct Scsi_Host *shost = hba->shost;
+
+       iscsi_host_remove(shost);
+       INIT_LIST_HEAD(&hba->ep_ofld_list);
+       INIT_LIST_HEAD(&hba->ep_destroy_list);
+       pci_dev_put(hba->pcidev);
+
+       if (hba->regview) {
+               iounmap(hba->regview);
+               hba->regview = NULL;
+       }
+       bnx2i_free_mp_bdt(hba);
+       bnx2i_release_free_cid_que(hba);
+       iscsi_host_free(shost);
+}
+
+/**
+ * bnx2i_conn_free_login_resources - free DMA resources used for login process
+ * @hba:               pointer to adapter instance
+ * @bnx2i_conn:                iscsi connection pointer
+ *
+ * Login related resources, mostly BDT & payload DMA memory is freed
+ */
+static void bnx2i_conn_free_login_resources(struct bnx2i_hba *hba,
+                                           struct bnx2i_conn *bnx2i_conn)
+{
+       if (bnx2i_conn->gen_pdu.resp_bd_tbl) {
+               dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
+                                 bnx2i_conn->gen_pdu.resp_bd_tbl,
+                                 bnx2i_conn->gen_pdu.resp_bd_dma);
+               bnx2i_conn->gen_pdu.resp_bd_tbl = NULL;
+       }
+
+       if (bnx2i_conn->gen_pdu.req_bd_tbl) {
+               dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
+                                 bnx2i_conn->gen_pdu.req_bd_tbl,
+                                 bnx2i_conn->gen_pdu.req_bd_dma);
+               bnx2i_conn->gen_pdu.req_bd_tbl = NULL;
+       }
+
+       if (bnx2i_conn->gen_pdu.resp_buf) {
+               dma_free_coherent(&hba->pcidev->dev,
+                                 ISCSI_DEF_MAX_RECV_SEG_LEN,
+                                 bnx2i_conn->gen_pdu.resp_buf,
+                                 bnx2i_conn->gen_pdu.resp_dma_addr);
+               bnx2i_conn->gen_pdu.resp_buf = NULL;
+       }
+
+       if (bnx2i_conn->gen_pdu.req_buf) {
+               dma_free_coherent(&hba->pcidev->dev,
+                                 ISCSI_DEF_MAX_RECV_SEG_LEN,
+                                 bnx2i_conn->gen_pdu.req_buf,
+                                 bnx2i_conn->gen_pdu.req_dma_addr);
+               bnx2i_conn->gen_pdu.req_buf = NULL;
+       }
+}
+
+/**
+ * bnx2i_conn_alloc_login_resources - alloc DMA resources for login/nop.
+ * @hba:               pointer to adapter instance
+ * @bnx2i_conn:                iscsi connection pointer
+ *
+ * Mgmt task DNA resources are allocated in this routine.
+ */
+static int bnx2i_conn_alloc_login_resources(struct bnx2i_hba *hba,
+                                           struct bnx2i_conn *bnx2i_conn)
+{
+       /* Allocate memory for login request/response buffers */
+       bnx2i_conn->gen_pdu.req_buf =
+               dma_alloc_coherent(&hba->pcidev->dev,
+                                  ISCSI_DEF_MAX_RECV_SEG_LEN,
+                                  &bnx2i_conn->gen_pdu.req_dma_addr,
+                                  GFP_KERNEL);
+       if (bnx2i_conn->gen_pdu.req_buf == NULL)
+               goto login_req_buf_failure;
+
+       bnx2i_conn->gen_pdu.req_buf_size = 0;
+       bnx2i_conn->gen_pdu.req_wr_ptr = bnx2i_conn->gen_pdu.req_buf;
+
+       bnx2i_conn->gen_pdu.resp_buf =
+               dma_alloc_coherent(&hba->pcidev->dev,
+                                  ISCSI_DEF_MAX_RECV_SEG_LEN,
+                                  &bnx2i_conn->gen_pdu.resp_dma_addr,
+                                  GFP_KERNEL);
+       if (bnx2i_conn->gen_pdu.resp_buf == NULL)
+               goto login_resp_buf_failure;
+
+       bnx2i_conn->gen_pdu.resp_buf_size = ISCSI_DEF_MAX_RECV_SEG_LEN;
+       bnx2i_conn->gen_pdu.resp_wr_ptr = bnx2i_conn->gen_pdu.resp_buf;
+
+       bnx2i_conn->gen_pdu.req_bd_tbl =
+               dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE,
+                                  &bnx2i_conn->gen_pdu.req_bd_dma, GFP_KERNEL);
+       if (bnx2i_conn->gen_pdu.req_bd_tbl == NULL)
+               goto login_req_bd_tbl_failure;
+
+       bnx2i_conn->gen_pdu.resp_bd_tbl =
+               dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE,
+                                  &bnx2i_conn->gen_pdu.resp_bd_dma,
+                                  GFP_KERNEL);
+       if (bnx2i_conn->gen_pdu.resp_bd_tbl == NULL)
+               goto login_resp_bd_tbl_failure;
+
+       return 0;
+
+login_resp_bd_tbl_failure:
+       dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE,
+                         bnx2i_conn->gen_pdu.req_bd_tbl,
+                         bnx2i_conn->gen_pdu.req_bd_dma);
+       bnx2i_conn->gen_pdu.req_bd_tbl = NULL;
+
+login_req_bd_tbl_failure:
+       dma_free_coherent(&hba->pcidev->dev, ISCSI_DEF_MAX_RECV_SEG_LEN,
+                         bnx2i_conn->gen_pdu.resp_buf,
+                         bnx2i_conn->gen_pdu.resp_dma_addr);
+       bnx2i_conn->gen_pdu.resp_buf = NULL;
+login_resp_buf_failure:
+       dma_free_coherent(&hba->pcidev->dev, ISCSI_DEF_MAX_RECV_SEG_LEN,
+                         bnx2i_conn->gen_pdu.req_buf,
+                         bnx2i_conn->gen_pdu.req_dma_addr);
+       bnx2i_conn->gen_pdu.req_buf = NULL;
+login_req_buf_failure:
+       iscsi_conn_printk(KERN_ERR, bnx2i_conn->cls_conn->dd_data,
+                         "login resource alloc failed!!\n");
+       return -ENOMEM;
+
+}
+
+
+/**
+ * bnx2i_iscsi_prep_generic_pdu_bd - prepares BD table.
+ * @bnx2i_conn:                iscsi connection pointer
+ *
+ * Allocates buffers and BD tables before shipping requests to cnic
+ *     for PDUs prepared by 'iscsid' daemon
+ */
+static void bnx2i_iscsi_prep_generic_pdu_bd(struct bnx2i_conn *bnx2i_conn)
+{
+       struct iscsi_bd *bd_tbl;
+
+       bd_tbl = (struct iscsi_bd *) bnx2i_conn->gen_pdu.req_bd_tbl;
+
+       bd_tbl->buffer_addr_hi =
+               (u32) ((u64) bnx2i_conn->gen_pdu.req_dma_addr >> 32);
+       bd_tbl->buffer_addr_lo = (u32) bnx2i_conn->gen_pdu.req_dma_addr;
+       bd_tbl->buffer_length = bnx2i_conn->gen_pdu.req_wr_ptr -
+                               bnx2i_conn->gen_pdu.req_buf;
+       bd_tbl->reserved0 = 0;
+       bd_tbl->flags = ISCSI_BD_LAST_IN_BD_CHAIN |
+                       ISCSI_BD_FIRST_IN_BD_CHAIN;
+
+       bd_tbl = (struct iscsi_bd  *) bnx2i_conn->gen_pdu.resp_bd_tbl;
+       bd_tbl->buffer_addr_hi = (u64) bnx2i_conn->gen_pdu.resp_dma_addr >> 32;
+       bd_tbl->buffer_addr_lo = (u32) bnx2i_conn->gen_pdu.resp_dma_addr;
+       bd_tbl->buffer_length = ISCSI_DEF_MAX_RECV_SEG_LEN;
+       bd_tbl->reserved0 = 0;
+       bd_tbl->flags = ISCSI_BD_LAST_IN_BD_CHAIN |
+                       ISCSI_BD_FIRST_IN_BD_CHAIN;
+}
+
+
+/**
+ * bnx2i_iscsi_send_generic_request - called to send mgmt tasks.
+ * @task:      transport layer task pointer
+ *
+ * called to transmit PDUs prepared by the 'iscsid' daemon. iSCSI login,
+ *     Nop-out and Logout requests flow through this path.
+ */
+static int bnx2i_iscsi_send_generic_request(struct iscsi_task *task)
+{
+       struct bnx2i_cmd *cmd = task->dd_data;
+       struct bnx2i_conn *bnx2i_conn = cmd->conn;
+       int rc = 0;
+       char *buf;
+       int data_len;
+
+       bnx2i_iscsi_prep_generic_pdu_bd(bnx2i_conn);
+       switch (task->hdr->opcode & ISCSI_OPCODE_MASK) {
+       case ISCSI_OP_LOGIN:
+               bnx2i_send_iscsi_login(bnx2i_conn, task);
+               break;
+       case ISCSI_OP_NOOP_OUT:
+               data_len = bnx2i_conn->gen_pdu.req_buf_size;
+               buf = bnx2i_conn->gen_pdu.req_buf;
+               if (data_len)
+                       rc = bnx2i_send_iscsi_nopout(bnx2i_conn, task,
+                                                    RESERVED_ITT,
+                                                    buf, data_len, 1);
+               else
+                       rc = bnx2i_send_iscsi_nopout(bnx2i_conn, task,
+                                                    RESERVED_ITT,
+                                                    NULL, 0, 1);
+               break;
+       case ISCSI_OP_LOGOUT:
+               rc = bnx2i_send_iscsi_logout(bnx2i_conn, task);
+               break;
+       case ISCSI_OP_SCSI_TMFUNC:
+               rc = bnx2i_send_iscsi_tmf(bnx2i_conn, task);
+               break;
+       default:
+               iscsi_conn_printk(KERN_ALERT, bnx2i_conn->cls_conn->dd_data,
+                                 "send_gen: unsupported op 0x%x\n",
+                                 task->hdr->opcode);
+       }
+       return rc;
+}
+
+
+/**********************************************************************
+ *             SCSI-ML Interface
+ **********************************************************************/
+
+/**
+ * bnx2i_cpy_scsi_cdb - copies LUN & CDB fields in required format to sq wqe
+ * @sc:                SCSI-ML command pointer
+ * @cmd:       iscsi cmd pointer
+ */
+static void bnx2i_cpy_scsi_cdb(struct scsi_cmnd *sc, struct bnx2i_cmd *cmd)
+{
+       u32 dword;
+       int lpcnt;
+       u8 *srcp;
+       u32 *dstp;
+       u32 scsi_lun[2];
+
+       int_to_scsilun(sc->device->lun, (struct scsi_lun *) scsi_lun);
+       cmd->req.lun[0] = be32_to_cpu(scsi_lun[0]);
+       cmd->req.lun[1] = be32_to_cpu(scsi_lun[1]);
+
+       lpcnt = cmd->scsi_cmd->cmd_len / sizeof(dword);
+       srcp = (u8 *) sc->cmnd;
+       dstp = (u32 *) cmd->req.cdb;
+       while (lpcnt--) {
+               memcpy(&dword, (const void *) srcp, 4);
+               *dstp = cpu_to_be32(dword);
+               srcp += 4;
+               dstp++;
+       }
+       if (sc->cmd_len & 0x3) {
+               dword = (u32) srcp[0] | ((u32) srcp[1] << 8);
+               *dstp = cpu_to_be32(dword);
+       }
+}
+
+static void bnx2i_cleanup_task(struct iscsi_task *task)
+{
+       struct iscsi_conn *conn = task->conn;
+       struct bnx2i_conn *bnx2i_conn = conn->dd_data;
+       struct bnx2i_hba *hba = bnx2i_conn->hba;
+
+       /*
+        * mgmt task or cmd was never sent to us to transmit.
+        */
+       if (!task->sc || task->state == ISCSI_TASK_PENDING)
+               return;
+       /*
+        * need to clean-up task context to claim dma buffers
+        */
+       if (task->state == ISCSI_TASK_ABRT_TMF) {
+               bnx2i_send_cmd_cleanup_req(hba, task->dd_data);
+
+               spin_unlock_bh(&conn->session->lock);
+               wait_for_completion_timeout(&bnx2i_conn->cmd_cleanup_cmpl,
+                               msecs_to_jiffies(ISCSI_CMD_CLEANUP_TIMEOUT));
+               spin_lock_bh(&conn->session->lock);
+       }
+       bnx2i_iscsi_unmap_sg_list(task->dd_data);
+}
+
+/**
+ * bnx2i_mtask_xmit - transmit mtask to chip for further processing
+ * @conn:      transport layer conn structure pointer
+ * @task:      transport layer command structure pointer
+ */
+static int
+bnx2i_mtask_xmit(struct iscsi_conn *conn, struct iscsi_task *task)
+{
+       struct bnx2i_conn *bnx2i_conn = conn->dd_data;
+       struct bnx2i_cmd *cmd = task->dd_data;
+
+       memset(bnx2i_conn->gen_pdu.req_buf, 0, ISCSI_DEF_MAX_RECV_SEG_LEN);
+
+       bnx2i_setup_cmd_wqe_template(cmd);
+       bnx2i_conn->gen_pdu.req_buf_size = task->data_count;
+       if (task->data_count) {
+               memcpy(bnx2i_conn->gen_pdu.req_buf, task->data,
+                      task->data_count);
+               bnx2i_conn->gen_pdu.req_wr_ptr =
+                       bnx2i_conn->gen_pdu.req_buf + task->data_count;
+       }
+       cmd->conn = conn->dd_data;
+       cmd->scsi_cmd = NULL;
+       return bnx2i_iscsi_send_generic_request(task);
+}
+
+/**
+ * bnx2i_task_xmit - transmit iscsi command to chip for further processing
+ * @task:      transport layer command structure pointer
+ *
+ * maps SG buffers and send request to chip/firmware in the form of SQ WQE
+ */
+static int bnx2i_task_xmit(struct iscsi_task *task)
+{
+       struct iscsi_conn *conn = task->conn;
+       struct iscsi_session *session = conn->session;
+       struct Scsi_Host *shost = iscsi_session_to_shost(session->cls_session);
+       struct bnx2i_hba *hba = iscsi_host_priv(shost);
+       struct bnx2i_conn *bnx2i_conn = conn->dd_data;
+       struct scsi_cmnd *sc = task->sc;
+       struct bnx2i_cmd *cmd = task->dd_data;
+       struct iscsi_cmd *hdr = (struct iscsi_cmd *) task->hdr;
+
+       if (test_bit(ADAPTER_STATE_LINK_DOWN, &hba->adapter_state))
+               return -ENOTCONN;
+
+       if (!bnx2i_conn->is_bound)
+               return -ENOTCONN;
+
+       /*
+        * If there is no scsi_cmnd this must be a mgmt task
+        */
+       if (!sc)
+               return bnx2i_mtask_xmit(conn, task);
+
+       bnx2i_setup_cmd_wqe_template(cmd);
+       cmd->req.op_code = ISCSI_OP_SCSI_CMD;
+       cmd->conn = bnx2i_conn;
+       cmd->scsi_cmd = sc;
+       cmd->req.total_data_transfer_length = scsi_bufflen(sc);
+       cmd->req.cmd_sn = be32_to_cpu(hdr->cmdsn);
+
+       bnx2i_iscsi_map_sg_list(cmd);
+       bnx2i_cpy_scsi_cdb(sc, cmd);
+
+       cmd->req.op_attr = ISCSI_ATTR_SIMPLE;
+       if (sc->sc_data_direction == DMA_TO_DEVICE) {
+               cmd->req.op_attr |= ISCSI_CMD_REQUEST_WRITE;
+               cmd->req.itt = task->itt |
+                       (ISCSI_TASK_TYPE_WRITE << ISCSI_CMD_REQUEST_TYPE_SHIFT);
+               bnx2i_setup_write_cmd_bd_info(task);
+       } else {
+               if (scsi_bufflen(sc))
+                       cmd->req.op_attr |= ISCSI_CMD_REQUEST_READ;
+               cmd->req.itt = task->itt |
+                       (ISCSI_TASK_TYPE_READ << ISCSI_CMD_REQUEST_TYPE_SHIFT);
+       }
+
+       cmd->req.num_bds = cmd->io_tbl.bd_valid;
+       if (!cmd->io_tbl.bd_valid) {
+               cmd->req.bd_list_addr_lo = (u32) hba->mp_bd_dma;
+               cmd->req.bd_list_addr_hi = (u32) ((u64) hba->mp_bd_dma >> 32);
+               cmd->req.num_bds = 1;
+       }
+
+       bnx2i_send_iscsi_scsicmd(bnx2i_conn, cmd);
+       return 0;
+}
+
+/**
+ * bnx2i_session_create - create a new iscsi session
+ * @cmds_max:          max commands supported
+ * @qdepth:            scsi queue depth to support
+ * @initial_cmdsn:     initial iscsi CMDSN to be used for this session
+ *
+ * Creates a new iSCSI session instance on given device.
+ */
+static struct iscsi_cls_session *
+bnx2i_session_create(struct iscsi_endpoint *ep,
+                    uint16_t cmds_max, uint16_t qdepth,
+                    uint32_t initial_cmdsn)
+{
+       struct Scsi_Host *shost;
+       struct iscsi_cls_session *cls_session;
+       struct bnx2i_hba *hba;
+       struct bnx2i_endpoint *bnx2i_ep;
+
+       if (!ep) {
+               printk(KERN_ERR "bnx2i: missing ep.\n");
+               return NULL;
+       }
+
+       bnx2i_ep = ep->dd_data;
+       shost = bnx2i_ep->hba->shost;
+       hba = iscsi_host_priv(shost);
+       if (bnx2i_adapter_ready(hba))
+               return NULL;
+
+       /*
+        * user can override hw limit as long as it is within
+        * the min/max.
+        */
+       if (cmds_max > hba->max_sqes)
+               cmds_max = hba->max_sqes;
+       else if (cmds_max < BNX2I_SQ_WQES_MIN)
+               cmds_max = BNX2I_SQ_WQES_MIN;
+
+       cls_session = iscsi_session_setup(&bnx2i_iscsi_transport, shost,
+                                         cmds_max, sizeof(struct bnx2i_cmd),
+                                         initial_cmdsn, ISCSI_MAX_TARGET);
+       if (!cls_session)
+               return NULL;
+
+       if (bnx2i_setup_cmd_pool(hba, cls_session->dd_data))
+               goto session_teardown;
+       return cls_session;
+
+session_teardown:
+       iscsi_session_teardown(cls_session);
+       return NULL;
+}
+
+
+/**
+ * bnx2i_session_destroy - destroys iscsi session
+ * @cls_session:       pointer to iscsi cls session
+ *
+ * Destroys previously created iSCSI session instance and releases
+ *     all resources held by it
+ */
+static void bnx2i_session_destroy(struct iscsi_cls_session *cls_session)
+{
+       struct iscsi_session *session = cls_session->dd_data;
+       struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+       struct bnx2i_hba *hba = iscsi_host_priv(shost);
+
+       bnx2i_destroy_cmd_pool(hba, session);
+       iscsi_session_teardown(cls_session);
+}
+
+
+/**
+ * bnx2i_conn_create - create iscsi connection instance
+ * @cls_session:       pointer to iscsi cls session
+ * @cid:               iscsi cid as per rfc (not NX2's CID terminology)
+ *
+ * Creates a new iSCSI connection instance for a given session
+ */
+static struct iscsi_cls_conn *
+bnx2i_conn_create(struct iscsi_cls_session *cls_session, uint32_t cid)
+{
+       struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+       struct bnx2i_hba *hba = iscsi_host_priv(shost);
+       struct bnx2i_conn *bnx2i_conn;
+       struct iscsi_cls_conn *cls_conn;
+       struct iscsi_conn *conn;
+
+       cls_conn = iscsi_conn_setup(cls_session, sizeof(*bnx2i_conn),
+                                   cid);
+       if (!cls_conn)
+               return NULL;
+       conn = cls_conn->dd_data;
+
+       bnx2i_conn = conn->dd_data;
+       bnx2i_conn->cls_conn = cls_conn;
+       bnx2i_conn->hba = hba;
+       /* 'ep' ptr will be assigned in bind() call */
+       bnx2i_conn->ep = NULL;
+       init_completion(&bnx2i_conn->cmd_cleanup_cmpl);
+
+       if (bnx2i_conn_alloc_login_resources(hba, bnx2i_conn)) {
+               iscsi_conn_printk(KERN_ALERT, conn,
+                                 "conn_new: login resc alloc failed!!\n");
+               goto free_conn;
+       }
+
+       return cls_conn;
+
+free_conn:
+       iscsi_conn_teardown(cls_conn);
+       return NULL;
+}
+
+/**
+ * bnx2i_conn_bind - binds iscsi sess, conn and ep objects together
+ * @cls_session:       pointer to iscsi cls session
+ * @cls_conn:          pointer to iscsi cls conn
+ * @transport_fd:      64-bit EP handle
+ * @is_leading:                leading connection on this session?
+ *
+ * Binds together iSCSI session instance, iSCSI connection instance
+ *     and the TCP connection. This routine returns error code if
+ *     TCP connection does not belong on the device iSCSI sess/conn
+ *     is bound
+ */
+static int bnx2i_conn_bind(struct iscsi_cls_session *cls_session,
+                          struct iscsi_cls_conn *cls_conn,
+                          uint64_t transport_fd, int is_leading)
+{
+       struct iscsi_conn *conn = cls_conn->dd_data;
+       struct bnx2i_conn *bnx2i_conn = conn->dd_data;
+       struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+       struct bnx2i_hba *hba = iscsi_host_priv(shost);
+       struct bnx2i_endpoint *bnx2i_ep;
+       struct iscsi_endpoint *ep;
+       int ret_code;
+
+       ep = iscsi_lookup_endpoint(transport_fd);
+       if (!ep)
+               return -EINVAL;
+
+       bnx2i_ep = ep->dd_data;
+       if ((bnx2i_ep->state == EP_STATE_TCP_FIN_RCVD) ||
+           (bnx2i_ep->state == EP_STATE_TCP_RST_RCVD))
+               /* Peer disconnect via' FIN or RST */
+               return -EINVAL;
+
+       if (iscsi_conn_bind(cls_session, cls_conn, is_leading))
+               return -EINVAL;
+
+       if (bnx2i_ep->hba != hba) {
+               /* Error - TCP connection does not belong to this device
+                */
+               iscsi_conn_printk(KERN_ALERT, cls_conn->dd_data,
+                                 "conn bind, ep=0x%p (%s) does not",
+                                 bnx2i_ep, bnx2i_ep->hba->netdev->name);
+               iscsi_conn_printk(KERN_ALERT, cls_conn->dd_data,
+                                 "belong to hba (%s)\n",
+                                 hba->netdev->name);
+               return -EEXIST;
+       }
+
+       bnx2i_ep->conn = bnx2i_conn;
+       bnx2i_conn->ep = bnx2i_ep;
+       bnx2i_conn->iscsi_conn_cid = bnx2i_ep->ep_iscsi_cid;
+       bnx2i_conn->fw_cid = bnx2i_ep->ep_cid;
+       bnx2i_conn->is_bound = 1;
+
+       ret_code = bnx2i_bind_conn_to_iscsi_cid(hba, bnx2i_conn,
+                                               bnx2i_ep->ep_iscsi_cid);
+
+       /* 5706/5708/5709 FW takes RQ as full when initiated, but for 57710
+        * driver needs to explicitly replenish RQ index during setup.
+        */
+       if (test_bit(BNX2I_NX2_DEV_57710, &bnx2i_ep->hba->cnic_dev_type))
+               bnx2i_put_rq_buf(bnx2i_conn, 0);
+
+       bnx2i_arm_cq_event_coalescing(bnx2i_conn->ep, CNIC_ARM_CQE);
+       return ret_code;
+}
+
+
+/**
+ * bnx2i_conn_destroy - destroy iscsi connection instance & release resources
+ * @cls_conn:  pointer to iscsi cls conn
+ *
+ * Destroy an iSCSI connection instance and release memory resources held by
+ *     this connection
+ */
+static void bnx2i_conn_destroy(struct iscsi_cls_conn *cls_conn)
+{
+       struct iscsi_conn *conn = cls_conn->dd_data;
+       struct bnx2i_conn *bnx2i_conn = conn->dd_data;
+       struct Scsi_Host *shost;
+       struct bnx2i_hba *hba;
+
+       shost = iscsi_session_to_shost(iscsi_conn_to_session(cls_conn));
+       hba = iscsi_host_priv(shost);
+
+       bnx2i_conn_free_login_resources(hba, bnx2i_conn);
+       iscsi_conn_teardown(cls_conn);
+}
+
+
+/**
+ * bnx2i_conn_get_param - return iscsi connection parameter to caller
+ * @cls_conn:  pointer to iscsi cls conn
+ * @param:     parameter type identifier
+ * @buf:       buffer pointer
+ *
+ * returns iSCSI connection parameters
+ */
+static int bnx2i_conn_get_param(struct iscsi_cls_conn *cls_conn,
+                               enum iscsi_param param, char *buf)
+{
+       struct iscsi_conn *conn = cls_conn->dd_data;
+       struct bnx2i_conn *bnx2i_conn = conn->dd_data;
+       int len = 0;
+
+       switch (param) {
+       case ISCSI_PARAM_CONN_PORT:
+               if (bnx2i_conn->ep)
+                       len = sprintf(buf, "%hu\n",
+                                     bnx2i_conn->ep->cm_sk->dst_port);
+               break;
+       case ISCSI_PARAM_CONN_ADDRESS:
+               if (bnx2i_conn->ep)
+                       len = sprintf(buf, NIPQUAD_FMT "\n",
+                                     NIPQUAD(bnx2i_conn->ep->cm_sk->dst_ip));
+               break;
+       default:
+               return iscsi_conn_get_param(cls_conn, param, buf);
+       }
+
+       return len;
+}
+
+/**
+ * bnx2i_host_get_param - returns host (adapter) related parameters
+ * @shost:     scsi host pointer
+ * @param:     parameter type identifier
+ * @buf:       buffer pointer
+ */
+static int bnx2i_host_get_param(struct Scsi_Host *shost,
+                               enum iscsi_host_param param, char *buf)
+{
+       struct bnx2i_hba *hba = iscsi_host_priv(shost);
+       int len = 0;
+
+       switch (param) {
+       case ISCSI_HOST_PARAM_HWADDRESS:
+               len = sysfs_format_mac(buf, hba->cnic->mac_addr, 6);
+               break;
+       case ISCSI_HOST_PARAM_NETDEV_NAME:
+               len = sprintf(buf, "%s\n", hba->netdev->name);
+               break;
+       default:
+               return iscsi_host_get_param(shost, param, buf);
+       }
+       return len;
+}
+
+/**
+ * bnx2i_conn_start - completes iscsi connection migration to FFP
+ * @cls_conn:  pointer to iscsi cls conn
+ *
+ * last call in FFP migration to handover iscsi conn to the driver
+ */
+static int bnx2i_conn_start(struct iscsi_cls_conn *cls_conn)
+{
+       struct iscsi_conn *conn = cls_conn->dd_data;
+       struct bnx2i_conn *bnx2i_conn = conn->dd_data;
+
+       bnx2i_conn->ep->state = EP_STATE_ULP_UPDATE_START;
+       bnx2i_update_iscsi_conn(conn);
+
+       /*
+        * this should normally not sleep for a long time so it should
+        * not disrupt the caller.
+        */
+       bnx2i_conn->ep->ofld_timer.expires = 1 * HZ + jiffies;
+       bnx2i_conn->ep->ofld_timer.function = bnx2i_ep_ofld_timer;
+       bnx2i_conn->ep->ofld_timer.data = (unsigned long) bnx2i_conn->ep;
+       add_timer(&bnx2i_conn->ep->ofld_timer);
+       /* update iSCSI context for this conn, wait for CNIC to complete */
+       wait_event_interruptible(bnx2i_conn->ep->ofld_wait,
+                       bnx2i_conn->ep->state != EP_STATE_ULP_UPDATE_START);
+
+       if (signal_pending(current))
+               flush_signals(current);
+       del_timer_sync(&bnx2i_conn->ep->ofld_timer);
+
+       iscsi_conn_start(cls_conn);
+       return 0;
+}
+
+
+/**
+ * bnx2i_conn_get_stats - returns iSCSI stats
+ * @cls_conn:  pointer to iscsi cls conn
+ * @stats:     pointer to iscsi statistic struct
+ */
+static void bnx2i_conn_get_stats(struct iscsi_cls_conn *cls_conn,
+                                struct iscsi_stats *stats)
+{
+       struct iscsi_conn *conn = cls_conn->dd_data;
+
+       stats->txdata_octets = conn->txdata_octets;
+       stats->rxdata_octets = conn->rxdata_octets;
+       stats->scsicmd_pdus = conn->scsicmd_pdus_cnt;
+       stats->dataout_pdus = conn->dataout_pdus_cnt;
+       stats->scsirsp_pdus = conn->scsirsp_pdus_cnt;
+       stats->datain_pdus = conn->datain_pdus_cnt;
+       stats->r2t_pdus = conn->r2t_pdus_cnt;
+       stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt;
+       stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt;
+       stats->custom_length = 3;
+       strcpy(stats->custom[2].desc, "eh_abort_cnt");
+       stats->custom[2].value = conn->eh_abort_cnt;
+       stats->digest_err = 0;
+       stats->timeout_err = 0;
+       stats->custom_length = 0;
+}
+
+
+/**
+ * bnx2i_check_route - checks if target IP route belongs to one of NX2 devices
+ * @dst_addr:  target IP address
+ *
+ * check if route resolves to BNX2 device
+ */
+static struct bnx2i_hba *bnx2i_check_route(struct sockaddr *dst_addr)
+{
+       struct sockaddr_in *desti = (struct sockaddr_in *) dst_addr;
+       struct bnx2i_hba *hba;
+       struct cnic_dev *cnic = NULL;
+
+       bnx2i_reg_dev_all();
+
+       hba = get_adapter_list_head();
+       if (hba && hba->cnic)
+               cnic = hba->cnic->cm_select_dev(desti, CNIC_ULP_ISCSI);
+       if (!cnic) {
+               printk(KERN_ALERT "bnx2i: no route,"
+                      "can't connect using cnic\n");
+               goto no_nx2_route;
+       }
+       hba = bnx2i_find_hba_for_cnic(cnic);
+       if (!hba)
+               goto no_nx2_route;
+
+       if (bnx2i_adapter_ready(hba)) {
+               printk(KERN_ALERT "bnx2i: check route, hba not found\n");
+               goto no_nx2_route;
+       }
+       if (hba->netdev->mtu > hba->mtu_supported) {
+               printk(KERN_ALERT "bnx2i: %s network i/f mtu is set to %d\n",
+                                 hba->netdev->name, hba->netdev->mtu);
+               printk(KERN_ALERT "bnx2i: iSCSI HBA can support mtu of %d\n",
+                                 hba->mtu_supported);
+               goto no_nx2_route;
+       }
+       return hba;
+no_nx2_route:
+       return NULL;
+}
+
+
+/**
+ * bnx2i_tear_down_conn - tear down iscsi/tcp connection and free resources
+ * @hba:       pointer to adapter instance
+ * @ep:                endpoint (transport indentifier) structure
+ *
+ * destroys cm_sock structure and on chip iscsi context
+ */
+static int bnx2i_tear_down_conn(struct bnx2i_hba *hba,
+                                struct bnx2i_endpoint *ep)
+{
+       if (test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic))
+               hba->cnic->cm_destroy(ep->cm_sk);
+
+       if (test_bit(ADAPTER_STATE_GOING_DOWN, &ep->hba->adapter_state))
+               ep->state = EP_STATE_DISCONN_COMPL;
+
+       if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type) &&
+           ep->state == EP_STATE_DISCONN_TIMEDOUT) {
+               printk(KERN_ALERT "bnx2i - ERROR - please submit GRC Dump,"
+                                 " NW/PCIe trace, driver msgs to developers"
+                                 " for analysis\n");
+               return 1;
+       }
+
+       ep->state = EP_STATE_CLEANUP_START;
+       init_timer(&ep->ofld_timer);
+       ep->ofld_timer.expires = 10*HZ + jiffies;
+       ep->ofld_timer.function = bnx2i_ep_ofld_timer;
+       ep->ofld_timer.data = (unsigned long) ep;
+       add_timer(&ep->ofld_timer);
+
+       bnx2i_ep_destroy_list_add(hba, ep);
+
+       /* destroy iSCSI context, wait for it to complete */
+       bnx2i_send_conn_destroy(hba, ep);
+       wait_event_interruptible(ep->ofld_wait,
+                                (ep->state != EP_STATE_CLEANUP_START));
+
+       if (signal_pending(current))
+               flush_signals(current);
+       del_timer_sync(&ep->ofld_timer);
+
+       bnx2i_ep_destroy_list_del(hba, ep);
+
+       if (ep->state != EP_STATE_CLEANUP_CMPL)
+               /* should never happen */
+               printk(KERN_ALERT "bnx2i - conn destroy failed\n");
+
+       return 0;
+}
+
+
+/**
+ * bnx2i_ep_connect - establish TCP connection to target portal
+ * @shost:             scsi host
+ * @dst_addr:          target IP address
+ * @non_blocking:      blocking or non-blocking call
+ *
+ * this routine initiates the TCP/IP connection by invoking Option-2 i/f
+ *     with l5_core and the CNIC. This is a multi-step process of resolving
+ *     route to target, create a iscsi connection context, handshaking with
+ *     CNIC module to create/initialize the socket struct and finally
+ *     sending down option-2 request to complete TCP 3-way handshake
+ */
+static struct iscsi_endpoint *bnx2i_ep_connect(struct Scsi_Host *shost,
+                                              struct sockaddr *dst_addr,
+                                              int non_blocking)
+{
+       u32 iscsi_cid = BNX2I_CID_RESERVED;
+       struct sockaddr_in *desti = (struct sockaddr_in *) dst_addr;
+       struct sockaddr_in6 *desti6;
+       struct bnx2i_endpoint *bnx2i_ep;
+       struct bnx2i_hba *hba;
+       struct cnic_dev *cnic;
+       struct cnic_sockaddr saddr;
+       struct iscsi_endpoint *ep;
+       int rc = 0;
+
+       if (shost)
+               /* driver is given scsi host to work with */
+               hba = iscsi_host_priv(shost);
+       else
+               /*
+                * check if the given destination can be reached through
+                * a iscsi capable NetXtreme2 device
+                */
+               hba = bnx2i_check_route(dst_addr);
+       if (!hba) {
+               rc = -ENOMEM;
+               goto check_busy;
+       }
+
+       cnic = hba->cnic;
+       ep = bnx2i_alloc_ep(hba);
+       if (!ep) {
+               rc = -ENOMEM;
+               goto check_busy;
+       }
+       bnx2i_ep = ep->dd_data;
+
+       mutex_lock(&hba->net_dev_lock);
+       if (bnx2i_adapter_ready(hba)) {
+               rc = -EPERM;
+               goto net_if_down;
+       }
+
+       bnx2i_ep->state = EP_STATE_IDLE;
+       bnx2i_ep->ep_iscsi_cid = (u16) -1;
+       bnx2i_ep->num_active_cmds = 0;
+       iscsi_cid = bnx2i_alloc_iscsi_cid(hba);
+       if (iscsi_cid == -1) {
+               printk(KERN_ALERT "alloc_ep: unable to allocate iscsi cid\n");
+               rc = -ENOMEM;
+               goto iscsi_cid_err;
+       }
+       bnx2i_ep->hba_age = hba->age;
+
+       rc = bnx2i_alloc_qp_resc(hba, bnx2i_ep);
+       if (rc != 0) {
+               printk(KERN_ALERT "bnx2i: ep_conn, alloc QP resc error\n");
+               rc = -ENOMEM;
+               goto qp_resc_err;
+       }
+
+       bnx2i_ep->ep_iscsi_cid = (u16)iscsi_cid;
+       bnx2i_ep->state = EP_STATE_OFLD_START;
+       bnx2i_ep_ofld_list_add(hba, bnx2i_ep);
+
+       init_timer(&bnx2i_ep->ofld_timer);
+       bnx2i_ep->ofld_timer.expires = 2 * HZ + jiffies;
+       bnx2i_ep->ofld_timer.function = bnx2i_ep_ofld_timer;
+       bnx2i_ep->ofld_timer.data = (unsigned long) bnx2i_ep;
+       add_timer(&bnx2i_ep->ofld_timer);
+
+       bnx2i_send_conn_ofld_req(hba, bnx2i_ep);
+
+       /* Wait for CNIC hardware to setup conn context and return 'cid' */
+       wait_event_interruptible(bnx2i_ep->ofld_wait,
+                                bnx2i_ep->state != EP_STATE_OFLD_START);
+
+       if (signal_pending(current))
+               flush_signals(current);
+       del_timer_sync(&bnx2i_ep->ofld_timer);
+
+       bnx2i_ep_ofld_list_del(hba, bnx2i_ep);
+
+       if (bnx2i_ep->state != EP_STATE_OFLD_COMPL) {
+               rc = -ENOSPC;
+               goto conn_failed;
+       }
+
+       rc = cnic->cm_create(cnic, CNIC_ULP_ISCSI, bnx2i_ep->ep_cid,
+                            iscsi_cid, &bnx2i_ep->cm_sk, bnx2i_ep);
+       if (rc) {
+               rc = -EINVAL;
+               goto conn_failed;
+       }
+
+       bnx2i_ep->cm_sk->rcv_buf = 256 * 1024;
+       bnx2i_ep->cm_sk->snd_buf = 256 * 1024;
+       clear_bit(SK_TCP_TIMESTAMP, &bnx2i_ep->cm_sk->tcp_flags);
+
+       memset(&saddr, 0, sizeof(saddr));
+       if (dst_addr->sa_family == AF_INET) {
+               desti = (struct sockaddr_in *) dst_addr;
+               saddr.remote.v4 = *desti;
+               saddr.local.v4.sin_family = desti->sin_family;
+       } else if (dst_addr->sa_family == AF_INET6) {
+               desti6 = (struct sockaddr_in6 *) dst_addr;
+               saddr.remote.v6 = *desti6;
+               saddr.local.v6.sin6_family = desti6->sin6_family;
+       }
+
+       bnx2i_ep->timestamp = jiffies;
+       bnx2i_ep->state = EP_STATE_CONNECT_START;
+       if (!test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) {
+               rc = -EINVAL;
+               goto conn_failed;
+       } else
+               rc = cnic->cm_connect(bnx2i_ep->cm_sk, &saddr);
+
+       if (rc)
+               goto release_ep;
+
+       if (bnx2i_map_ep_dbell_regs(bnx2i_ep))
+               goto release_ep;
+       mutex_unlock(&hba->net_dev_lock);
+       return ep;
+
+release_ep:
+       if (bnx2i_tear_down_conn(hba, bnx2i_ep)) {
+               mutex_unlock(&hba->net_dev_lock);
+               return ERR_PTR(rc);
+       }
+conn_failed:
+net_if_down:
+iscsi_cid_err:
+       bnx2i_free_qp_resc(hba, bnx2i_ep);
+qp_resc_err:
+       bnx2i_free_ep(ep);
+       mutex_unlock(&hba->net_dev_lock);
+check_busy:
+       bnx2i_unreg_dev_all();
+       return ERR_PTR(rc);
+}
+
+
+/**
+ * bnx2i_ep_poll - polls for TCP connection establishement
+ * @ep:                        TCP connection (endpoint) handle
+ * @timeout_ms:                timeout value in milli secs
+ *
+ * polls for TCP connect request to complete
+ */
+static int bnx2i_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)
+{
+       struct bnx2i_endpoint *bnx2i_ep;
+       int rc = 0;
+
+       bnx2i_ep = ep->dd_data;
+       if ((bnx2i_ep->state == EP_STATE_IDLE) ||
+           (bnx2i_ep->state == EP_STATE_CONNECT_FAILED) ||
+           (bnx2i_ep->state == EP_STATE_OFLD_FAILED))
+               return -1;
+       if (bnx2i_ep->state == EP_STATE_CONNECT_COMPL)
+               return 1;
+
+       rc = wait_event_interruptible_timeout(bnx2i_ep->ofld_wait,
+                                             ((bnx2i_ep->state ==
+                                               EP_STATE_OFLD_FAILED) ||
+                                              (bnx2i_ep->state ==
+                                               EP_STATE_CONNECT_FAILED) ||
+                                              (bnx2i_ep->state ==
+                                               EP_STATE_CONNECT_COMPL)),
+                                             msecs_to_jiffies(timeout_ms));
+       if (!rc || (bnx2i_ep->state == EP_STATE_OFLD_FAILED))
+               rc = -1;
+
+       if (rc > 0)
+               return 1;
+       else if (!rc)
+               return 0;       /* timeout */
+       else
+               return rc;
+}
+
+
+/**
+ * bnx2i_ep_tcp_conn_active - check EP state transition
+ * @ep:                endpoint pointer
+ *
+ * check if underlying TCP connection is active
+ */
+static int bnx2i_ep_tcp_conn_active(struct bnx2i_endpoint *bnx2i_ep)
+{
+       int ret;
+       int cnic_dev_10g = 0;
+
+       if (test_bit(BNX2I_NX2_DEV_57710, &bnx2i_ep->hba->cnic_dev_type))
+               cnic_dev_10g = 1;
+
+       switch (bnx2i_ep->state) {
+       case EP_STATE_CONNECT_START:
+       case EP_STATE_CLEANUP_FAILED:
+       case EP_STATE_OFLD_FAILED:
+       case EP_STATE_DISCONN_TIMEDOUT:
+               ret = 0;
+               break;
+       case EP_STATE_CONNECT_COMPL:
+       case EP_STATE_ULP_UPDATE_START:
+       case EP_STATE_ULP_UPDATE_COMPL:
+       case EP_STATE_TCP_FIN_RCVD:
+       case EP_STATE_ULP_UPDATE_FAILED:
+               ret = 1;
+               break;
+       case EP_STATE_TCP_RST_RCVD:
+               ret = 0;
+               break;
+       case EP_STATE_CONNECT_FAILED:
+               if (cnic_dev_10g)
+                       ret = 1;
+               else
+                       ret = 0;
+               break;
+       default:
+               ret = 0;
+       }
+
+       return ret;
+}
+
+
+/**
+ * bnx2i_ep_disconnect - executes TCP connection teardown process
+ * @ep:                TCP connection (endpoint) handle
+ *
+ * executes  TCP connection teardown process
+ */
+static void bnx2i_ep_disconnect(struct iscsi_endpoint *ep)
+{
+       struct bnx2i_endpoint *bnx2i_ep;
+       struct bnx2i_conn *bnx2i_conn = NULL;
+       struct iscsi_session *session = NULL;
+       struct iscsi_conn *conn;
+       struct cnic_dev *cnic;
+       struct bnx2i_hba *hba;
+
+       bnx2i_ep = ep->dd_data;
+
+       /* driver should not attempt connection cleanup untill TCP_CONNECT
+        * completes either successfully or fails. Timeout is 9-secs, so
+        * wait for it to complete
+        */
+       while ((bnx2i_ep->state == EP_STATE_CONNECT_START) &&
+               !time_after(jiffies, bnx2i_ep->timestamp + (12 * HZ)))
+               msleep(250);
+
+       if (bnx2i_ep->conn) {
+               bnx2i_conn = bnx2i_ep->conn;
+               conn = bnx2i_conn->cls_conn->dd_data;
+               session = conn->session;
+
+               spin_lock_bh(&session->lock);
+               bnx2i_conn->is_bound = 0;
+               spin_unlock_bh(&session->lock);
+       }
+
+       hba = bnx2i_ep->hba;
+       if (bnx2i_ep->state == EP_STATE_IDLE)
+               goto return_bnx2i_ep;
+       cnic = hba->cnic;
+
+       mutex_lock(&hba->net_dev_lock);
+
+       if (!test_bit(ADAPTER_STATE_UP, &hba->adapter_state))
+               goto free_resc;
+       if (bnx2i_ep->hba_age != hba->age)
+               goto free_resc;
+
+       if (!bnx2i_ep_tcp_conn_active(bnx2i_ep))
+               goto destory_conn;
+
+       bnx2i_ep->state = EP_STATE_DISCONN_START;
+
+       init_timer(&bnx2i_ep->ofld_timer);
+       bnx2i_ep->ofld_timer.expires = 10*HZ + jiffies;
+       bnx2i_ep->ofld_timer.function = bnx2i_ep_ofld_timer;
+       bnx2i_ep->ofld_timer.data = (unsigned long) bnx2i_ep;
+       add_timer(&bnx2i_ep->ofld_timer);
+
+       if (test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) {
+               int close = 0;
+
+               if (session) {
+                       spin_lock_bh(&session->lock);
+                       if (session->state == ISCSI_STATE_LOGGING_OUT)
+                               close = 1;
+                       spin_unlock_bh(&session->lock);
+               }
+               if (close)
+                       cnic->cm_close(bnx2i_ep->cm_sk);
+               else
+                       cnic->cm_abort(bnx2i_ep->cm_sk);
+       } else
+               goto free_resc;
+
+       /* wait for option-2 conn teardown */
+       wait_event_interruptible(bnx2i_ep->ofld_wait,
+                                bnx2i_ep->state != EP_STATE_DISCONN_START);
+
+       if (signal_pending(current))
+               flush_signals(current);
+       del_timer_sync(&bnx2i_ep->ofld_timer);
+
+destory_conn:
+       if (bnx2i_tear_down_conn(hba, bnx2i_ep)) {
+               mutex_unlock(&hba->net_dev_lock);
+               return;
+       }
+free_resc:
+       mutex_unlock(&hba->net_dev_lock);
+       bnx2i_free_qp_resc(hba, bnx2i_ep);
+return_bnx2i_ep:
+       if (bnx2i_conn)
+               bnx2i_conn->ep = NULL;
+
+       bnx2i_free_ep(ep);
+
+       if (!hba->ofld_conns_active)
+               bnx2i_unreg_dev_all();
+}
+
+
+/**
+ * bnx2i_nl_set_path - ISCSI_UEVENT_PATH_UPDATE user message handler
+ * @buf:       pointer to buffer containing iscsi path message
+ *
+ */
+static int bnx2i_nl_set_path(struct Scsi_Host *shost, struct iscsi_path *params)
+{
+       struct bnx2i_hba *hba = iscsi_host_priv(shost);
+       char *buf = (char *) params;
+       u16 len = sizeof(*params);
+
+       /* handled by cnic driver */
+       hba->cnic->iscsi_nl_msg_recv(hba->cnic, ISCSI_UEVENT_PATH_UPDATE, buf,
+                                    len);
+
+       return 0;
+}
+
+
+/*
+ * 'Scsi_Host_Template' structure and 'iscsi_tranport' structure template
+ * used while registering with the scsi host and iSCSI transport module.
+ */
+static struct scsi_host_template bnx2i_host_template = {
+       .module                 = THIS_MODULE,
+       .name                   = "Broadcom Offload iSCSI Initiator",
+       .proc_name              = "bnx2i",
+       .queuecommand           = iscsi_queuecommand,
+       .eh_abort_handler       = iscsi_eh_abort,
+       .eh_device_reset_handler = iscsi_eh_device_reset,
+       .eh_target_reset_handler = iscsi_eh_target_reset,
+       .can_queue              = 1024,
+       .max_sectors            = 127,
+       .cmd_per_lun            = 32,
+       .this_id                = -1,
+       .use_clustering         = ENABLE_CLUSTERING,
+       .sg_tablesize           = ISCSI_MAX_BDS_PER_CMD,
+       .shost_attrs            = bnx2i_dev_attributes,
+};
+
+struct iscsi_transport bnx2i_iscsi_transport = {
+       .owner                  = THIS_MODULE,
+       .name                   = "bnx2i",
+       .caps                   = CAP_RECOVERY_L0 | CAP_HDRDGST |
+                                 CAP_MULTI_R2T | CAP_DATADGST |
+                                 CAP_DATA_PATH_OFFLOAD,
+       .param_mask             = ISCSI_MAX_RECV_DLENGTH |
+                                 ISCSI_MAX_XMIT_DLENGTH |
+                                 ISCSI_HDRDGST_EN |
+                                 ISCSI_DATADGST_EN |
+                                 ISCSI_INITIAL_R2T_EN |
+                                 ISCSI_MAX_R2T |
+                                 ISCSI_IMM_DATA_EN |
+                                 ISCSI_FIRST_BURST |
+                                 ISCSI_MAX_BURST |
+                                 ISCSI_PDU_INORDER_EN |
+                                 ISCSI_DATASEQ_INORDER_EN |
+                                 ISCSI_ERL |
+                                 ISCSI_CONN_PORT |
+                                 ISCSI_CONN_ADDRESS |
+                                 ISCSI_EXP_STATSN |
+                                 ISCSI_PERSISTENT_PORT |
+                                 ISCSI_PERSISTENT_ADDRESS |
+                                 ISCSI_TARGET_NAME | ISCSI_TPGT |
+                                 ISCSI_USERNAME | ISCSI_PASSWORD |
+                                 ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
+                                 ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
+                                 ISCSI_LU_RESET_TMO |
+                                 ISCSI_PING_TMO | ISCSI_RECV_TMO |
+                                 ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
+       .host_param_mask        = ISCSI_HOST_HWADDRESS | ISCSI_HOST_NETDEV_NAME,
+       .create_session         = bnx2i_session_create,
+       .destroy_session        = bnx2i_session_destroy,
+       .create_conn            = bnx2i_conn_create,
+       .bind_conn              = bnx2i_conn_bind,
+       .destroy_conn           = bnx2i_conn_destroy,
+       .set_param              = iscsi_set_param,
+       .get_conn_param         = bnx2i_conn_get_param,
+       .get_session_param      = iscsi_session_get_param,
+       .get_host_param         = bnx2i_host_get_param,
+       .start_conn             = bnx2i_conn_start,
+       .stop_conn              = iscsi_conn_stop,
+       .send_pdu               = iscsi_conn_send_pdu,
+       .xmit_task              = bnx2i_task_xmit,
+       .get_stats              = bnx2i_conn_get_stats,
+       /* TCP connect - disconnect - option-2 interface calls */
+       .ep_connect             = bnx2i_ep_connect,
+       .ep_poll                = bnx2i_ep_poll,
+       .ep_disconnect          = bnx2i_ep_disconnect,
+       .set_path               = bnx2i_nl_set_path,
+       /* Error recovery timeout call */
+       .session_recovery_timedout = iscsi_session_recovery_timedout,
+       .cleanup_task           = bnx2i_cleanup_task,
+};
diff --git a/drivers/scsi/bnx2i/bnx2i_sysfs.c b/drivers/scsi/bnx2i/bnx2i_sysfs.c
new file mode 100644 (file)
index 0000000..96426b7
--- /dev/null
@@ -0,0 +1,142 @@
+/* bnx2i_sysfs.c: Broadcom NetXtreme II iSCSI driver.
+ *
+ * Copyright (c) 2004 - 2009 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: Anil Veerabhadrappa (anilgv@broadcom.com)
+ */
+
+#include "bnx2i.h"
+
+/**
+ * bnx2i_dev_to_hba - maps dev pointer to adapter struct
+ * @dev:       device pointer
+ *
+ * Map device to hba structure
+ */
+static inline struct bnx2i_hba *bnx2i_dev_to_hba(struct device *dev)
+{
+       struct Scsi_Host *shost = class_to_shost(dev);
+       return iscsi_host_priv(shost);
+}
+
+
+/**
+ * bnx2i_show_sq_info - return(s currently configured send queue (SQ) size
+ * @dev:       device pointer
+ * @buf:       buffer to return current SQ size parameter
+ *
+ * Returns current SQ size parameter, this paramater determines the number
+ * outstanding iSCSI commands supported on a connection
+ */
+static ssize_t bnx2i_show_sq_info(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       struct bnx2i_hba *hba = bnx2i_dev_to_hba(dev);
+
+       return sprintf(buf, "0x%x\n", hba->max_sqes);
+}
+
+
+/**
+ * bnx2i_set_sq_info - update send queue (SQ) size parameter
+ * @dev:       device pointer
+ * @buf:       buffer to return current SQ size parameter
+ * @count:     parameter buffer size
+ *
+ * Interface for user to change shared queue size allocated for each conn
+ * Must be within SQ limits and a power of 2. For the latter this is needed
+ * because of how libiscsi preallocates tasks.
+ */
+static ssize_t bnx2i_set_sq_info(struct device *dev,
+                                struct device_attribute *attr,
+                                const char *buf, size_t count)
+{
+       struct bnx2i_hba *hba = bnx2i_dev_to_hba(dev);
+       u32 val;
+       int max_sq_size;
+
+       if (hba->ofld_conns_active)
+               goto skip_config;
+
+       if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type))
+               max_sq_size = BNX2I_5770X_SQ_WQES_MAX;
+       else
+               max_sq_size = BNX2I_570X_SQ_WQES_MAX;
+
+       if (sscanf(buf, " 0x%x ", &val) > 0) {
+               if ((val >= BNX2I_SQ_WQES_MIN) && (val <= max_sq_size) &&
+                   (is_power_of_2(val)))
+                       hba->max_sqes = val;
+       }
+
+       return count;
+
+skip_config:
+       printk(KERN_ERR "bnx2i: device busy, cannot change SQ size\n");
+       return 0;
+}
+
+
+/**
+ * bnx2i_show_ccell_info - returns command cell (HQ) size
+ * @dev:       device pointer
+ * @buf:       buffer to return current SQ size parameter
+ *
+ * returns per-connection TCP history queue size parameter
+ */
+static ssize_t bnx2i_show_ccell_info(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       struct bnx2i_hba *hba = bnx2i_dev_to_hba(dev);
+
+       return sprintf(buf, "0x%x\n", hba->num_ccell);
+}
+
+
+/**
+ * bnx2i_get_link_state - set command cell (HQ) size
+ * @dev:       device pointer
+ * @buf:       buffer to return current SQ size parameter
+ * @count:     parameter buffer size
+ *
+ * updates per-connection TCP history queue size parameter
+ */
+static ssize_t bnx2i_set_ccell_info(struct device *dev,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t count)
+{
+       u32 val;
+       struct bnx2i_hba *hba = bnx2i_dev_to_hba(dev);
+
+       if (hba->ofld_conns_active)
+               goto skip_config;
+
+       if (sscanf(buf, " 0x%x ", &val) > 0) {
+               if ((val >= BNX2I_CCELLS_MIN) &&
+                   (val <= BNX2I_CCELLS_MAX)) {
+                       hba->num_ccell = val;
+               }
+       }
+
+       return count;
+
+skip_config:
+       printk(KERN_ERR "bnx2i: device busy, cannot change CCELL size\n");
+       return 0;
+}
+
+
+static DEVICE_ATTR(sq_size, S_IRUGO | S_IWUSR,
+                  bnx2i_show_sq_info, bnx2i_set_sq_info);
+static DEVICE_ATTR(num_ccell, S_IRUGO | S_IWUSR,
+                  bnx2i_show_ccell_info, bnx2i_set_ccell_info);
+
+struct device_attribute *bnx2i_dev_attributes[] = {
+       &dev_attr_sq_size,
+       &dev_attr_num_ccell,
+       NULL
+};
index 59b0958d2d116d0a85b2bbd4c045535a8028fdb5..e3133b58e5944d5c2c36a2bda0d2ed6a161076a5 100644 (file)
@@ -144,7 +144,6 @@ struct cxgb3i_adapter *cxgb3i_adapter_find_by_tdev(struct t3cdev *);
 void cxgb3i_adapter_open(struct t3cdev *);
 void cxgb3i_adapter_close(struct t3cdev *);
 
-struct cxgb3i_hba *cxgb3i_hba_find_by_netdev(struct net_device *);
 struct cxgb3i_hba *cxgb3i_hba_host_add(struct cxgb3i_adapter *,
                                       struct net_device *);
 void cxgb3i_hba_host_remove(struct cxgb3i_hba *);
index 9212400b9b13ecde501a5fe75831855d742fe86e..74369a3f963b66bdbdbaa0b71bdd6f848a074048 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <linux/inet.h>
 #include <linux/crypto.h>
+#include <net/dst.h>
 #include <net/tcp.h>
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_device.h>
@@ -178,7 +179,7 @@ void cxgb3i_adapter_close(struct t3cdev *t3dev)
  * cxgb3i_hba_find_by_netdev - find the cxgb3i_hba structure via net_device
  * @t3dev: t3cdev adapter
  */
-struct cxgb3i_hba *cxgb3i_hba_find_by_netdev(struct net_device *ndev)
+static struct cxgb3i_hba *cxgb3i_hba_find_by_netdev(struct net_device *ndev)
 {
        struct cxgb3i_adapter *snic;
        int i;
@@ -261,20 +262,27 @@ void cxgb3i_hba_host_remove(struct cxgb3i_hba *hba)
 
 /**
  * cxgb3i_ep_connect - establish TCP connection to target portal
+ * @shost:             scsi host to use
  * @dst_addr:          target IP address
  * @non_blocking:      blocking or non-blocking call
  *
  * Initiates a TCP/IP connection to the dst_addr
  */
-static struct iscsi_endpoint *cxgb3i_ep_connect(struct sockaddr *dst_addr,
+static struct iscsi_endpoint *cxgb3i_ep_connect(struct Scsi_Host *shost,
+                                               struct sockaddr *dst_addr,
                                                int non_blocking)
 {
        struct iscsi_endpoint *ep;
        struct cxgb3i_endpoint *cep;
-       struct cxgb3i_hba *hba;
+       struct cxgb3i_hba *hba = NULL;
        struct s3_conn *c3cn = NULL;
        int err = 0;
 
+       if (shost)
+               hba = iscsi_host_priv(shost);
+
+       cxgb3i_api_debug("shost 0x%p, hba 0x%p.\n", shost, hba);
+
        c3cn = cxgb3i_c3cn_create();
        if (!c3cn) {
                cxgb3i_log_info("ep connect OOM.\n");
@@ -282,17 +290,27 @@ static struct iscsi_endpoint *cxgb3i_ep_connect(struct sockaddr *dst_addr,
                goto release_conn;
        }
 
-       err = cxgb3i_c3cn_connect(c3cn, (struct sockaddr_in *)dst_addr);
+       err = cxgb3i_c3cn_connect(hba ? hba->ndev : NULL, c3cn,
+                                (struct sockaddr_in *)dst_addr);
        if (err < 0) {
                cxgb3i_log_info("ep connect failed.\n");
                goto release_conn;
        }
+
        hba = cxgb3i_hba_find_by_netdev(c3cn->dst_cache->dev);
        if (!hba) {
                err = -ENOSPC;
                cxgb3i_log_info("NOT going through cxgbi device.\n");
                goto release_conn;
        }
+
+       if (shost && hba != iscsi_host_priv(shost)) {
+               err = -ENOSPC;
+               cxgb3i_log_info("Could not connect through request host%u\n",
+                               shost->host_no);
+               goto release_conn;
+       }
+
        if (c3cn_is_closing(c3cn)) {
                err = -ENOSPC;
                cxgb3i_log_info("ep connect unable to connect.\n");
index e11c9c180f39d322127f37b9aae88a5409b04863..c1d5be4adf9c6cdfe7fa530c93821f22f091ba97 100644 (file)
@@ -1479,12 +1479,13 @@ static struct net_device *cxgb3_egress_dev(struct net_device *root_dev,
        return NULL;
 }
 
-static struct rtable *find_route(__be32 saddr, __be32 daddr,
+static struct rtable *find_route(struct net_device *dev,
+                                __be32 saddr, __be32 daddr,
                                 __be16 sport, __be16 dport)
 {
        struct rtable *rt;
        struct flowi fl = {
-               .oif = 0,
+               .oif = dev ? dev->ifindex : 0,
                .nl_u = {
                         .ip4_u = {
                                   .daddr = daddr,
@@ -1573,36 +1574,40 @@ out_err:
  *
  * return 0 if active open request is sent, < 0 otherwise.
  */
-int cxgb3i_c3cn_connect(struct s3_conn *c3cn, struct sockaddr_in *usin)
+int cxgb3i_c3cn_connect(struct net_device *dev, struct s3_conn *c3cn,
+                       struct sockaddr_in *usin)
 {
        struct rtable *rt;
-       struct net_device *dev;
        struct cxgb3i_sdev_data *cdata;
        struct t3cdev *cdev;
        __be32 sipv4;
        int err;
 
+       c3cn_conn_debug("c3cn 0x%p, dev 0x%p.\n", c3cn, dev);
+
        if (usin->sin_family != AF_INET)
                return -EAFNOSUPPORT;
 
        c3cn->daddr.sin_port = usin->sin_port;
        c3cn->daddr.sin_addr.s_addr = usin->sin_addr.s_addr;
 
-       rt = find_route(c3cn->saddr.sin_addr.s_addr,
+       rt = find_route(dev, c3cn->saddr.sin_addr.s_addr,
                        c3cn->daddr.sin_addr.s_addr,
                        c3cn->saddr.sin_port,
                        c3cn->daddr.sin_port);
        if (rt == NULL) {
-               c3cn_conn_debug("NO route to 0x%x, port %u.\n",
+               c3cn_conn_debug("NO route to 0x%x, port %u, dev %s.\n",
                                c3cn->daddr.sin_addr.s_addr,
-                               ntohs(c3cn->daddr.sin_port));
+                               ntohs(c3cn->daddr.sin_port),
+                               dev ? dev->name : "any");
                return -ENETUNREACH;
        }
 
        if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {
-               c3cn_conn_debug("multi-cast route to 0x%x, port %u.\n",
+               c3cn_conn_debug("multi-cast route to 0x%x, port %u, dev %s.\n",
                                c3cn->daddr.sin_addr.s_addr,
-                               ntohs(c3cn->daddr.sin_port));
+                               ntohs(c3cn->daddr.sin_port),
+                               dev ? dev->name : "any");
                ip_rt_put(rt);
                return -ENETUNREACH;
        }
index ebfca960c0a9d8358236c8658c084e44bda4ed3d..6a1d86b1fafea325c9bcbc415419732273b8568c 100644 (file)
@@ -169,7 +169,8 @@ void cxgb3i_sdev_add(struct t3cdev *, struct cxgb3_client *);
 void cxgb3i_sdev_remove(struct t3cdev *);
 
 struct s3_conn *cxgb3i_c3cn_create(void);
-int cxgb3i_c3cn_connect(struct s3_conn *, struct sockaddr_in *);
+int cxgb3i_c3cn_connect(struct net_device *, struct s3_conn *,
+                       struct sockaddr_in *);
 void cxgb3i_c3cn_rx_credits(struct s3_conn *, int);
 int cxgb3i_c3cn_send_pdus(struct s3_conn *, struct sk_buff *);
 void cxgb3i_c3cn_release(struct s3_conn *);
index 43b8c51e98d090d5cdf1f333fc449730e6002d1e..fd0544f7da81700d2e586387b0f20ae1b0cd64d2 100644 (file)
@@ -561,6 +561,12 @@ static int rdac_check_sense(struct scsi_device *sdev,
        struct rdac_dh_data *h = get_rdac_data(sdev);
        switch (sense_hdr->sense_key) {
        case NOT_READY:
+               if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x01)
+                       /* LUN Not Ready - Logical Unit Not Ready and is in
+                       * the process of becoming ready
+                       * Just retry.
+                       */
+                       return ADD_TO_MLQUEUE;
                if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x81)
                        /* LUN Not Ready - Storage firmware incompatible
                         * Manual code synchonisation required.
index 03e1926f40b523c231033e809031ad9b1d138ec7..e606b4829d4430684a973c743d721bf81ba66d1f 100644 (file)
@@ -54,7 +54,6 @@ MODULE_LICENSE("GPL v2");
 /* fcoe host list */
 LIST_HEAD(fcoe_hostlist);
 DEFINE_RWLOCK(fcoe_hostlist_lock);
-DEFINE_TIMER(fcoe_timer, NULL, 0, 0);
 DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
 
 /* Function Prototypes */
@@ -71,7 +70,7 @@ static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *);
 static int fcoe_hostlist_add(const struct fc_lport *);
 static int fcoe_hostlist_remove(const struct fc_lport *);
 
-static int fcoe_check_wait_queue(struct fc_lport *);
+static void fcoe_check_wait_queue(struct fc_lport *, struct sk_buff *);
 static int fcoe_device_notification(struct notifier_block *, ulong, void *);
 static void fcoe_dev_setup(void);
 static void fcoe_dev_cleanup(void);
@@ -146,6 +145,7 @@ static int fcoe_lport_config(struct fc_lport *lp)
        lp->link_up = 0;
        lp->qfull = 0;
        lp->max_retry_count = 3;
+       lp->max_rport_retry_count = 3;
        lp->e_d_tov = 2 * 1000; /* FC-FS default */
        lp->r_a_tov = 2 * 2 * 1000;
        lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
@@ -166,6 +166,18 @@ static int fcoe_lport_config(struct fc_lport *lp)
        return 0;
 }
 
+/**
+ * fcoe_queue_timer() - fcoe queue timer
+ * @lp: the fc_lport pointer
+ *
+ * Calls fcoe_check_wait_queue on timeout
+ *
+ */
+static void fcoe_queue_timer(ulong lp)
+{
+       fcoe_check_wait_queue((struct fc_lport *)lp, NULL);
+}
+
 /**
  * fcoe_netdev_config() - Set up netdev for SW FCoE
  * @lp : ptr to the fc_lport
@@ -236,6 +248,7 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
        }
        skb_queue_head_init(&fc->fcoe_pending_queue);
        fc->fcoe_pending_queue_active = 0;
+       setup_timer(&fc->timer, fcoe_queue_timer, (unsigned long)lp);
 
        /* setup Source Mac Address */
        memcpy(fc->ctlr.ctl_src_addr, fc->real_dev->dev_addr,
@@ -386,6 +399,9 @@ static int fcoe_if_destroy(struct net_device *netdev)
        /* Free existing skbs */
        fcoe_clean_pending_queue(lp);
 
+       /* Stop the timer */
+       del_timer_sync(&fc->timer);
+
        /* Free memory used by statistical counters */
        fc_lport_free_stats(lp);
 
@@ -988,7 +1004,7 @@ u32 fcoe_fc_crc(struct fc_frame *fp)
  */
 int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
 {
-       int wlen, rc = 0;
+       int wlen;
        u32 crc;
        struct ethhdr *eh;
        struct fcoe_crc_eof *cp;
@@ -1021,8 +1037,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
        sof = fr_sof(fp);
        eof = fr_eof(fp);
 
-       elen = (fc->real_dev->priv_flags & IFF_802_1Q_VLAN) ?
-               sizeof(struct vlan_ethhdr) : sizeof(struct ethhdr);
+       elen = sizeof(struct ethhdr);
        hlen = sizeof(struct fcoe_hdr);
        tlen = sizeof(struct fcoe_crc_eof);
        wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE;
@@ -1107,18 +1122,9 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
        /* send down to lld */
        fr_dev(fp) = lp;
        if (fc->fcoe_pending_queue.qlen)
-               rc = fcoe_check_wait_queue(lp);
-
-       if (rc == 0)
-               rc = fcoe_start_io(skb);
-
-       if (rc) {
-               spin_lock_bh(&fc->fcoe_pending_queue.lock);
-               __skb_queue_tail(&fc->fcoe_pending_queue, skb);
-               spin_unlock_bh(&fc->fcoe_pending_queue.lock);
-               if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
-                       lp->qfull = 1;
-       }
+               fcoe_check_wait_queue(lp, skb);
+       else if (fcoe_start_io(skb))
+               fcoe_check_wait_queue(lp, skb);
 
        return 0;
 }
@@ -1267,32 +1273,6 @@ int fcoe_percpu_receive_thread(void *arg)
        return 0;
 }
 
-/**
- * fcoe_watchdog() - fcoe timer callback
- * @vp:
- *
- * This checks the pending queue length for fcoe and set lport qfull
- * if the FCOE_MAX_QUEUE_DEPTH is reached. This is done for all fc_lport on the
- * fcoe_hostlist.
- *
- * Returns: 0 for success
- */
-void fcoe_watchdog(ulong vp)
-{
-       struct fcoe_softc *fc;
-
-       read_lock(&fcoe_hostlist_lock);
-       list_for_each_entry(fc, &fcoe_hostlist, list) {
-               if (fc->ctlr.lp)
-                       fcoe_check_wait_queue(fc->ctlr.lp);
-       }
-       read_unlock(&fcoe_hostlist_lock);
-
-       fcoe_timer.expires = jiffies + (1 * HZ);
-       add_timer(&fcoe_timer);
-}
-
-
 /**
  * fcoe_check_wait_queue() - attempt to clear the transmit backlog
  * @lp: the fc_lport
@@ -1305,16 +1285,17 @@ void fcoe_watchdog(ulong vp)
  * The wait_queue is used when the skb transmit fails. skb will go
  * in the wait_queue which will be emptied by the timer function or
  * by the next skb transmit.
- *
- * Returns: 0 for success
  */
-static int fcoe_check_wait_queue(struct fc_lport *lp)
+static void fcoe_check_wait_queue(struct fc_lport *lp, struct sk_buff *skb)
 {
        struct fcoe_softc *fc = lport_priv(lp);
-       struct sk_buff *skb;
-       int rc = -1;
+       int rc;
 
        spin_lock_bh(&fc->fcoe_pending_queue.lock);
+
+       if (skb)
+               __skb_queue_tail(&fc->fcoe_pending_queue, skb);
+
        if (fc->fcoe_pending_queue_active)
                goto out;
        fc->fcoe_pending_queue_active = 1;
@@ -1340,23 +1321,26 @@ static int fcoe_check_wait_queue(struct fc_lport *lp)
 
        if (fc->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH)
                lp->qfull = 0;
+       if (fc->fcoe_pending_queue.qlen && !timer_pending(&fc->timer))
+               mod_timer(&fc->timer, jiffies + 2);
        fc->fcoe_pending_queue_active = 0;
-       rc = fc->fcoe_pending_queue.qlen;
 out:
+       if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
+               lp->qfull = 1;
        spin_unlock_bh(&fc->fcoe_pending_queue.lock);
-       return rc;
+       return;
 }
 
 /**
  * fcoe_dev_setup() - setup link change notification interface
  */
-static void fcoe_dev_setup()
+static void fcoe_dev_setup(void)
 {
        register_netdevice_notifier(&fcoe_notifier);
 }
 
 /**
- * fcoe_dev_setup() - cleanup link change notification interface
+ * fcoe_dev_cleanup() - cleanup link change notification interface
  */
 static void fcoe_dev_cleanup(void)
 {
@@ -1815,10 +1799,6 @@ static int __init fcoe_init(void)
        /* Setup link change notification */
        fcoe_dev_setup();
 
-       setup_timer(&fcoe_timer, fcoe_watchdog, 0);
-
-       mod_timer(&fcoe_timer, jiffies + (10 * HZ));
-
        fcoe_if_init();
 
        return 0;
@@ -1844,9 +1824,6 @@ static void __exit fcoe_exit(void)
 
        fcoe_dev_cleanup();
 
-       /* Stop the timer */
-       del_timer_sync(&fcoe_timer);
-
        /* releases the associated fcoe hosts */
        list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
                fcoe_if_destroy(fc->real_dev);
index 917aae88689733feb40d49963928d5d6bf6863f9..a1eb8c1988b07584bed2a10280a6efbf768f8813 100644 (file)
@@ -61,6 +61,7 @@ struct fcoe_softc {
        struct packet_type  fip_packet_type;
        struct sk_buff_head fcoe_pending_queue;
        u8      fcoe_pending_queue_active;
+       struct timer_list timer;                /* queue timer */
        struct fcoe_ctlr ctlr;
 };
 
index 62ba0f39c6bd267f2e7a5fc30354792434569b29..929411880e4b16c010ca7ca3d7369fddec6abd88 100644 (file)
@@ -213,7 +213,7 @@ static void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf)
        sol->desc.size.fd_size = htons(fcoe_size);
 
        skb_put(skb, sizeof(*sol));
-       skb->protocol = htons(ETH_P_802_3);
+       skb->protocol = htons(ETH_P_FIP);
        skb_reset_mac_header(skb);
        skb_reset_network_header(skb);
        fip->send(fip, skb);
@@ -365,7 +365,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
        }
 
        skb_put(skb, len);
-       skb->protocol = htons(ETH_P_802_3);
+       skb->protocol = htons(ETH_P_FIP);
        skb_reset_mac_header(skb);
        skb_reset_network_header(skb);
        fip->send(fip, skb);
@@ -424,7 +424,7 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip,
        if (dtype != ELS_FLOGI)
                memcpy(mac->fd_mac, fip->data_src_addr, ETH_ALEN);
 
-       skb->protocol = htons(ETH_P_802_3);
+       skb->protocol = htons(ETH_P_FIP);
        skb_reset_mac_header(skb);
        skb_reset_network_header(skb);
        return 0;
@@ -447,14 +447,10 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
        u16 old_xid;
        u8 op;
 
-       if (fip->state == FIP_ST_NON_FIP)
-               return 0;
-
        fh = (struct fc_frame_header *)skb->data;
        op = *(u8 *)(fh + 1);
 
-       switch (op) {
-       case ELS_FLOGI:
+       if (op == ELS_FLOGI) {
                old_xid = fip->flogi_oxid;
                fip->flogi_oxid = ntohs(fh->fh_ox_id);
                if (fip->state == FIP_ST_AUTO) {
@@ -466,6 +462,15 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
                        fip->map_dest = 1;
                        return 0;
                }
+               if (fip->state == FIP_ST_NON_FIP)
+                       fip->map_dest = 1;
+       }
+
+       if (fip->state == FIP_ST_NON_FIP)
+               return 0;
+
+       switch (op) {
+       case ELS_FLOGI:
                op = FIP_DT_FLOGI;
                break;
        case ELS_FDISC:
index 32ef6b87d8953142aa8bdd24092f7f2d77540c00..a84072865fc2615a7a9fcce47c48b94fe05f4592 100644 (file)
@@ -680,6 +680,7 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
        }
 
        lp->max_retry_count = fnic->config.flogi_retries;
+       lp->max_rport_retry_count = fnic->config.plogi_retries;
        lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
                              FCP_SPPF_CONF_COMPL);
        if (fnic->config.flags & VFCF_FCP_SEQ_LVL_ERR)
index 59349a316e137facdd9b7f76b412ac14f23190ae..1258da34fbc251bf779f85d1a2fdd0a015e51a34 100644 (file)
@@ -152,6 +152,7 @@ static int gdth_get_info(char *buffer,char **start,off_t offset,int length,
                          struct Scsi_Host *host, gdth_ha_str *ha)
 {
     int size = 0,len = 0;
+    int hlen;
     off_t begin = 0,pos = 0;
     int id, i, j, k, sec, flag;
     int no_mdrv = 0, drv_no, is_mirr;
@@ -192,11 +193,11 @@ static int gdth_get_info(char *buffer,char **start,off_t offset,int length,
     if (reserve_list[0] == 0xff)
         strcpy(hrec, "--");
     else {
-        sprintf(hrec, "%d", reserve_list[0]);
+        hlen = sprintf(hrec, "%d", reserve_list[0]);
         for (i = 1;  i < MAX_RES_ARGS; i++) {
             if (reserve_list[i] == 0xff) 
                 break;
-            sprintf(hrec,"%s,%d", hrec, reserve_list[i]);
+            hlen += snprintf(hrec + hlen , 161 - hlen, ",%d", reserve_list[i]);
         }
     }
     size = sprintf(buffer+len,
index ea4abee7a2a95e8d783da6b7f82da2bc7f39aab8..b4b805e8d7db7aaad258c75f9d8fd30088ff228c 100644 (file)
@@ -110,7 +110,7 @@ static const struct {
        { IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_DEAD, DID_ERROR, 0, 1, "transport dead" },
        { IBMVFC_FABRIC_MAPPED, IBMVFC_CONFIG_ERROR, DID_ERROR, 1, 1, "configuration error" },
        { IBMVFC_FABRIC_MAPPED, IBMVFC_NAME_SERVER_FAIL, DID_ERROR, 1, 1, "name server failure" },
-       { IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_HALTED, DID_REQUEUE, 0, 0, "link halted" },
+       { IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_HALTED, DID_REQUEUE, 1, 0, "link halted" },
        { IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_GENERAL, DID_OK, 1, 0, "general transport error" },
 
        { IBMVFC_VIOS_FAILURE, IBMVFC_CRQ_FAILURE, DID_REQUEUE, 1, 1, "CRQ failure" },
@@ -143,6 +143,7 @@ static void ibmvfc_npiv_login(struct ibmvfc_host *);
 static void ibmvfc_tgt_send_prli(struct ibmvfc_target *);
 static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *);
 static void ibmvfc_tgt_query_target(struct ibmvfc_target *);
+static void ibmvfc_npiv_logout(struct ibmvfc_host *);
 
 static const char *unknown_error = "unknown error";
 
@@ -275,7 +276,7 @@ static int ibmvfc_get_err_result(struct ibmvfc_cmd *vfc_cmd)
        int fc_rsp_len = rsp->fcp_rsp_len;
 
        if ((rsp->flags & FCP_RSP_LEN_VALID) &&
-           ((!fc_rsp_len && fc_rsp_len != 4 && fc_rsp_len != 8) ||
+           ((fc_rsp_len && fc_rsp_len != 4 && fc_rsp_len != 8) ||
             rsp->data.info.rsp_code))
                return DID_ERROR << 16;
 
@@ -431,6 +432,8 @@ static void ibmvfc_set_tgt_action(struct ibmvfc_target *tgt,
        case IBMVFC_TGT_ACTION_DEL_RPORT:
                break;
        default:
+               if (action == IBMVFC_TGT_ACTION_DEL_RPORT)
+                       tgt->add_rport = 0;
                tgt->action = action;
                break;
        }
@@ -475,6 +478,10 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
                if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT)
                        vhost->action = action;
                break;
+       case IBMVFC_HOST_ACTION_LOGO_WAIT:
+               if (vhost->action == IBMVFC_HOST_ACTION_LOGO)
+                       vhost->action = action;
+               break;
        case IBMVFC_HOST_ACTION_INIT_WAIT:
                if (vhost->action == IBMVFC_HOST_ACTION_INIT)
                        vhost->action = action;
@@ -483,7 +490,7 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
                switch (vhost->action) {
                case IBMVFC_HOST_ACTION_INIT_WAIT:
                case IBMVFC_HOST_ACTION_NONE:
-               case IBMVFC_HOST_ACTION_TGT_ADD:
+               case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
                        vhost->action = action;
                        break;
                default:
@@ -494,11 +501,11 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
                if (vhost->action == IBMVFC_HOST_ACTION_ALLOC_TGTS)
                        vhost->action = action;
                break;
+       case IBMVFC_HOST_ACTION_LOGO:
        case IBMVFC_HOST_ACTION_INIT:
        case IBMVFC_HOST_ACTION_TGT_DEL:
        case IBMVFC_HOST_ACTION_QUERY_TGTS:
        case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
-       case IBMVFC_HOST_ACTION_TGT_ADD:
        case IBMVFC_HOST_ACTION_NONE:
        default:
                vhost->action = action;
@@ -576,7 +583,7 @@ static void ibmvfc_init_host(struct ibmvfc_host *vhost, int relogin)
                }
 
                list_for_each_entry(tgt, &vhost->targets, queue)
-                       tgt->need_login = 1;
+                       ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
                scsi_block_requests(vhost->host);
                ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
                vhost->job_step = ibmvfc_npiv_login;
@@ -646,6 +653,7 @@ static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost)
        } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
 
        vhost->state = IBMVFC_NO_CRQ;
+       vhost->logged_in = 0;
        dma_unmap_single(vhost->dev, crq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL);
        free_page((unsigned long)crq->msgs);
 }
@@ -692,6 +700,7 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
        } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
 
        vhost->state = IBMVFC_NO_CRQ;
+       vhost->logged_in = 0;
        ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
 
        /* Clean out the queue */
@@ -807,10 +816,10 @@ static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code)
 }
 
 /**
- * __ibmvfc_reset_host - Reset the connection to the server (no locking)
+ * ibmvfc_hard_reset_host - Reset the connection to the server by breaking the CRQ
  * @vhost:     struct ibmvfc host to reset
  **/
-static void __ibmvfc_reset_host(struct ibmvfc_host *vhost)
+static void ibmvfc_hard_reset_host(struct ibmvfc_host *vhost)
 {
        int rc;
 
@@ -826,9 +835,25 @@ static void __ibmvfc_reset_host(struct ibmvfc_host *vhost)
 }
 
 /**
- * ibmvfc_reset_host - Reset the connection to the server
+ * __ibmvfc_reset_host - Reset the connection to the server (no locking)
  * @vhost:     struct ibmvfc host to reset
  **/
+static void __ibmvfc_reset_host(struct ibmvfc_host *vhost)
+{
+       if (vhost->logged_in && vhost->action != IBMVFC_HOST_ACTION_LOGO_WAIT &&
+           !ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
+               scsi_block_requests(vhost->host);
+               ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_LOGO);
+               vhost->job_step = ibmvfc_npiv_logout;
+               wake_up(&vhost->work_wait_q);
+       } else
+               ibmvfc_hard_reset_host(vhost);
+}
+
+/**
+ * ibmvfc_reset_host - Reset the connection to the server
+ * @vhost:     ibmvfc host struct
+ **/
 static void ibmvfc_reset_host(struct ibmvfc_host *vhost)
 {
        unsigned long flags;
@@ -842,9 +867,13 @@ static void ibmvfc_reset_host(struct ibmvfc_host *vhost)
  * ibmvfc_retry_host_init - Retry host initialization if allowed
  * @vhost:     ibmvfc host struct
  *
+ * Returns: 1 if init will be retried / 0 if not
+ *
  **/
-static void ibmvfc_retry_host_init(struct ibmvfc_host *vhost)
+static int ibmvfc_retry_host_init(struct ibmvfc_host *vhost)
 {
+       int retry = 0;
+
        if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) {
                vhost->delay_init = 1;
                if (++vhost->init_retries > IBMVFC_MAX_HOST_INIT_RETRIES) {
@@ -853,11 +882,14 @@ static void ibmvfc_retry_host_init(struct ibmvfc_host *vhost)
                        ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
                } else if (vhost->init_retries == IBMVFC_MAX_HOST_INIT_RETRIES)
                        __ibmvfc_reset_host(vhost);
-               else
+               else {
                        ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
+                       retry = 1;
+               }
        }
 
        wake_up(&vhost->work_wait_q);
+       return retry;
 }
 
 /**
@@ -1137,8 +1169,9 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
        login_info->partition_num = vhost->partition_number;
        login_info->vfc_frame_version = 1;
        login_info->fcp_version = 3;
+       login_info->flags = IBMVFC_FLUSH_ON_HALT;
        if (vhost->client_migrated)
-               login_info->flags = IBMVFC_CLIENT_MIGRATED;
+               login_info->flags |= IBMVFC_CLIENT_MIGRATED;
 
        login_info->max_cmds = max_requests + IBMVFC_NUM_INTERNAL_REQ;
        login_info->capabilities = IBMVFC_CAN_MIGRATE;
@@ -1451,6 +1484,27 @@ static void ibmvfc_log_error(struct ibmvfc_event *evt)
                    rsp->flags, rsp_code, scsi_get_resid(cmnd), rsp->scsi_status);
 }
 
+/**
+ * ibmvfc_relogin - Log back into the specified device
+ * @sdev:      scsi device struct
+ *
+ **/
+static void ibmvfc_relogin(struct scsi_device *sdev)
+{
+       struct ibmvfc_host *vhost = shost_priv(sdev->host);
+       struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+       struct ibmvfc_target *tgt;
+
+       list_for_each_entry(tgt, &vhost->targets, queue) {
+               if (rport == tgt->rport) {
+                       ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+                       break;
+               }
+       }
+
+       ibmvfc_reinit_host(vhost);
+}
+
 /**
  * ibmvfc_scsi_done - Handle responses from commands
  * @evt:       ibmvfc event to be handled
@@ -1483,7 +1537,7 @@ static void ibmvfc_scsi_done(struct ibmvfc_event *evt)
                        if ((rsp->flags & FCP_SNS_LEN_VALID) && rsp->fcp_sense_len && rsp_len <= 8)
                                memcpy(cmnd->sense_buffer, rsp->data.sense + rsp_len, sense_len);
                        if ((vfc_cmd->status & IBMVFC_VIOS_FAILURE) && (vfc_cmd->error == IBMVFC_PLOGI_REQUIRED))
-                               ibmvfc_reinit_host(evt->vhost);
+                               ibmvfc_relogin(cmnd->device);
 
                        if (!cmnd->result && (!scsi_get_resid(cmnd) || (rsp->flags & FCP_RESID_OVER)))
                                cmnd->result = (DID_ERROR << 16);
@@ -2148,13 +2202,31 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
                                struct ibmvfc_host *vhost)
 {
        const char *desc = ibmvfc_get_ae_desc(crq->event);
+       struct ibmvfc_target *tgt;
 
        ibmvfc_log(vhost, 3, "%s event received. scsi_id: %llx, wwpn: %llx,"
                   " node_name: %llx\n", desc, crq->scsi_id, crq->wwpn, crq->node_name);
 
        switch (crq->event) {
-       case IBMVFC_AE_LINK_UP:
        case IBMVFC_AE_RESUME:
+               switch (crq->link_state) {
+               case IBMVFC_AE_LS_LINK_DOWN:
+                       ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
+                       break;
+               case IBMVFC_AE_LS_LINK_DEAD:
+                       ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+                       break;
+               case IBMVFC_AE_LS_LINK_UP:
+               case IBMVFC_AE_LS_LINK_BOUNCED:
+               default:
+                       vhost->events_to_log |= IBMVFC_AE_LINKUP;
+                       vhost->delay_init = 1;
+                       __ibmvfc_reset_host(vhost);
+                       break;
+               };
+
+               break;
+       case IBMVFC_AE_LINK_UP:
                vhost->events_to_log |= IBMVFC_AE_LINKUP;
                vhost->delay_init = 1;
                __ibmvfc_reset_host(vhost);
@@ -2168,9 +2240,23 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
        case IBMVFC_AE_SCN_NPORT:
        case IBMVFC_AE_SCN_GROUP:
                vhost->events_to_log |= IBMVFC_AE_RSCN;
+               ibmvfc_reinit_host(vhost);
+               break;
        case IBMVFC_AE_ELS_LOGO:
        case IBMVFC_AE_ELS_PRLO:
        case IBMVFC_AE_ELS_PLOGI:
+               list_for_each_entry(tgt, &vhost->targets, queue) {
+                       if (!crq->scsi_id && !crq->wwpn && !crq->node_name)
+                               break;
+                       if (crq->scsi_id && tgt->scsi_id != crq->scsi_id)
+                               continue;
+                       if (crq->wwpn && tgt->ids.port_name != crq->wwpn)
+                               continue;
+                       if (crq->node_name && tgt->ids.node_name != crq->node_name)
+                               continue;
+                       ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+               }
+
                ibmvfc_reinit_host(vhost);
                break;
        case IBMVFC_AE_LINK_DOWN:
@@ -2222,6 +2308,7 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost)
                return;
        case IBMVFC_CRQ_XPORT_EVENT:
                vhost->state = IBMVFC_NO_CRQ;
+               vhost->logged_in = 0;
                ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
                if (crq->format == IBMVFC_PARTITION_MIGRATED) {
                        /* We need to re-setup the interpartition connection */
@@ -2299,7 +2386,7 @@ static int ibmvfc_scan_finished(struct Scsi_Host *shost, unsigned long time)
                done = 1;
        }
 
-       if (vhost->state != IBMVFC_NO_CRQ && vhost->action == IBMVFC_HOST_ACTION_NONE)
+       if (vhost->scan_complete)
                done = 1;
        spin_unlock_irqrestore(shost->host_lock, flags);
        return done;
@@ -2434,14 +2521,6 @@ static ssize_t ibmvfc_show_host_partition_name(struct device *dev,
                        vhost->login_buf->resp.partition_name);
 }
 
-static struct device_attribute ibmvfc_host_partition_name = {
-       .attr = {
-               .name = "partition_name",
-               .mode = S_IRUGO,
-       },
-       .show = ibmvfc_show_host_partition_name,
-};
-
 static ssize_t ibmvfc_show_host_device_name(struct device *dev,
                                            struct device_attribute *attr, char *buf)
 {
@@ -2452,14 +2531,6 @@ static ssize_t ibmvfc_show_host_device_name(struct device *dev,
                        vhost->login_buf->resp.device_name);
 }
 
-static struct device_attribute ibmvfc_host_device_name = {
-       .attr = {
-               .name = "device_name",
-               .mode = S_IRUGO,
-       },
-       .show = ibmvfc_show_host_device_name,
-};
-
 static ssize_t ibmvfc_show_host_loc_code(struct device *dev,
                                         struct device_attribute *attr, char *buf)
 {
@@ -2470,14 +2541,6 @@ static ssize_t ibmvfc_show_host_loc_code(struct device *dev,
                        vhost->login_buf->resp.port_loc_code);
 }
 
-static struct device_attribute ibmvfc_host_loc_code = {
-       .attr = {
-               .name = "port_loc_code",
-               .mode = S_IRUGO,
-       },
-       .show = ibmvfc_show_host_loc_code,
-};
-
 static ssize_t ibmvfc_show_host_drc_name(struct device *dev,
                                         struct device_attribute *attr, char *buf)
 {
@@ -2488,14 +2551,6 @@ static ssize_t ibmvfc_show_host_drc_name(struct device *dev,
                        vhost->login_buf->resp.drc_name);
 }
 
-static struct device_attribute ibmvfc_host_drc_name = {
-       .attr = {
-               .name = "drc_name",
-               .mode = S_IRUGO,
-       },
-       .show = ibmvfc_show_host_drc_name,
-};
-
 static ssize_t ibmvfc_show_host_npiv_version(struct device *dev,
                                             struct device_attribute *attr, char *buf)
 {
@@ -2504,13 +2559,13 @@ static ssize_t ibmvfc_show_host_npiv_version(struct device *dev,
        return snprintf(buf, PAGE_SIZE, "%d\n", vhost->login_buf->resp.version);
 }
 
-static struct device_attribute ibmvfc_host_npiv_version = {
-       .attr = {
-               .name = "npiv_version",
-               .mode = S_IRUGO,
-       },
-       .show = ibmvfc_show_host_npiv_version,
-};
+static ssize_t ibmvfc_show_host_capabilities(struct device *dev,
+                                            struct device_attribute *attr, char *buf)
+{
+       struct Scsi_Host *shost = class_to_shost(dev);
+       struct ibmvfc_host *vhost = shost_priv(shost);
+       return snprintf(buf, PAGE_SIZE, "%llx\n", vhost->login_buf->resp.capabilities);
+}
 
 /**
  * ibmvfc_show_log_level - Show the adapter's error logging level
@@ -2556,14 +2611,14 @@ static ssize_t ibmvfc_store_log_level(struct device *dev,
        return strlen(buf);
 }
 
-static struct device_attribute ibmvfc_log_level_attr = {
-       .attr = {
-               .name =         "log_level",
-               .mode =         S_IRUGO | S_IWUSR,
-       },
-       .show = ibmvfc_show_log_level,
-       .store = ibmvfc_store_log_level
-};
+static DEVICE_ATTR(partition_name, S_IRUGO, ibmvfc_show_host_partition_name, NULL);
+static DEVICE_ATTR(device_name, S_IRUGO, ibmvfc_show_host_device_name, NULL);
+static DEVICE_ATTR(port_loc_code, S_IRUGO, ibmvfc_show_host_loc_code, NULL);
+static DEVICE_ATTR(drc_name, S_IRUGO, ibmvfc_show_host_drc_name, NULL);
+static DEVICE_ATTR(npiv_version, S_IRUGO, ibmvfc_show_host_npiv_version, NULL);
+static DEVICE_ATTR(capabilities, S_IRUGO, ibmvfc_show_host_capabilities, NULL);
+static DEVICE_ATTR(log_level, S_IRUGO | S_IWUSR,
+                  ibmvfc_show_log_level, ibmvfc_store_log_level);
 
 #ifdef CONFIG_SCSI_IBMVFC_TRACE
 /**
@@ -2612,12 +2667,13 @@ static struct bin_attribute ibmvfc_trace_attr = {
 #endif
 
 static struct device_attribute *ibmvfc_attrs[] = {
-       &ibmvfc_host_partition_name,
-       &ibmvfc_host_device_name,
-       &ibmvfc_host_loc_code,
-       &ibmvfc_host_drc_name,
-       &ibmvfc_host_npiv_version,
-       &ibmvfc_log_level_attr,
+       &dev_attr_partition_name,
+       &dev_attr_device_name,
+       &dev_attr_port_loc_code,
+       &dev_attr_drc_name,
+       &dev_attr_npiv_version,
+       &dev_attr_capabilities,
+       &dev_attr_log_level,
        NULL
 };
 
@@ -2774,15 +2830,19 @@ static void ibmvfc_init_tgt(struct ibmvfc_target *tgt,
  * @tgt:               ibmvfc target struct
  * @job_step:  initialization job step
  *
+ * Returns: 1 if step will be retried / 0 if not
+ *
  **/
-static void ibmvfc_retry_tgt_init(struct ibmvfc_target *tgt,
+static int ibmvfc_retry_tgt_init(struct ibmvfc_target *tgt,
                                  void (*job_step) (struct ibmvfc_target *))
 {
        if (++tgt->init_retries > IBMVFC_MAX_TGT_INIT_RETRIES) {
                ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
                wake_up(&tgt->vhost->work_wait_q);
+               return 0;
        } else
                ibmvfc_init_tgt(tgt, job_step);
+       return 1;
 }
 
 /* Defined in FC-LS */
@@ -2831,7 +2891,7 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
        struct ibmvfc_process_login *rsp = &evt->xfer_iu->prli;
        struct ibmvfc_prli_svc_parms *parms = &rsp->parms;
        u32 status = rsp->common.status;
-       int index;
+       int index, level = IBMVFC_DEFAULT_LOG_LEVEL;
 
        vhost->discovery_threads--;
        ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
@@ -2850,7 +2910,7 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
                                                tgt->ids.roles |= FC_PORT_ROLE_FCP_TARGET;
                                        if (parms->service_parms & IBMVFC_PRLI_INITIATOR_FUNC)
                                                tgt->ids.roles |= FC_PORT_ROLE_FCP_INITIATOR;
-                                       ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_ADD_RPORT);
+                                       tgt->add_rport = 1;
                                } else
                                        ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
                        } else if (prli_rsp[index].retry)
@@ -2867,13 +2927,14 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
                break;
        case IBMVFC_MAD_FAILED:
        default:
-               tgt_err(tgt, "Process Login failed: %s (%x:%x) rc=0x%02X\n",
-                       ibmvfc_get_cmd_error(rsp->status, rsp->error),
-                       rsp->status, rsp->error, status);
                if (ibmvfc_retry_cmd(rsp->status, rsp->error))
-                       ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
+                       level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
                else
                        ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+
+               tgt_log(tgt, level, "Process Login failed: %s (%x:%x) rc=0x%02X\n",
+                       ibmvfc_get_cmd_error(rsp->status, rsp->error),
+                       rsp->status, rsp->error, status);
                break;
        };
 
@@ -2932,6 +2993,7 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
        struct ibmvfc_host *vhost = evt->vhost;
        struct ibmvfc_port_login *rsp = &evt->xfer_iu->plogi;
        u32 status = rsp->common.status;
+       int level = IBMVFC_DEFAULT_LOG_LEVEL;
 
        vhost->discovery_threads--;
        ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
@@ -2960,15 +3022,15 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
                break;
        case IBMVFC_MAD_FAILED:
        default:
-               tgt_err(tgt, "Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
-                       ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error,
-                       ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type,
-                       ibmvfc_get_ls_explain(rsp->fc_explain), rsp->fc_explain, status);
-
                if (ibmvfc_retry_cmd(rsp->status, rsp->error))
-                       ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
+                       level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
                else
                        ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+
+               tgt_log(tgt, level, "Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
+                       ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error,
+                       ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type,
+                       ibmvfc_get_ls_explain(rsp->fc_explain), rsp->fc_explain, status);
                break;
        };
 
@@ -3129,13 +3191,13 @@ static void ibmvfc_tgt_adisc_done(struct ibmvfc_event *evt)
        case IBMVFC_MAD_SUCCESS:
                tgt_dbg(tgt, "ADISC succeeded\n");
                if (ibmvfc_adisc_needs_plogi(mad, tgt))
-                       tgt->need_login = 1;
+                       ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
                break;
        case IBMVFC_MAD_DRIVER_FAILED:
                break;
        case IBMVFC_MAD_FAILED:
        default:
-               tgt->need_login = 1;
+               ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
                fc_reason = (mad->fc_iu.response[1] & 0x00ff0000) >> 16;
                fc_explain = (mad->fc_iu.response[1] & 0x0000ff00) >> 8;
                tgt_info(tgt, "ADISC failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
@@ -3322,6 +3384,7 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
        struct ibmvfc_host *vhost = evt->vhost;
        struct ibmvfc_query_tgt *rsp = &evt->xfer_iu->query_tgt;
        u32 status = rsp->common.status;
+       int level = IBMVFC_DEFAULT_LOG_LEVEL;
 
        vhost->discovery_threads--;
        ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
@@ -3341,19 +3404,19 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
                break;
        case IBMVFC_MAD_FAILED:
        default:
-               tgt_err(tgt, "Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
-                       ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error,
-                       ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type,
-                       ibmvfc_get_gs_explain(rsp->fc_explain), rsp->fc_explain, status);
-
                if ((rsp->status & IBMVFC_FABRIC_MAPPED) == IBMVFC_FABRIC_MAPPED &&
                    rsp->error == IBMVFC_UNABLE_TO_PERFORM_REQ &&
                    rsp->fc_explain == IBMVFC_PORT_NAME_NOT_REG)
                        ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
                else if (ibmvfc_retry_cmd(rsp->status, rsp->error))
-                       ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_query_target);
+                       level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_query_target);
                else
                        ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+
+               tgt_log(tgt, level, "Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
+                       ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error,
+                       ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type,
+                       ibmvfc_get_gs_explain(rsp->fc_explain), rsp->fc_explain, status);
                break;
        };
 
@@ -3420,7 +3483,7 @@ static int ibmvfc_alloc_target(struct ibmvfc_host *vhost, u64 scsi_id)
        }
        spin_unlock_irqrestore(vhost->host->host_lock, flags);
 
-       tgt = mempool_alloc(vhost->tgt_pool, GFP_KERNEL);
+       tgt = mempool_alloc(vhost->tgt_pool, GFP_NOIO);
        if (!tgt) {
                dev_err(vhost->dev, "Target allocation failure for scsi id %08llx\n",
                        scsi_id);
@@ -3472,6 +3535,7 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
        struct ibmvfc_host *vhost = evt->vhost;
        struct ibmvfc_discover_targets *rsp = &evt->xfer_iu->discover_targets;
        u32 mad_status = rsp->common.status;
+       int level = IBMVFC_DEFAULT_LOG_LEVEL;
 
        switch (mad_status) {
        case IBMVFC_MAD_SUCCESS:
@@ -3480,9 +3544,9 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
                ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_ALLOC_TGTS);
                break;
        case IBMVFC_MAD_FAILED:
-               dev_err(vhost->dev, "Discover Targets failed: %s (%x:%x)\n",
-                       ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error);
-               ibmvfc_retry_host_init(vhost);
+               level += ibmvfc_retry_host_init(vhost);
+               ibmvfc_log(vhost, level, "Discover Targets failed: %s (%x:%x)\n",
+                          ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error);
                break;
        case IBMVFC_MAD_DRIVER_FAILED:
                break;
@@ -3534,18 +3598,19 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
        u32 mad_status = evt->xfer_iu->npiv_login.common.status;
        struct ibmvfc_npiv_login_resp *rsp = &vhost->login_buf->resp;
        unsigned int npiv_max_sectors;
+       int level = IBMVFC_DEFAULT_LOG_LEVEL;
 
        switch (mad_status) {
        case IBMVFC_MAD_SUCCESS:
                ibmvfc_free_event(evt);
                break;
        case IBMVFC_MAD_FAILED:
-               dev_err(vhost->dev, "NPIV Login failed: %s (%x:%x)\n",
-                       ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error);
                if (ibmvfc_retry_cmd(rsp->status, rsp->error))
-                       ibmvfc_retry_host_init(vhost);
+                       level += ibmvfc_retry_host_init(vhost);
                else
                        ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+               ibmvfc_log(vhost, level, "NPIV Login failed: %s (%x:%x)\n",
+                          ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error);
                ibmvfc_free_event(evt);
                return;
        case IBMVFC_MAD_CRQ_ERROR:
@@ -3578,6 +3643,7 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
                return;
        }
 
+       vhost->logged_in = 1;
        npiv_max_sectors = min((uint)(rsp->max_dma_len >> 9), IBMVFC_MAX_SECTORS);
        dev_info(vhost->dev, "Host partition: %s, device: %s %s %s max sectors %u\n",
                 rsp->partition_name, rsp->device_name, rsp->port_loc_code,
@@ -3635,6 +3701,65 @@ static void ibmvfc_npiv_login(struct ibmvfc_host *vhost)
                ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
 };
 
+/**
+ * ibmvfc_npiv_logout_done - Completion handler for NPIV Logout
+ * @vhost:             ibmvfc host struct
+ *
+ **/
+static void ibmvfc_npiv_logout_done(struct ibmvfc_event *evt)
+{
+       struct ibmvfc_host *vhost = evt->vhost;
+       u32 mad_status = evt->xfer_iu->npiv_logout.common.status;
+
+       ibmvfc_free_event(evt);
+
+       switch (mad_status) {
+       case IBMVFC_MAD_SUCCESS:
+               if (list_empty(&vhost->sent) &&
+                   vhost->action == IBMVFC_HOST_ACTION_LOGO_WAIT) {
+                       ibmvfc_init_host(vhost, 0);
+                       return;
+               }
+               break;
+       case IBMVFC_MAD_FAILED:
+       case IBMVFC_MAD_NOT_SUPPORTED:
+       case IBMVFC_MAD_CRQ_ERROR:
+       case IBMVFC_MAD_DRIVER_FAILED:
+       default:
+               ibmvfc_dbg(vhost, "NPIV Logout failed. 0x%X\n", mad_status);
+               break;
+       }
+
+       ibmvfc_hard_reset_host(vhost);
+}
+
+/**
+ * ibmvfc_npiv_logout - Issue an NPIV Logout
+ * @vhost:             ibmvfc host struct
+ *
+ **/
+static void ibmvfc_npiv_logout(struct ibmvfc_host *vhost)
+{
+       struct ibmvfc_npiv_logout_mad *mad;
+       struct ibmvfc_event *evt;
+
+       evt = ibmvfc_get_event(vhost);
+       ibmvfc_init_event(evt, ibmvfc_npiv_logout_done, IBMVFC_MAD_FORMAT);
+
+       mad = &evt->iu.npiv_logout;
+       memset(mad, 0, sizeof(*mad));
+       mad->common.version = 1;
+       mad->common.opcode = IBMVFC_NPIV_LOGOUT;
+       mad->common.length = sizeof(struct ibmvfc_npiv_logout_mad);
+
+       ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_LOGO_WAIT);
+
+       if (!ibmvfc_send_event(evt, vhost, default_timeout))
+               ibmvfc_dbg(vhost, "Sent NPIV logout\n");
+       else
+               ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+}
+
 /**
  * ibmvfc_dev_init_to_do - Is there target initialization work to do?
  * @vhost:             ibmvfc host struct
@@ -3671,6 +3796,7 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
        switch (vhost->action) {
        case IBMVFC_HOST_ACTION_NONE:
        case IBMVFC_HOST_ACTION_INIT_WAIT:
+       case IBMVFC_HOST_ACTION_LOGO_WAIT:
                return 0;
        case IBMVFC_HOST_ACTION_TGT_INIT:
        case IBMVFC_HOST_ACTION_QUERY_TGTS:
@@ -3683,9 +3809,9 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
                        if (tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
                                return 0;
                return 1;
+       case IBMVFC_HOST_ACTION_LOGO:
        case IBMVFC_HOST_ACTION_INIT:
        case IBMVFC_HOST_ACTION_ALLOC_TGTS:
-       case IBMVFC_HOST_ACTION_TGT_ADD:
        case IBMVFC_HOST_ACTION_TGT_DEL:
        case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
        case IBMVFC_HOST_ACTION_QUERY:
@@ -3740,25 +3866,26 @@ static void ibmvfc_log_ae(struct ibmvfc_host *vhost, int events)
 static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt)
 {
        struct ibmvfc_host *vhost = tgt->vhost;
-       struct fc_rport *rport = tgt->rport;
+       struct fc_rport *rport;
        unsigned long flags;
 
-       if (rport) {
-               tgt_dbg(tgt, "Setting rport roles\n");
-               fc_remote_port_rolechg(rport, tgt->ids.roles);
-               spin_lock_irqsave(vhost->host->host_lock, flags);
-               ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+       tgt_dbg(tgt, "Adding rport\n");
+       rport = fc_remote_port_add(vhost->host, 0, &tgt->ids);
+       spin_lock_irqsave(vhost->host->host_lock, flags);
+
+       if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
+               tgt_dbg(tgt, "Deleting rport\n");
+               list_del(&tgt->queue);
                spin_unlock_irqrestore(vhost->host->host_lock, flags);
+               fc_remote_port_delete(rport);
+               del_timer_sync(&tgt->timer);
+               kref_put(&tgt->kref, ibmvfc_release_tgt);
                return;
        }
 
-       tgt_dbg(tgt, "Adding rport\n");
-       rport = fc_remote_port_add(vhost->host, 0, &tgt->ids);
-       spin_lock_irqsave(vhost->host->host_lock, flags);
-       tgt->rport = rport;
-       ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
        if (rport) {
                tgt_dbg(tgt, "rport add succeeded\n");
+               tgt->rport = rport;
                rport->maxframe_size = tgt->service_parms.common.bb_rcv_sz & 0x0fff;
                rport->supported_classes = 0;
                tgt->target_id = rport->scsi_target_id;
@@ -3789,8 +3916,12 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
        vhost->events_to_log = 0;
        switch (vhost->action) {
        case IBMVFC_HOST_ACTION_NONE:
+       case IBMVFC_HOST_ACTION_LOGO_WAIT:
        case IBMVFC_HOST_ACTION_INIT_WAIT:
                break;
+       case IBMVFC_HOST_ACTION_LOGO:
+               vhost->job_step(vhost);
+               break;
        case IBMVFC_HOST_ACTION_INIT:
                BUG_ON(vhost->state != IBMVFC_INITIALIZING);
                if (vhost->delay_init) {
@@ -3836,11 +3967,21 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
 
                if (vhost->state == IBMVFC_INITIALIZING) {
                        if (vhost->action == IBMVFC_HOST_ACTION_TGT_DEL_FAILED) {
-                               ibmvfc_set_host_state(vhost, IBMVFC_ACTIVE);
-                               ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_ADD);
-                               vhost->init_retries = 0;
-                               spin_unlock_irqrestore(vhost->host->host_lock, flags);
-                               scsi_unblock_requests(vhost->host);
+                               if (vhost->reinit) {
+                                       vhost->reinit = 0;
+                                       scsi_block_requests(vhost->host);
+                                       ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+                                       spin_unlock_irqrestore(vhost->host->host_lock, flags);
+                               } else {
+                                       ibmvfc_set_host_state(vhost, IBMVFC_ACTIVE);
+                                       ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
+                                       wake_up(&vhost->init_wait_q);
+                                       schedule_work(&vhost->rport_add_work_q);
+                                       vhost->init_retries = 0;
+                                       spin_unlock_irqrestore(vhost->host->host_lock, flags);
+                                       scsi_unblock_requests(vhost->host);
+                               }
+
                                return;
                        } else {
                                ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
@@ -3871,24 +4012,6 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
                if (!ibmvfc_dev_init_to_do(vhost))
                        ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL_FAILED);
                break;
-       case IBMVFC_HOST_ACTION_TGT_ADD:
-               list_for_each_entry(tgt, &vhost->targets, queue) {
-                       if (tgt->action == IBMVFC_TGT_ACTION_ADD_RPORT) {
-                               spin_unlock_irqrestore(vhost->host->host_lock, flags);
-                               ibmvfc_tgt_add_rport(tgt);
-                               return;
-                       }
-               }
-
-               if (vhost->reinit && !ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
-                       vhost->reinit = 0;
-                       scsi_block_requests(vhost->host);
-                       ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
-               } else {
-                       ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
-                       wake_up(&vhost->init_wait_q);
-               }
-               break;
        default:
                break;
        };
@@ -4117,6 +4240,56 @@ nomem:
        return -ENOMEM;
 }
 
+/**
+ * ibmvfc_rport_add_thread - Worker thread for rport adds
+ * @work:      work struct
+ *
+ **/
+static void ibmvfc_rport_add_thread(struct work_struct *work)
+{
+       struct ibmvfc_host *vhost = container_of(work, struct ibmvfc_host,
+                                                rport_add_work_q);
+       struct ibmvfc_target *tgt;
+       struct fc_rport *rport;
+       unsigned long flags;
+       int did_work;
+
+       ENTER;
+       spin_lock_irqsave(vhost->host->host_lock, flags);
+       do {
+               did_work = 0;
+               if (vhost->state != IBMVFC_ACTIVE)
+                       break;
+
+               list_for_each_entry(tgt, &vhost->targets, queue) {
+                       if (tgt->add_rport) {
+                               did_work = 1;
+                               tgt->add_rport = 0;
+                               kref_get(&tgt->kref);
+                               rport = tgt->rport;
+                               if (!rport) {
+                                       spin_unlock_irqrestore(vhost->host->host_lock, flags);
+                                       ibmvfc_tgt_add_rport(tgt);
+                               } else if (get_device(&rport->dev)) {
+                                       spin_unlock_irqrestore(vhost->host->host_lock, flags);
+                                       tgt_dbg(tgt, "Setting rport roles\n");
+                                       fc_remote_port_rolechg(rport, tgt->ids.roles);
+                                       put_device(&rport->dev);
+                               }
+
+                               kref_put(&tgt->kref, ibmvfc_release_tgt);
+                               spin_lock_irqsave(vhost->host->host_lock, flags);
+                               break;
+                       }
+               }
+       } while(did_work);
+
+       if (vhost->state == IBMVFC_ACTIVE)
+               vhost->scan_complete = 1;
+       spin_unlock_irqrestore(vhost->host->host_lock, flags);
+       LEAVE;
+}
+
 /**
  * ibmvfc_probe - Adapter hot plug add entry point
  * @vdev:      vio device struct
@@ -4160,6 +4333,7 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
        strcpy(vhost->partition_name, "UNKNOWN");
        init_waitqueue_head(&vhost->work_wait_q);
        init_waitqueue_head(&vhost->init_wait_q);
+       INIT_WORK(&vhost->rport_add_work_q, ibmvfc_rport_add_thread);
 
        if ((rc = ibmvfc_alloc_mem(vhost)))
                goto free_scsi_host;
index ca1dcf7a7568b95ab1f4d3608a5fd710a449798c..c2668d7d67f5308cdfc33819f0333e719f842947 100644 (file)
@@ -29,8 +29,8 @@
 #include "viosrp.h"
 
 #define IBMVFC_NAME    "ibmvfc"
-#define IBMVFC_DRIVER_VERSION          "1.0.5"
-#define IBMVFC_DRIVER_DATE             "(March 19, 2009)"
+#define IBMVFC_DRIVER_VERSION          "1.0.6"
+#define IBMVFC_DRIVER_DATE             "(May 28, 2009)"
 
 #define IBMVFC_DEFAULT_TIMEOUT 60
 #define IBMVFC_ADISC_CANCEL_TIMEOUT    45
  * Ensure we have resources for ERP and initialization:
  * 1 for ERP
  * 1 for initialization
+ * 1 for NPIV Logout
  * 2 for each discovery thread
  */
-#define IBMVFC_NUM_INTERNAL_REQ        (1 + 1 + (disc_threads * 2))
+#define IBMVFC_NUM_INTERNAL_REQ        (1 + 1 + 1 + (disc_threads * 2))
 
 #define IBMVFC_MAD_SUCCESS             0x00
 #define IBMVFC_MAD_NOT_SUPPORTED       0xF1
@@ -127,6 +128,7 @@ enum ibmvfc_mad_types {
        IBMVFC_IMPLICIT_LOGOUT  = 0x0040,
        IBMVFC_PASSTHRU         = 0x0200,
        IBMVFC_TMF_MAD          = 0x0100,
+       IBMVFC_NPIV_LOGOUT      = 0x0800,
 };
 
 struct ibmvfc_mad_common {
@@ -143,6 +145,10 @@ struct ibmvfc_npiv_login_mad {
        struct srp_direct_buf buffer;
 }__attribute__((packed, aligned (8)));
 
+struct ibmvfc_npiv_logout_mad {
+       struct ibmvfc_mad_common common;
+}__attribute__((packed, aligned (8)));
+
 #define IBMVFC_MAX_NAME 256
 
 struct ibmvfc_npiv_login {
@@ -201,7 +207,8 @@ struct ibmvfc_npiv_login_resp {
 #define IBMVFC_NATIVE_FC               0x01
 #define IBMVFC_CAN_FLUSH_ON_HALT       0x08
        u32 reserved;
-       u64 capabilites;
+       u64 capabilities;
+#define IBMVFC_CAN_FLUSH_ON_HALT       0x08
        u32 max_cmds;
        u32 scsi_id_sz;
        u64 max_dma_len;
@@ -541,9 +548,17 @@ struct ibmvfc_crq_queue {
        dma_addr_t msg_token;
 };
 
+enum ibmvfc_ae_link_state {
+       IBMVFC_AE_LS_LINK_UP            = 0x01,
+       IBMVFC_AE_LS_LINK_BOUNCED       = 0x02,
+       IBMVFC_AE_LS_LINK_DOWN          = 0x04,
+       IBMVFC_AE_LS_LINK_DEAD          = 0x08,
+};
+
 struct ibmvfc_async_crq {
        volatile u8 valid;
-       u8 pad[3];
+       u8 link_state;
+       u8 pad[2];
        u32 pad2;
        volatile u64 event;
        volatile u64 scsi_id;
@@ -561,6 +576,7 @@ struct ibmvfc_async_crq_queue {
 union ibmvfc_iu {
        struct ibmvfc_mad_common mad_common;
        struct ibmvfc_npiv_login_mad npiv_login;
+       struct ibmvfc_npiv_logout_mad npiv_logout;
        struct ibmvfc_discover_targets discover_targets;
        struct ibmvfc_port_login plogi;
        struct ibmvfc_process_login prli;
@@ -575,7 +591,6 @@ enum ibmvfc_target_action {
        IBMVFC_TGT_ACTION_NONE = 0,
        IBMVFC_TGT_ACTION_INIT,
        IBMVFC_TGT_ACTION_INIT_WAIT,
-       IBMVFC_TGT_ACTION_ADD_RPORT,
        IBMVFC_TGT_ACTION_DEL_RPORT,
 };
 
@@ -588,6 +603,7 @@ struct ibmvfc_target {
        int target_id;
        enum ibmvfc_target_action action;
        int need_login;
+       int add_rport;
        int init_retries;
        u32 cancel_key;
        struct ibmvfc_service_parms service_parms;
@@ -627,6 +643,8 @@ struct ibmvfc_event_pool {
 
 enum ibmvfc_host_action {
        IBMVFC_HOST_ACTION_NONE = 0,
+       IBMVFC_HOST_ACTION_LOGO,
+       IBMVFC_HOST_ACTION_LOGO_WAIT,
        IBMVFC_HOST_ACTION_INIT,
        IBMVFC_HOST_ACTION_INIT_WAIT,
        IBMVFC_HOST_ACTION_QUERY,
@@ -635,7 +653,6 @@ enum ibmvfc_host_action {
        IBMVFC_HOST_ACTION_ALLOC_TGTS,
        IBMVFC_HOST_ACTION_TGT_INIT,
        IBMVFC_HOST_ACTION_TGT_DEL_FAILED,
-       IBMVFC_HOST_ACTION_TGT_ADD,
 };
 
 enum ibmvfc_host_state {
@@ -682,6 +699,8 @@ struct ibmvfc_host {
        int client_migrated;
        int reinit;
        int delay_init;
+       int scan_complete;
+       int logged_in;
        int events_to_log;
 #define IBMVFC_AE_LINKUP       0x0001
 #define IBMVFC_AE_LINKDOWN     0x0002
@@ -692,6 +711,7 @@ struct ibmvfc_host {
        void (*job_step) (struct ibmvfc_host *);
        struct task_struct *work_thread;
        struct tasklet_struct tasklet;
+       struct work_struct rport_add_work_q;
        wait_queue_head_t init_wait_q;
        wait_queue_head_t work_wait_q;
 };
@@ -707,6 +727,12 @@ struct ibmvfc_host {
 #define tgt_err(t, fmt, ...)           \
        dev_err((t)->vhost->dev, "%llX: " fmt, (t)->scsi_id, ##__VA_ARGS__)
 
+#define tgt_log(t, level, fmt, ...) \
+       do { \
+               if ((t)->vhost->log_level >= level) \
+                       tgt_err(t, fmt, ##__VA_ARGS__); \
+       } while (0)
+
 #define ibmvfc_dbg(vhost, ...) \
        DBG_CMD(dev_info((vhost)->dev, ##__VA_ARGS__))
 
index c9aa7611e40824142534d21aa46fc4461936ba33..11d2602ae88ecd2ffd4c2ab8b759538dac8a02c8 100644 (file)
@@ -70,6 +70,7 @@
 #include <linux/moduleparam.h>
 #include <linux/dma-mapping.h>
 #include <linux/delay.h>
+#include <linux/of.h>
 #include <asm/firmware.h>
 #include <asm/vio.h>
 #include <asm/firmware.h>
  */
 static int max_id = 64;
 static int max_channel = 3;
-static int init_timeout = 5;
+static int init_timeout = 300;
+static int login_timeout = 60;
+static int info_timeout = 30;
+static int abort_timeout = 60;
+static int reset_timeout = 60;
 static int max_requests = IBMVSCSI_MAX_REQUESTS_DEFAULT;
 static int max_events = IBMVSCSI_MAX_REQUESTS_DEFAULT + 2;
+static int fast_fail = 1;
+static int client_reserve = 1;
 
 static struct scsi_transport_template *ibmvscsi_transport_template;
 
@@ -110,6 +117,10 @@ module_param_named(init_timeout, init_timeout, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(init_timeout, "Initialization timeout in seconds");
 module_param_named(max_requests, max_requests, int, S_IRUGO);
 MODULE_PARM_DESC(max_requests, "Maximum requests for this adapter");
+module_param_named(fast_fail, fast_fail, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(fast_fail, "Enable fast fail. [Default=1]");
+module_param_named(client_reserve, client_reserve, int, S_IRUGO );
+MODULE_PARM_DESC(client_reserve, "Attempt client managed reserve/release");
 
 /* ------------------------------------------------------------
  * Routines for the event pool and event structs
@@ -781,105 +792,53 @@ static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd,
 /* ------------------------------------------------------------
  * Routines for driver initialization
  */
+
 /**
- * adapter_info_rsp: - Handle response to MAD adapter info request
- * @evt_struct:        srp_event_struct with the response
+ * map_persist_bufs: - Pre-map persistent data for adapter logins
+ * @hostdata:   ibmvscsi_host_data of host
  *
- * Used as a "done" callback by when sending adapter_info. Gets called
- * by ibmvscsi_handle_crq()
-*/
-static void adapter_info_rsp(struct srp_event_struct *evt_struct)
+ * Map the capabilities and adapter info DMA buffers to avoid runtime failures.
+ * Return 1 on error, 0 on success.
+ */
+static int map_persist_bufs(struct ibmvscsi_host_data *hostdata)
 {
-       struct ibmvscsi_host_data *hostdata = evt_struct->hostdata;
-       dma_unmap_single(hostdata->dev,
-                        evt_struct->iu.mad.adapter_info.buffer,
-                        evt_struct->iu.mad.adapter_info.common.length,
-                        DMA_BIDIRECTIONAL);
 
-       if (evt_struct->xfer_iu->mad.adapter_info.common.status) {
-               dev_err(hostdata->dev, "error %d getting adapter info\n",
-                       evt_struct->xfer_iu->mad.adapter_info.common.status);
-       } else {
-               dev_info(hostdata->dev, "host srp version: %s, "
-                        "host partition %s (%d), OS %d, max io %u\n",
-                        hostdata->madapter_info.srp_version,
-                        hostdata->madapter_info.partition_name,
-                        hostdata->madapter_info.partition_number,
-                        hostdata->madapter_info.os_type,
-                        hostdata->madapter_info.port_max_txu[0]);
-               
-               if (hostdata->madapter_info.port_max_txu[0]) 
-                       hostdata->host->max_sectors = 
-                               hostdata->madapter_info.port_max_txu[0] >> 9;
-               
-               if (hostdata->madapter_info.os_type == 3 &&
-                   strcmp(hostdata->madapter_info.srp_version, "1.6a") <= 0) {
-                       dev_err(hostdata->dev, "host (Ver. %s) doesn't support large transfers\n",
-                               hostdata->madapter_info.srp_version);
-                       dev_err(hostdata->dev, "limiting scatterlists to %d\n",
-                               MAX_INDIRECT_BUFS);
-                       hostdata->host->sg_tablesize = MAX_INDIRECT_BUFS;
-               }
+       hostdata->caps_addr = dma_map_single(hostdata->dev, &hostdata->caps,
+                                            sizeof(hostdata->caps), DMA_BIDIRECTIONAL);
+
+       if (dma_mapping_error(hostdata->dev, hostdata->caps_addr)) {
+               dev_err(hostdata->dev, "Unable to map capabilities buffer!\n");
+               return 1;
        }
+
+       hostdata->adapter_info_addr = dma_map_single(hostdata->dev,
+                                                    &hostdata->madapter_info,
+                                                    sizeof(hostdata->madapter_info),
+                                                    DMA_BIDIRECTIONAL);
+       if (dma_mapping_error(hostdata->dev, hostdata->adapter_info_addr)) {
+               dev_err(hostdata->dev, "Unable to map adapter info buffer!\n");
+               dma_unmap_single(hostdata->dev, hostdata->caps_addr,
+                                sizeof(hostdata->caps), DMA_BIDIRECTIONAL);
+               return 1;
+       }
+
+       return 0;
 }
 
 /**
- * send_mad_adapter_info: - Sends the mad adapter info request
- *      and stores the result so it can be retrieved with
- *      sysfs.  We COULD consider causing a failure if the
- *      returned SRP version doesn't match ours.
- * @hostdata:  ibmvscsi_host_data of host
- * 
- * Returns zero if successful.
-*/
-static void send_mad_adapter_info(struct ibmvscsi_host_data *hostdata)
+ * unmap_persist_bufs: - Unmap persistent data needed for adapter logins
+ * @hostdata:   ibmvscsi_host_data of host
+ *
+ * Unmap the capabilities and adapter info DMA buffers
+ */
+static void unmap_persist_bufs(struct ibmvscsi_host_data *hostdata)
 {
-       struct viosrp_adapter_info *req;
-       struct srp_event_struct *evt_struct;
-       unsigned long flags;
-       dma_addr_t addr;
-
-       evt_struct = get_event_struct(&hostdata->pool);
-       if (!evt_struct) {
-               dev_err(hostdata->dev,
-                       "couldn't allocate an event for ADAPTER_INFO_REQ!\n");
-               return;
-       }
-
-       init_event_struct(evt_struct,
-                         adapter_info_rsp,
-                         VIOSRP_MAD_FORMAT,
-                         init_timeout);
-       
-       req = &evt_struct->iu.mad.adapter_info;
-       memset(req, 0x00, sizeof(*req));
-       
-       req->common.type = VIOSRP_ADAPTER_INFO_TYPE;
-       req->common.length = sizeof(hostdata->madapter_info);
-       req->buffer = addr = dma_map_single(hostdata->dev,
-                                           &hostdata->madapter_info,
-                                           sizeof(hostdata->madapter_info),
-                                           DMA_BIDIRECTIONAL);
+       dma_unmap_single(hostdata->dev, hostdata->caps_addr,
+                        sizeof(hostdata->caps), DMA_BIDIRECTIONAL);
 
-       if (dma_mapping_error(hostdata->dev, req->buffer)) {
-               if (!firmware_has_feature(FW_FEATURE_CMO))
-                       dev_err(hostdata->dev,
-                               "Unable to map request_buffer for "
-                               "adapter_info!\n");
-               free_event_struct(&hostdata->pool, evt_struct);
-               return;
-       }
-       
-       spin_lock_irqsave(hostdata->host->host_lock, flags);
-       if (ibmvscsi_send_srp_event(evt_struct, hostdata, init_timeout * 2)) {
-               dev_err(hostdata->dev, "couldn't send ADAPTER_INFO_REQ!\n");
-               dma_unmap_single(hostdata->dev,
-                                addr,
-                                sizeof(hostdata->madapter_info),
-                                DMA_BIDIRECTIONAL);
-       }
-       spin_unlock_irqrestore(hostdata->host->host_lock, flags);
-};
+       dma_unmap_single(hostdata->dev, hostdata->adapter_info_addr,
+                        sizeof(hostdata->madapter_info), DMA_BIDIRECTIONAL);
+}
 
 /**
  * login_rsp: - Handle response to SRP login request
@@ -909,9 +868,7 @@ static void login_rsp(struct srp_event_struct *evt_struct)
        }
 
        dev_info(hostdata->dev, "SRP_LOGIN succeeded\n");
-
-       if (evt_struct->xfer_iu->srp.login_rsp.req_lim_delta < 0)
-               dev_err(hostdata->dev, "Invalid request_limit.\n");
+       hostdata->client_migrated = 0;
 
        /* Now we know what the real request-limit is.
         * This value is set rather than added to request_limit because
@@ -922,15 +879,12 @@ static void login_rsp(struct srp_event_struct *evt_struct)
 
        /* If we had any pending I/Os, kick them */
        scsi_unblock_requests(hostdata->host);
-
-       send_mad_adapter_info(hostdata);
-       return;
 }
 
 /**
  * send_srp_login: - Sends the srp login
  * @hostdata:  ibmvscsi_host_data of host
- * 
+ *
  * Returns zero if successful.
 */
 static int send_srp_login(struct ibmvscsi_host_data *hostdata)
@@ -939,22 +893,17 @@ static int send_srp_login(struct ibmvscsi_host_data *hostdata)
        unsigned long flags;
        struct srp_login_req *login;
        struct srp_event_struct *evt_struct = get_event_struct(&hostdata->pool);
-       if (!evt_struct) {
-               dev_err(hostdata->dev, "couldn't allocate an event for login req!\n");
-               return FAILED;
-       }
 
-       init_event_struct(evt_struct,
-                         login_rsp,
-                         VIOSRP_SRP_FORMAT,
-                         init_timeout);
+       BUG_ON(!evt_struct);
+       init_event_struct(evt_struct, login_rsp,
+                         VIOSRP_SRP_FORMAT, login_timeout);
 
        login = &evt_struct->iu.srp.login_req;
-       memset(login, 0x00, sizeof(struct srp_login_req));
+       memset(login, 0, sizeof(*login));
        login->opcode = SRP_LOGIN_REQ;
        login->req_it_iu_len = sizeof(union srp_iu);
        login->req_buf_fmt = SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT;
-       
+
        spin_lock_irqsave(hostdata->host->host_lock, flags);
        /* Start out with a request limit of 0, since this is negotiated in
         * the login request we are just sending and login requests always
@@ -962,12 +911,240 @@ static int send_srp_login(struct ibmvscsi_host_data *hostdata)
         */
        atomic_set(&hostdata->request_limit, 0);
 
-       rc = ibmvscsi_send_srp_event(evt_struct, hostdata, init_timeout * 2);
+       rc = ibmvscsi_send_srp_event(evt_struct, hostdata, login_timeout * 2);
        spin_unlock_irqrestore(hostdata->host->host_lock, flags);
        dev_info(hostdata->dev, "sent SRP login\n");
        return rc;
 };
 
+/**
+ * capabilities_rsp: - Handle response to MAD adapter capabilities request
+ * @evt_struct:        srp_event_struct with the response
+ *
+ * Used as a "done" callback by when sending adapter_info.
+ */
+static void capabilities_rsp(struct srp_event_struct *evt_struct)
+{
+       struct ibmvscsi_host_data *hostdata = evt_struct->hostdata;
+
+       if (evt_struct->xfer_iu->mad.capabilities.common.status) {
+               dev_err(hostdata->dev, "error 0x%X getting capabilities info\n",
+                       evt_struct->xfer_iu->mad.capabilities.common.status);
+       } else {
+               if (hostdata->caps.migration.common.server_support != SERVER_SUPPORTS_CAP)
+                       dev_info(hostdata->dev, "Partition migration not supported\n");
+
+               if (client_reserve) {
+                       if (hostdata->caps.reserve.common.server_support ==
+                           SERVER_SUPPORTS_CAP)
+                               dev_info(hostdata->dev, "Client reserve enabled\n");
+                       else
+                               dev_info(hostdata->dev, "Client reserve not supported\n");
+               }
+       }
+
+       send_srp_login(hostdata);
+}
+
+/**
+ * send_mad_capabilities: - Sends the mad capabilities request
+ *      and stores the result so it can be retrieved with
+ * @hostdata:  ibmvscsi_host_data of host
+ */
+static void send_mad_capabilities(struct ibmvscsi_host_data *hostdata)
+{
+       struct viosrp_capabilities *req;
+       struct srp_event_struct *evt_struct;
+       unsigned long flags;
+       struct device_node *of_node = hostdata->dev->archdata.of_node;
+       const char *location;
+
+       evt_struct = get_event_struct(&hostdata->pool);
+       BUG_ON(!evt_struct);
+
+       init_event_struct(evt_struct, capabilities_rsp,
+                         VIOSRP_MAD_FORMAT, info_timeout);
+
+       req = &evt_struct->iu.mad.capabilities;
+       memset(req, 0, sizeof(*req));
+
+       hostdata->caps.flags = CAP_LIST_SUPPORTED;
+       if (hostdata->client_migrated)
+               hostdata->caps.flags |= CLIENT_MIGRATED;
+
+       strncpy(hostdata->caps.name, dev_name(&hostdata->host->shost_gendev),
+               sizeof(hostdata->caps.name));
+       hostdata->caps.name[sizeof(hostdata->caps.name) - 1] = '\0';
+
+       location = of_get_property(of_node, "ibm,loc-code", NULL);
+       location = location ? location : dev_name(hostdata->dev);
+       strncpy(hostdata->caps.loc, location, sizeof(hostdata->caps.loc));
+       hostdata->caps.loc[sizeof(hostdata->caps.loc) - 1] = '\0';
+
+       req->common.type = VIOSRP_CAPABILITIES_TYPE;
+       req->buffer = hostdata->caps_addr;
+
+       hostdata->caps.migration.common.cap_type = MIGRATION_CAPABILITIES;
+       hostdata->caps.migration.common.length = sizeof(hostdata->caps.migration);
+       hostdata->caps.migration.common.server_support = SERVER_SUPPORTS_CAP;
+       hostdata->caps.migration.ecl = 1;
+
+       if (client_reserve) {
+               hostdata->caps.reserve.common.cap_type = RESERVATION_CAPABILITIES;
+               hostdata->caps.reserve.common.length = sizeof(hostdata->caps.reserve);
+               hostdata->caps.reserve.common.server_support = SERVER_SUPPORTS_CAP;
+               hostdata->caps.reserve.type = CLIENT_RESERVE_SCSI_2;
+               req->common.length = sizeof(hostdata->caps);
+       } else
+               req->common.length = sizeof(hostdata->caps) - sizeof(hostdata->caps.reserve);
+
+       spin_lock_irqsave(hostdata->host->host_lock, flags);
+       if (ibmvscsi_send_srp_event(evt_struct, hostdata, info_timeout * 2))
+               dev_err(hostdata->dev, "couldn't send CAPABILITIES_REQ!\n");
+       spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+};
+
+/**
+ * fast_fail_rsp: - Handle response to MAD enable fast fail
+ * @evt_struct:        srp_event_struct with the response
+ *
+ * Used as a "done" callback by when sending enable fast fail. Gets called
+ * by ibmvscsi_handle_crq()
+ */
+static void fast_fail_rsp(struct srp_event_struct *evt_struct)
+{
+       struct ibmvscsi_host_data *hostdata = evt_struct->hostdata;
+       u8 status = evt_struct->xfer_iu->mad.fast_fail.common.status;
+
+       if (status == VIOSRP_MAD_NOT_SUPPORTED)
+               dev_err(hostdata->dev, "fast_fail not supported in server\n");
+       else if (status == VIOSRP_MAD_FAILED)
+               dev_err(hostdata->dev, "fast_fail request failed\n");
+       else if (status != VIOSRP_MAD_SUCCESS)
+               dev_err(hostdata->dev, "error 0x%X enabling fast_fail\n", status);
+
+       send_mad_capabilities(hostdata);
+}
+
+/**
+ * init_host - Start host initialization
+ * @hostdata:  ibmvscsi_host_data of host
+ *
+ * Returns zero if successful.
+ */
+static int enable_fast_fail(struct ibmvscsi_host_data *hostdata)
+{
+       int rc;
+       unsigned long flags;
+       struct viosrp_fast_fail *fast_fail_mad;
+       struct srp_event_struct *evt_struct;
+
+       if (!fast_fail) {
+               send_mad_capabilities(hostdata);
+               return 0;
+       }
+
+       evt_struct = get_event_struct(&hostdata->pool);
+       BUG_ON(!evt_struct);
+
+       init_event_struct(evt_struct, fast_fail_rsp, VIOSRP_MAD_FORMAT, info_timeout);
+
+       fast_fail_mad = &evt_struct->iu.mad.fast_fail;
+       memset(fast_fail_mad, 0, sizeof(*fast_fail_mad));
+       fast_fail_mad->common.type = VIOSRP_ENABLE_FAST_FAIL;
+       fast_fail_mad->common.length = sizeof(*fast_fail_mad);
+
+       spin_lock_irqsave(hostdata->host->host_lock, flags);
+       rc = ibmvscsi_send_srp_event(evt_struct, hostdata, info_timeout * 2);
+       spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+       return rc;
+}
+
+/**
+ * adapter_info_rsp: - Handle response to MAD adapter info request
+ * @evt_struct:        srp_event_struct with the response
+ *
+ * Used as a "done" callback by when sending adapter_info. Gets called
+ * by ibmvscsi_handle_crq()
+*/
+static void adapter_info_rsp(struct srp_event_struct *evt_struct)
+{
+       struct ibmvscsi_host_data *hostdata = evt_struct->hostdata;
+
+       if (evt_struct->xfer_iu->mad.adapter_info.common.status) {
+               dev_err(hostdata->dev, "error %d getting adapter info\n",
+                       evt_struct->xfer_iu->mad.adapter_info.common.status);
+       } else {
+               dev_info(hostdata->dev, "host srp version: %s, "
+                        "host partition %s (%d), OS %d, max io %u\n",
+                        hostdata->madapter_info.srp_version,
+                        hostdata->madapter_info.partition_name,
+                        hostdata->madapter_info.partition_number,
+                        hostdata->madapter_info.os_type,
+                        hostdata->madapter_info.port_max_txu[0]);
+               
+               if (hostdata->madapter_info.port_max_txu[0]) 
+                       hostdata->host->max_sectors = 
+                               hostdata->madapter_info.port_max_txu[0] >> 9;
+               
+               if (hostdata->madapter_info.os_type == 3 &&
+                   strcmp(hostdata->madapter_info.srp_version, "1.6a") <= 0) {
+                       dev_err(hostdata->dev, "host (Ver. %s) doesn't support large transfers\n",
+                               hostdata->madapter_info.srp_version);
+                       dev_err(hostdata->dev, "limiting scatterlists to %d\n",
+                               MAX_INDIRECT_BUFS);
+                       hostdata->host->sg_tablesize = MAX_INDIRECT_BUFS;
+               }
+       }
+
+       enable_fast_fail(hostdata);
+}
+
+/**
+ * send_mad_adapter_info: - Sends the mad adapter info request
+ *      and stores the result so it can be retrieved with
+ *      sysfs.  We COULD consider causing a failure if the
+ *      returned SRP version doesn't match ours.
+ * @hostdata:  ibmvscsi_host_data of host
+ * 
+ * Returns zero if successful.
+*/
+static void send_mad_adapter_info(struct ibmvscsi_host_data *hostdata)
+{
+       struct viosrp_adapter_info *req;
+       struct srp_event_struct *evt_struct;
+       unsigned long flags;
+
+       evt_struct = get_event_struct(&hostdata->pool);
+       BUG_ON(!evt_struct);
+
+       init_event_struct(evt_struct,
+                         adapter_info_rsp,
+                         VIOSRP_MAD_FORMAT,
+                         info_timeout);
+       
+       req = &evt_struct->iu.mad.adapter_info;
+       memset(req, 0x00, sizeof(*req));
+       
+       req->common.type = VIOSRP_ADAPTER_INFO_TYPE;
+       req->common.length = sizeof(hostdata->madapter_info);
+       req->buffer = hostdata->adapter_info_addr;
+
+       spin_lock_irqsave(hostdata->host->host_lock, flags);
+       if (ibmvscsi_send_srp_event(evt_struct, hostdata, info_timeout * 2))
+               dev_err(hostdata->dev, "couldn't send ADAPTER_INFO_REQ!\n");
+       spin_unlock_irqrestore(hostdata->host->host_lock, flags);
+};
+
+/**
+ * init_adapter: Start virtual adapter initialization sequence
+ *
+ */
+static void init_adapter(struct ibmvscsi_host_data *hostdata)
+{
+       send_mad_adapter_info(hostdata);
+}
+
 /**
  * sync_completion: Signal that a synchronous command has completed
  * Note that after returning from this call, the evt_struct is freed.
@@ -1029,7 +1206,7 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd)
                init_event_struct(evt,
                                  sync_completion,
                                  VIOSRP_SRP_FORMAT,
-                                 init_timeout);
+                                 abort_timeout);
 
                tsk_mgmt = &evt->iu.srp.tsk_mgmt;
        
@@ -1043,7 +1220,7 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd)
                evt->sync_srp = &srp_rsp;
 
                init_completion(&evt->comp);
-               rsp_rc = ibmvscsi_send_srp_event(evt, hostdata, init_timeout * 2);
+               rsp_rc = ibmvscsi_send_srp_event(evt, hostdata, abort_timeout * 2);
 
                if (rsp_rc != SCSI_MLQUEUE_HOST_BUSY)
                        break;
@@ -1152,7 +1329,7 @@ static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd)
                init_event_struct(evt,
                                  sync_completion,
                                  VIOSRP_SRP_FORMAT,
-                                 init_timeout);
+                                 reset_timeout);
 
                tsk_mgmt = &evt->iu.srp.tsk_mgmt;
 
@@ -1165,7 +1342,7 @@ static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd)
                evt->sync_srp = &srp_rsp;
 
                init_completion(&evt->comp);
-               rsp_rc = ibmvscsi_send_srp_event(evt, hostdata, init_timeout * 2);
+               rsp_rc = ibmvscsi_send_srp_event(evt, hostdata, reset_timeout * 2);
 
                if (rsp_rc != SCSI_MLQUEUE_HOST_BUSY)
                        break;
@@ -1281,7 +1458,7 @@ void ibmvscsi_handle_crq(struct viosrp_crq *crq,
                        if ((rc = ibmvscsi_ops->send_crq(hostdata,
                                                         0xC002000000000000LL, 0)) == 0) {
                                /* Now login */
-                               send_srp_login(hostdata);
+                               init_adapter(hostdata);
                        } else {
                                dev_err(hostdata->dev, "Unable to send init rsp. rc=%ld\n", rc);
                        }
@@ -1291,7 +1468,7 @@ void ibmvscsi_handle_crq(struct viosrp_crq *crq,
                        dev_info(hostdata->dev, "partner initialization complete\n");
 
                        /* Now login */
-                       send_srp_login(hostdata);
+                       init_adapter(hostdata);
                        break;
                default:
                        dev_err(hostdata->dev, "unknown crq message type: %d\n", crq->format);
@@ -1303,6 +1480,7 @@ void ibmvscsi_handle_crq(struct viosrp_crq *crq,
                if (crq->format == 0x06) {
                        /* We need to re-setup the interpartition connection */
                        dev_info(hostdata->dev, "Re-enabling adapter!\n");
+                       hostdata->client_migrated = 1;
                        purge_requests(hostdata, DID_REQUEUE);
                        if ((ibmvscsi_ops->reenable_crq_queue(&hostdata->queue,
                                                              hostdata)) ||
@@ -1397,7 +1575,7 @@ static int ibmvscsi_do_host_config(struct ibmvscsi_host_data *hostdata,
        init_event_struct(evt_struct,
                          sync_completion,
                          VIOSRP_MAD_FORMAT,
-                         init_timeout);
+                         info_timeout);
 
        host_config = &evt_struct->iu.mad.host_config;
 
@@ -1419,7 +1597,7 @@ static int ibmvscsi_do_host_config(struct ibmvscsi_host_data *hostdata,
 
        init_completion(&evt_struct->comp);
        spin_lock_irqsave(hostdata->host->host_lock, flags);
-       rc = ibmvscsi_send_srp_event(evt_struct, hostdata, init_timeout * 2);
+       rc = ibmvscsi_send_srp_event(evt_struct, hostdata, info_timeout * 2);
        spin_unlock_irqrestore(hostdata->host->host_lock, flags);
        if (rc == 0)
                wait_for_completion(&evt_struct->comp);
@@ -1444,7 +1622,7 @@ static int ibmvscsi_slave_configure(struct scsi_device *sdev)
        spin_lock_irqsave(shost->host_lock, lock_flags);
        if (sdev->type == TYPE_DISK) {
                sdev->allow_restart = 1;
-               blk_queue_rq_timeout(sdev->request_queue, 60 * HZ);
+               blk_queue_rq_timeout(sdev->request_queue, 120 * HZ);
        }
        scsi_adjust_queue_depth(sdev, 0, shost->cmd_per_lun);
        spin_unlock_irqrestore(shost->host_lock, lock_flags);
@@ -1471,6 +1649,46 @@ static int ibmvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth)
 /* ------------------------------------------------------------
  * sysfs attributes
  */
+static ssize_t show_host_vhost_loc(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+       struct Scsi_Host *shost = class_to_shost(dev);
+       struct ibmvscsi_host_data *hostdata = shost_priv(shost);
+       int len;
+
+       len = snprintf(buf, sizeof(hostdata->caps.loc), "%s\n",
+                      hostdata->caps.loc);
+       return len;
+}
+
+static struct device_attribute ibmvscsi_host_vhost_loc = {
+       .attr = {
+                .name = "vhost_loc",
+                .mode = S_IRUGO,
+                },
+       .show = show_host_vhost_loc,
+};
+
+static ssize_t show_host_vhost_name(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct Scsi_Host *shost = class_to_shost(dev);
+       struct ibmvscsi_host_data *hostdata = shost_priv(shost);
+       int len;
+
+       len = snprintf(buf, sizeof(hostdata->caps.name), "%s\n",
+                      hostdata->caps.name);
+       return len;
+}
+
+static struct device_attribute ibmvscsi_host_vhost_name = {
+       .attr = {
+                .name = "vhost_name",
+                .mode = S_IRUGO,
+                },
+       .show = show_host_vhost_name,
+};
+
 static ssize_t show_host_srp_version(struct device *dev,
                                     struct device_attribute *attr, char *buf)
 {
@@ -1594,6 +1812,8 @@ static struct device_attribute ibmvscsi_host_config = {
 };
 
 static struct device_attribute *ibmvscsi_attrs[] = {
+       &ibmvscsi_host_vhost_loc,
+       &ibmvscsi_host_vhost_name,
        &ibmvscsi_host_srp_version,
        &ibmvscsi_host_partition_name,
        &ibmvscsi_host_partition_number,
@@ -1674,6 +1894,11 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
        atomic_set(&hostdata->request_limit, -1);
        hostdata->host->max_sectors = IBMVSCSI_MAX_SECTORS_DEFAULT;
 
+       if (map_persist_bufs(hostdata)) {
+               dev_err(&vdev->dev, "couldn't map persistent buffers\n");
+               goto persist_bufs_failed;
+       }
+
        rc = ibmvscsi_ops->init_crq_queue(&hostdata->queue, hostdata, max_events);
        if (rc != 0 && rc != H_RESOURCE) {
                dev_err(&vdev->dev, "couldn't initialize crq. rc=%d\n", rc);
@@ -1687,6 +1912,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
        host->max_lun = 8;
        host->max_id = max_id;
        host->max_channel = max_channel;
+       host->max_cmd_len = 16;
 
        if (scsi_add_host(hostdata->host, hostdata->dev))
                goto add_host_failed;
@@ -1733,6 +1959,8 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
       init_pool_failed:
        ibmvscsi_ops->release_crq_queue(&hostdata->queue, hostdata, max_events);
       init_crq_failed:
+       unmap_persist_bufs(hostdata);
+      persist_bufs_failed:
        scsi_host_put(host);
       scsi_host_alloc_failed:
        return -1;
@@ -1741,6 +1969,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
 static int ibmvscsi_remove(struct vio_dev *vdev)
 {
        struct ibmvscsi_host_data *hostdata = vdev->dev.driver_data;
+       unmap_persist_bufs(hostdata);
        release_event_pool(&hostdata->pool, hostdata);
        ibmvscsi_ops->release_crq_queue(&hostdata->queue, hostdata,
                                        max_events);
index 2d4339d5e16e4e3c9cfed8b7b4fe30b87cb84932..76425303def0c0265b89db3ea656132cf7b756c8 100644 (file)
@@ -90,6 +90,7 @@ struct event_pool {
 /* all driver data associated with a host adapter */
 struct ibmvscsi_host_data {
        atomic_t request_limit;
+       int client_migrated;
        struct device *dev;
        struct event_pool pool;
        struct crq_queue queue;
@@ -97,6 +98,9 @@ struct ibmvscsi_host_data {
        struct list_head sent;
        struct Scsi_Host *host;
        struct mad_adapter_info_data madapter_info;
+       struct capabilities caps;
+       dma_addr_t caps_addr;
+       dma_addr_t adapter_info_addr;
 };
 
 /* routines for managing a command/response queue */
index 204604501ad8cc347a3c01bc27205fc2bf614982..2cd735d1d1962fe43eee7b65f85f1774bfd95fef 100644 (file)
@@ -37,6 +37,7 @@
 
 #define SRP_VERSION "16.a"
 #define SRP_MAX_IU_LEN 256
+#define SRP_MAX_LOC_LEN 32
 
 union srp_iu {
        struct srp_login_req login_req;
@@ -86,7 +87,37 @@ enum viosrp_mad_types {
        VIOSRP_EMPTY_IU_TYPE = 0x01,
        VIOSRP_ERROR_LOG_TYPE = 0x02,
        VIOSRP_ADAPTER_INFO_TYPE = 0x03,
-       VIOSRP_HOST_CONFIG_TYPE = 0x04
+       VIOSRP_HOST_CONFIG_TYPE = 0x04,
+       VIOSRP_CAPABILITIES_TYPE = 0x05,
+       VIOSRP_ENABLE_FAST_FAIL = 0x08,
+};
+
+enum viosrp_mad_status {
+       VIOSRP_MAD_SUCCESS = 0x00,
+       VIOSRP_MAD_NOT_SUPPORTED = 0xF1,
+       VIOSRP_MAD_FAILED = 0xF7,
+};
+
+enum viosrp_capability_type {
+       MIGRATION_CAPABILITIES = 0x01,
+       RESERVATION_CAPABILITIES = 0x02,
+};
+
+enum viosrp_capability_support {
+       SERVER_DOES_NOT_SUPPORTS_CAP = 0x0,
+       SERVER_SUPPORTS_CAP = 0x01,
+       SERVER_CAP_DATA = 0x02,
+};
+
+enum viosrp_reserve_type {
+       CLIENT_RESERVE_SCSI_2 = 0x01,
+};
+
+enum viosrp_capability_flag {
+       CLIENT_MIGRATED = 0x01,
+       CLIENT_RECONNECT = 0x02,
+       CAP_LIST_SUPPORTED = 0x04,
+       CAP_LIST_DATA = 0x08,
 };
 
 /* 
@@ -127,11 +158,46 @@ struct viosrp_host_config {
        u64 buffer;
 };
 
+struct viosrp_fast_fail {
+       struct mad_common common;
+};
+
+struct viosrp_capabilities {
+       struct mad_common common;
+       u64 buffer;
+};
+
+struct mad_capability_common {
+       u32 cap_type;
+       u16 length;
+       u16 server_support;
+};
+
+struct mad_reserve_cap {
+       struct mad_capability_common common;
+       u32 type;
+};
+
+struct mad_migration_cap {
+       struct mad_capability_common common;
+       u32 ecl;
+};
+
+struct capabilities{
+       u32 flags;
+       char name[SRP_MAX_LOC_LEN];
+       char loc[SRP_MAX_LOC_LEN];
+       struct mad_migration_cap migration;
+       struct mad_reserve_cap reserve;
+};
+
 union mad_iu {
        struct viosrp_empty_iu empty_iu;
        struct viosrp_error_log error_log;
        struct viosrp_adapter_info adapter_info;
        struct viosrp_host_config host_config;
+       struct viosrp_fast_fail fast_fail;
+       struct viosrp_capabilities capabilities;
 };
 
 union viosrp_iu {
index dd689ded8609e0709cbb0980d7356359fc6a0524..0f8bc772b1124d910ffd96bf455736152dffcd6e 100644 (file)
@@ -7003,6 +7003,7 @@ static void ipr_pci_perm_failure(struct pci_dev *pdev)
                ioa_cfg->sdt_state = ABORT_DUMP;
        ioa_cfg->reset_retries = IPR_NUM_RESET_RELOAD_RETRIES;
        ioa_cfg->in_ioa_bringdown = 1;
+       ioa_cfg->allow_cmds = 0;
        ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
        spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
 }
@@ -7688,7 +7689,7 @@ static void __ipr_remove(struct pci_dev *pdev)
  * Return value:
  *     none
  **/
-static void ipr_remove(struct pci_dev *pdev)
+static void __devexit ipr_remove(struct pci_dev *pdev)
 {
        struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
 
@@ -7864,7 +7865,7 @@ static struct pci_driver ipr_driver = {
        .name = IPR_NAME,
        .id_table = ipr_pci_table,
        .probe = ipr_probe,
-       .remove = ipr_remove,
+       .remove = __devexit_p(ipr_remove),
        .shutdown = ipr_shutdown,
        .err_handler = &ipr_err_handler,
 };
index 992af05aacf154f8ee4cbb5ea663fbb22611f5e6..7af9bceb8aa9efa467c3ca58ed4da1694ec7d79c 100644 (file)
@@ -1159,6 +1159,10 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
                atomic_inc(&mp->stats.xid_not_found);
                goto out;
        }
+       if (ep->esb_stat & ESB_ST_COMPLETE) {
+               atomic_inc(&mp->stats.xid_not_found);
+               goto out;
+       }
        if (ep->rxid == FC_XID_UNKNOWN)
                ep->rxid = ntohs(fh->fh_rx_id);
        if (ep->sid != 0 && ep->sid != ntoh24(fh->fh_d_id)) {
index 521f996f9b131dd96c654530cfd73b06a961560c..ad8b747837b08c8922f56d4690f091d304e2321e 100644 (file)
@@ -1896,7 +1896,7 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
                sc_cmd->result = (DID_ERROR << 16) | fsp->cdb_status;
                break;
        case FC_CMD_ABORTED:
-               sc_cmd->result = (DID_ABORT << 16) | fsp->io_status;
+               sc_cmd->result = (DID_ERROR << 16) | fsp->io_status;
                break;
        case FC_CMD_TIME_OUT:
                sc_cmd->result = (DID_BUS_BUSY << 16) | fsp->io_status;
index 747d73c5c8affa7efa94bf0ad94c705ea9063394..7bfbff7e0efb256656d1ef6ce8a5e8ac68fa9c69 100644 (file)
@@ -478,7 +478,7 @@ static void fc_rport_error_retry(struct fc_rport *rport, struct fc_frame *fp)
        if (PTR_ERR(fp) == -FC_EX_CLOSED)
                return fc_rport_error(rport, fp);
 
-       if (rdata->retries < rdata->local_port->max_retry_count) {
+       if (rdata->retries < rdata->local_port->max_rport_retry_count) {
                FC_DEBUG_RPORT("Error %ld in state %s, retrying\n",
                               PTR_ERR(fp), fc_rport_state(rport));
                rdata->retries++;
@@ -1330,7 +1330,7 @@ int fc_rport_init(struct fc_lport *lport)
 }
 EXPORT_SYMBOL(fc_rport_init);
 
-int fc_setup_rport()
+int fc_setup_rport(void)
 {
        rport_event_queue = create_singlethread_workqueue("fc_rport_eq");
        if (!rport_event_queue)
@@ -1339,7 +1339,7 @@ int fc_setup_rport()
 }
 EXPORT_SYMBOL(fc_setup_rport);
 
-void fc_destroy_rport()
+void fc_destroy_rport(void)
 {
        destroy_workqueue(rport_event_queue);
 }
index e72b4ad47d35bd6ff3855ec038eed77b15c1719d..59908aead531e6257ad63f1767cf736c82585956 100644 (file)
@@ -81,7 +81,8 @@ inline void iscsi_conn_queue_work(struct iscsi_conn *conn)
        struct Scsi_Host *shost = conn->session->host;
        struct iscsi_host *ihost = shost_priv(shost);
 
-       queue_work(ihost->workq, &conn->xmitwork);
+       if (ihost->workq)
+               queue_work(ihost->workq, &conn->xmitwork);
 }
 EXPORT_SYMBOL_GPL(iscsi_conn_queue_work);
 
@@ -109,11 +110,9 @@ iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
                 * if the window closed with IO queued, then kick the
                 * xmit thread
                 */
-               if (!list_empty(&session->leadconn->xmitqueue) ||
-                   !list_empty(&session->leadconn->mgmtqueue)) {
-                       if (!(session->tt->caps & CAP_DATA_PATH_OFFLOAD))
-                               iscsi_conn_queue_work(session->leadconn);
-               }
+               if (!list_empty(&session->leadconn->cmdqueue) ||
+                   !list_empty(&session->leadconn->mgmtqueue))
+                       iscsi_conn_queue_work(session->leadconn);
        }
 }
 EXPORT_SYMBOL_GPL(iscsi_update_cmdsn);
@@ -257,9 +256,11 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
        itt_t itt;
        int rc;
 
-       rc = conn->session->tt->alloc_pdu(task, ISCSI_OP_SCSI_CMD);
-       if (rc)
-               return rc;
+       if (conn->session->tt->alloc_pdu) {
+               rc = conn->session->tt->alloc_pdu(task, ISCSI_OP_SCSI_CMD);
+               if (rc)
+                       return rc;
+       }
        hdr = (struct iscsi_cmd *) task->hdr;
        itt = hdr->itt;
        memset(hdr, 0, sizeof(*hdr));
@@ -364,7 +365,6 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
                return -EIO;
 
        task->state = ISCSI_TASK_RUNNING;
-       list_move_tail(&task->running, &conn->run_list);
 
        conn->scsicmd_pdus_cnt++;
        ISCSI_DBG_SESSION(session, "iscsi prep [%s cid %d sc %p cdb 0x%x "
@@ -380,26 +380,25 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
 }
 
 /**
- * iscsi_complete_command - finish a task
+ * iscsi_free_task - free a task
  * @task: iscsi cmd task
  *
  * Must be called with session lock.
  * This function returns the scsi command to scsi-ml or cleans
  * up mgmt tasks then returns the task to the pool.
  */
-static void iscsi_complete_command(struct iscsi_task *task)
+static void iscsi_free_task(struct iscsi_task *task)
 {
        struct iscsi_conn *conn = task->conn;
        struct iscsi_session *session = conn->session;
        struct scsi_cmnd *sc = task->sc;
 
+       ISCSI_DBG_SESSION(session, "freeing task itt 0x%x state %d sc %p\n",
+                         task->itt, task->state, task->sc);
+
        session->tt->cleanup_task(task);
-       list_del_init(&task->running);
-       task->state = ISCSI_TASK_COMPLETED;
+       task->state = ISCSI_TASK_FREE;
        task->sc = NULL;
-
-       if (conn->task == task)
-               conn->task = NULL;
        /*
         * login task is preallocated so do not free
         */
@@ -408,9 +407,6 @@ static void iscsi_complete_command(struct iscsi_task *task)
 
        __kfifo_put(session->cmdpool.queue, (void*)&task, sizeof(void*));
 
-       if (conn->ping_task == task)
-               conn->ping_task = NULL;
-
        if (sc) {
                task->sc = NULL;
                /* SCSI eh reuses commands to verify us */
@@ -433,7 +429,7 @@ EXPORT_SYMBOL_GPL(__iscsi_get_task);
 static void __iscsi_put_task(struct iscsi_task *task)
 {
        if (atomic_dec_and_test(&task->refcount))
-               iscsi_complete_command(task);
+               iscsi_free_task(task);
 }
 
 void iscsi_put_task(struct iscsi_task *task)
@@ -446,26 +442,74 @@ void iscsi_put_task(struct iscsi_task *task)
 }
 EXPORT_SYMBOL_GPL(iscsi_put_task);
 
+/**
+ * iscsi_complete_task - finish a task
+ * @task: iscsi cmd task
+ * @state: state to complete task with
+ *
+ * Must be called with session lock.
+ */
+static void iscsi_complete_task(struct iscsi_task *task, int state)
+{
+       struct iscsi_conn *conn = task->conn;
+
+       ISCSI_DBG_SESSION(conn->session,
+                         "complete task itt 0x%x state %d sc %p\n",
+                         task->itt, task->state, task->sc);
+       if (task->state == ISCSI_TASK_COMPLETED ||
+           task->state == ISCSI_TASK_ABRT_TMF ||
+           task->state == ISCSI_TASK_ABRT_SESS_RECOV)
+               return;
+       WARN_ON_ONCE(task->state == ISCSI_TASK_FREE);
+       task->state = state;
+
+       if (!list_empty(&task->running))
+               list_del_init(&task->running);
+
+       if (conn->task == task)
+               conn->task = NULL;
+
+       if (conn->ping_task == task)
+               conn->ping_task = NULL;
+
+       /* release get from queueing */
+       __iscsi_put_task(task);
+}
+
 /*
- * session lock must be held
+ * session lock must be held and if not called for a task that is
+ * still pending or from the xmit thread, then xmit thread must
+ * be suspended.
  */
-static void fail_command(struct iscsi_conn *conn, struct iscsi_task *task,
-                        int err)
+static void fail_scsi_task(struct iscsi_task *task, int err)
 {
+       struct iscsi_conn *conn = task->conn;
        struct scsi_cmnd *sc;
+       int state;
 
+       /*
+        * if a command completes and we get a successful tmf response
+        * we will hit this because the scsi eh abort code does not take
+        * a ref to the task.
+        */
        sc = task->sc;
        if (!sc)
                return;
 
-       if (task->state == ISCSI_TASK_PENDING)
+       if (task->state == ISCSI_TASK_PENDING) {
                /*
                 * cmd never made it to the xmit thread, so we should not count
                 * the cmd in the sequencing
                 */
                conn->session->queued_cmdsn--;
+               /* it was never sent so just complete like normal */
+               state = ISCSI_TASK_COMPLETED;
+       } else if (err == DID_TRANSPORT_DISRUPTED)
+               state = ISCSI_TASK_ABRT_SESS_RECOV;
+       else
+               state = ISCSI_TASK_ABRT_TMF;
 
-       sc->result = err;
+       sc->result = err << 16;
        if (!scsi_bidi_cmnd(sc))
                scsi_set_resid(sc, scsi_bufflen(sc));
        else {
@@ -473,10 +517,7 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_task *task,
                scsi_in(sc)->resid = scsi_in(sc)->length;
        }
 
-       if (conn->task == task)
-               conn->task = NULL;
-       /* release ref from queuecommand */
-       __iscsi_put_task(task);
+       iscsi_complete_task(task, state);
 }
 
 static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
@@ -516,7 +557,6 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
                session->state = ISCSI_STATE_LOGGING_OUT;
 
        task->state = ISCSI_TASK_RUNNING;
-       list_move_tail(&task->running, &conn->mgmt_run_list);
        ISCSI_DBG_SESSION(session, "mgmtpdu [op 0x%x hdr->itt 0x%x "
                          "datalen %d]\n", hdr->opcode & ISCSI_OPCODE_MASK,
                          hdr->itt, task->data_count);
@@ -528,6 +568,7 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                      char *data, uint32_t data_size)
 {
        struct iscsi_session *session = conn->session;
+       struct iscsi_host *ihost = shost_priv(session->host);
        struct iscsi_task *task;
        itt_t itt;
 
@@ -544,6 +585,9 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                 */
                task = conn->login_task;
        else {
+               if (session->state != ISCSI_STATE_LOGGED_IN)
+                       return NULL;
+
                BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE);
                BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED);
 
@@ -559,6 +603,8 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
        atomic_set(&task->refcount, 1);
        task->conn = conn;
        task->sc = NULL;
+       INIT_LIST_HEAD(&task->running);
+       task->state = ISCSI_TASK_PENDING;
 
        if (data_size) {
                memcpy(task->data, data, data_size);
@@ -566,11 +612,14 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
        } else
                task->data_count = 0;
 
-       if (conn->session->tt->alloc_pdu(task, hdr->opcode)) {
-               iscsi_conn_printk(KERN_ERR, conn, "Could not allocate "
-                                "pdu for mgmt task.\n");
-               goto requeue_task;
+       if (conn->session->tt->alloc_pdu) {
+               if (conn->session->tt->alloc_pdu(task, hdr->opcode)) {
+                       iscsi_conn_printk(KERN_ERR, conn, "Could not allocate "
+                                        "pdu for mgmt task.\n");
+                       goto free_task;
+               }
        }
+
        itt = task->hdr->itt;
        task->hdr_len = sizeof(struct iscsi_hdr);
        memcpy(task->hdr, hdr, sizeof(struct iscsi_hdr));
@@ -583,30 +632,22 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                                                   task->conn->session->age);
        }
 
-       INIT_LIST_HEAD(&task->running);
-       list_add_tail(&task->running, &conn->mgmtqueue);
-
-       if (session->tt->caps & CAP_DATA_PATH_OFFLOAD) {
+       if (!ihost->workq) {
                if (iscsi_prep_mgmt_task(conn, task))
                        goto free_task;
 
                if (session->tt->xmit_task(task))
                        goto free_task;
-
-       } else
+       } else {
+               list_add_tail(&task->running, &conn->mgmtqueue);
                iscsi_conn_queue_work(conn);
+       }
 
        return task;
 
 free_task:
        __iscsi_put_task(task);
        return NULL;
-
-requeue_task:
-       if (task != conn->login_task)
-               __kfifo_put(session->cmdpool.queue, (void*)&task,
-                           sizeof(void*));
-       return NULL;
 }
 
 int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr,
@@ -701,11 +742,10 @@ invalid_datalen:
                        sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
        }
 out:
-       ISCSI_DBG_SESSION(session, "done [sc %p res %d itt 0x%x]\n",
+       ISCSI_DBG_SESSION(session, "cmd rsp done [sc %p res %d itt 0x%x]\n",
                          sc, sc->result, task->itt);
        conn->scsirsp_pdus_cnt++;
-
-       __iscsi_put_task(task);
+       iscsi_complete_task(task, ISCSI_TASK_COMPLETED);
 }
 
 /**
@@ -724,6 +764,7 @@ iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
        if (!(rhdr->flags & ISCSI_FLAG_DATA_STATUS))
                return;
 
+       iscsi_update_cmdsn(conn->session, (struct iscsi_nopin *)hdr);
        sc->result = (DID_OK << 16) | rhdr->cmd_status;
        conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1;
        if (rhdr->flags & (ISCSI_FLAG_DATA_UNDERFLOW |
@@ -738,8 +779,11 @@ iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                        sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
        }
 
+       ISCSI_DBG_SESSION(conn->session, "data in with status done "
+                         "[sc %p res %d itt 0x%x]\n",
+                         sc, sc->result, task->itt);
        conn->scsirsp_pdus_cnt++;
-       __iscsi_put_task(task);
+       iscsi_complete_task(task, ISCSI_TASK_COMPLETED);
 }
 
 static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
@@ -823,7 +867,7 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
  *
  * The session lock must be held.
  */
-static struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt)
+struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt)
 {
        struct iscsi_session *session = conn->session;
        int i;
@@ -840,6 +884,7 @@ static struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt)
 
        return session->cmds[i];
 }
+EXPORT_SYMBOL_GPL(iscsi_itt_to_task);
 
 /**
  * __iscsi_complete_pdu - complete pdu
@@ -959,7 +1004,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                }
 
                iscsi_tmf_rsp(conn, hdr);
-               __iscsi_put_task(task);
+               iscsi_complete_task(task, ISCSI_TASK_COMPLETED);
                break;
        case ISCSI_OP_NOOP_IN:
                iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
@@ -977,7 +1022,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                        goto recv_pdu;
 
                mod_timer(&conn->transport_timer, jiffies + conn->recv_timeout);
-               __iscsi_put_task(task);
+               iscsi_complete_task(task, ISCSI_TASK_COMPLETED);
                break;
        default:
                rc = ISCSI_ERR_BAD_OPCODE;
@@ -989,7 +1034,7 @@ out:
 recv_pdu:
        if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))
                rc = ISCSI_ERR_CONN_FAILED;
-       __iscsi_put_task(task);
+       iscsi_complete_task(task, ISCSI_TASK_COMPLETED);
        return rc;
 }
 EXPORT_SYMBOL_GPL(__iscsi_complete_pdu);
@@ -1166,7 +1211,12 @@ void iscsi_requeue_task(struct iscsi_task *task)
 {
        struct iscsi_conn *conn = task->conn;
 
-       list_move_tail(&task->running, &conn->requeue);
+       /*
+        * this may be on the requeue list already if the xmit_task callout
+        * is handling the r2ts while we are adding new ones
+        */
+       if (list_empty(&task->running))
+               list_add_tail(&task->running, &conn->requeue);
        iscsi_conn_queue_work(conn);
 }
 EXPORT_SYMBOL_GPL(iscsi_requeue_task);
@@ -1206,6 +1256,7 @@ check_mgmt:
        while (!list_empty(&conn->mgmtqueue)) {
                conn->task = list_entry(conn->mgmtqueue.next,
                                         struct iscsi_task, running);
+               list_del_init(&conn->task->running);
                if (iscsi_prep_mgmt_task(conn, conn->task)) {
                        __iscsi_put_task(conn->task);
                        conn->task = NULL;
@@ -1217,23 +1268,26 @@ check_mgmt:
        }
 
        /* process pending command queue */
-       while (!list_empty(&conn->xmitqueue)) {
+       while (!list_empty(&conn->cmdqueue)) {
                if (conn->tmf_state == TMF_QUEUED)
                        break;
 
-               conn->task = list_entry(conn->xmitqueue.next,
+               conn->task = list_entry(conn->cmdqueue.next,
                                         struct iscsi_task, running);
+               list_del_init(&conn->task->running);
                if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
-                       fail_command(conn, conn->task, DID_IMM_RETRY << 16);
+                       fail_scsi_task(conn->task, DID_IMM_RETRY);
                        continue;
                }
                rc = iscsi_prep_scsi_cmd_pdu(conn->task);
                if (rc) {
                        if (rc == -ENOMEM) {
+                               list_add_tail(&conn->task->running,
+                                             &conn->cmdqueue);
                                conn->task = NULL;
                                goto again;
                        } else
-                               fail_command(conn, conn->task, DID_ABORT << 16);
+                               fail_scsi_task(conn->task, DID_ABORT);
                        continue;
                }
                rc = iscsi_xmit_task(conn);
@@ -1260,8 +1314,8 @@ check_mgmt:
 
                conn->task = list_entry(conn->requeue.next,
                                         struct iscsi_task, running);
+               list_del_init(&conn->task->running);
                conn->task->state = ISCSI_TASK_RUNNING;
-               list_move_tail(conn->requeue.next, &conn->run_list);
                rc = iscsi_xmit_task(conn);
                if (rc)
                        goto again;
@@ -1328,6 +1382,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
 {
        struct iscsi_cls_session *cls_session;
        struct Scsi_Host *host;
+       struct iscsi_host *ihost;
        int reason = 0;
        struct iscsi_session *session;
        struct iscsi_conn *conn;
@@ -1338,6 +1393,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
        sc->SCp.ptr = NULL;
 
        host = sc->device->host;
+       ihost = shost_priv(host);
        spin_unlock(host->host_lock);
 
        cls_session = starget_to_session(scsi_target(sc->device));
@@ -1350,13 +1406,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
                goto fault;
        }
 
-       /*
-        * ISCSI_STATE_FAILED is a temp. state. The recovery
-        * code will decide what is best to do with command queued
-        * during this time
-        */
-       if (session->state != ISCSI_STATE_LOGGED_IN &&
-           session->state != ISCSI_STATE_FAILED) {
+       if (session->state != ISCSI_STATE_LOGGED_IN) {
                /*
                 * to handle the race between when we set the recovery state
                 * and block the session we requeue here (commands could
@@ -1364,12 +1414,15 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
                 * up because the block code is not locked)
                 */
                switch (session->state) {
+               case ISCSI_STATE_FAILED:
                case ISCSI_STATE_IN_RECOVERY:
                        reason = FAILURE_SESSION_IN_RECOVERY;
-                       goto reject;
+                       sc->result = DID_IMM_RETRY << 16;
+                       break;
                case ISCSI_STATE_LOGGING_OUT:
                        reason = FAILURE_SESSION_LOGGING_OUT;
-                       goto reject;
+                       sc->result = DID_IMM_RETRY << 16;
+                       break;
                case ISCSI_STATE_RECOVERY_FAILED:
                        reason = FAILURE_SESSION_RECOVERY_TIMEOUT;
                        sc->result = DID_TRANSPORT_FAILFAST << 16;
@@ -1402,9 +1455,8 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
                reason = FAILURE_OOM;
                goto reject;
        }
-       list_add_tail(&task->running, &conn->xmitqueue);
 
-       if (session->tt->caps & CAP_DATA_PATH_OFFLOAD) {
+       if (!ihost->workq) {
                reason = iscsi_prep_scsi_cmd_pdu(task);
                if (reason) {
                        if (reason == -ENOMEM) {
@@ -1419,8 +1471,10 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
                        reason = FAILURE_SESSION_NOT_READY;
                        goto prepd_reject;
                }
-       } else
+       } else {
+               list_add_tail(&task->running, &conn->cmdqueue);
                iscsi_conn_queue_work(conn);
+       }
 
        session->queued_cmdsn++;
        spin_unlock(&session->lock);
@@ -1429,7 +1483,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
 
 prepd_reject:
        sc->scsi_done = NULL;
-       iscsi_complete_command(task);
+       iscsi_complete_task(task, ISCSI_TASK_COMPLETED);
 reject:
        spin_unlock(&session->lock);
        ISCSI_DBG_SESSION(session, "cmd 0x%x rejected (%d)\n",
@@ -1439,7 +1493,7 @@ reject:
 
 prepd_fault:
        sc->scsi_done = NULL;
-       iscsi_complete_command(task);
+       iscsi_complete_task(task, ISCSI_TASK_COMPLETED);
 fault:
        spin_unlock(&session->lock);
        ISCSI_DBG_SESSION(session, "iscsi: cmd 0x%x is not queued (%d)\n",
@@ -1608,44 +1662,24 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
  * Fail commands. session lock held and recv side suspended and xmit
  * thread flushed
  */
-static void fail_all_commands(struct iscsi_conn *conn, unsigned lun,
-                             int error)
+static void fail_scsi_tasks(struct iscsi_conn *conn, unsigned lun,
+                           int error)
 {
-       struct iscsi_task *task, *tmp;
-
-       if (conn->task) {
-               if (lun == -1 ||
-                   (conn->task->sc && conn->task->sc->device->lun == lun))
-                       conn->task = NULL;
-       }
+       struct iscsi_task *task;
+       int i;
 
-       /* flush pending */
-       list_for_each_entry_safe(task, tmp, &conn->xmitqueue, running) {
-               if (lun == task->sc->device->lun || lun == -1) {
-                       ISCSI_DBG_SESSION(conn->session,
-                                         "failing pending sc %p itt 0x%x\n",
-                                         task->sc, task->itt);
-                       fail_command(conn, task, error << 16);
-               }
-       }
+       for (i = 0; i < conn->session->cmds_max; i++) {
+               task = conn->session->cmds[i];
+               if (!task->sc || task->state == ISCSI_TASK_FREE)
+                       continue;
 
-       list_for_each_entry_safe(task, tmp, &conn->requeue, running) {
-               if (lun == task->sc->device->lun || lun == -1) {
-                       ISCSI_DBG_SESSION(conn->session,
-                                         "failing requeued sc %p itt 0x%x\n",
-                                         task->sc, task->itt);
-                       fail_command(conn, task, error << 16);
-               }
-       }
+               if (lun != -1 && lun != task->sc->device->lun)
+                       continue;
 
-       /* fail all other running */
-       list_for_each_entry_safe(task, tmp, &conn->run_list, running) {
-               if (lun == task->sc->device->lun || lun == -1) {
-                       ISCSI_DBG_SESSION(conn->session,
-                                        "failing in progress sc %p itt 0x%x\n",
-                                        task->sc, task->itt);
-                       fail_command(conn, task, error << 16);
-               }
+               ISCSI_DBG_SESSION(conn->session,
+                                 "failing sc %p itt 0x%x state %d\n",
+                                 task->sc, task->itt, task->state);
+               fail_scsi_task(task, error);
        }
 }
 
@@ -1655,7 +1689,7 @@ void iscsi_suspend_tx(struct iscsi_conn *conn)
        struct iscsi_host *ihost = shost_priv(shost);
 
        set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
-       if (!(conn->session->tt->caps & CAP_DATA_PATH_OFFLOAD))
+       if (ihost->workq)
                flush_workqueue(ihost->workq);
 }
 EXPORT_SYMBOL_GPL(iscsi_suspend_tx);
@@ -1663,8 +1697,23 @@ EXPORT_SYMBOL_GPL(iscsi_suspend_tx);
 static void iscsi_start_tx(struct iscsi_conn *conn)
 {
        clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
-       if (!(conn->session->tt->caps & CAP_DATA_PATH_OFFLOAD))
-               iscsi_conn_queue_work(conn);
+       iscsi_conn_queue_work(conn);
+}
+
+/*
+ * We want to make sure a ping is in flight. It has timed out.
+ * And we are not busy processing a pdu that is making
+ * progress but got started before the ping and is taking a while
+ * to complete so the ping is just stuck behind it in a queue.
+ */
+static int iscsi_has_ping_timed_out(struct iscsi_conn *conn)
+{
+       if (conn->ping_task &&
+           time_before_eq(conn->last_recv + (conn->recv_timeout * HZ) +
+                          (conn->ping_timeout * HZ), jiffies))
+               return 1;
+       else
+               return 0;
 }
 
 static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
@@ -1702,16 +1751,20 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
         * if the ping timedout then we are in the middle of cleaning up
         * and can let the iscsi eh handle it
         */
-       if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ) +
-                           (conn->ping_timeout * HZ), jiffies))
+       if (iscsi_has_ping_timed_out(conn)) {
                rc = BLK_EH_RESET_TIMER;
+               goto done;
+       }
        /*
         * if we are about to check the transport then give the command
         * more time
         */
        if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ),
-                          jiffies))
+                          jiffies)) {
                rc = BLK_EH_RESET_TIMER;
+               goto done;
+       }
+
        /* if in the middle of checking the transport then give us more time */
        if (conn->ping_task)
                rc = BLK_EH_RESET_TIMER;
@@ -1738,13 +1791,13 @@ static void iscsi_check_transport_timeouts(unsigned long data)
 
        recv_timeout *= HZ;
        last_recv = conn->last_recv;
-       if (conn->ping_task &&
-           time_before_eq(conn->last_ping + (conn->ping_timeout * HZ),
-                          jiffies)) {
+
+       if (iscsi_has_ping_timed_out(conn)) {
                iscsi_conn_printk(KERN_ERR, conn, "ping timeout of %d secs "
-                                 "expired, last rx %lu, last ping %lu, "
-                                 "now %lu\n", conn->ping_timeout, last_recv,
-                                 conn->last_ping, jiffies);
+                                 "expired, recv timeout %d, last rx %lu, "
+                                 "last ping %lu, now %lu\n",
+                                 conn->ping_timeout, conn->recv_timeout,
+                                 last_recv, conn->last_ping, jiffies);
                spin_unlock(&session->lock);
                iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
                return;
@@ -1788,6 +1841,8 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
        cls_session = starget_to_session(scsi_target(sc->device));
        session = cls_session->dd_data;
 
+       ISCSI_DBG_SESSION(session, "aborting sc %p\n", sc);
+
        mutex_lock(&session->eh_mutex);
        spin_lock_bh(&session->lock);
        /*
@@ -1810,6 +1865,8 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
            sc->SCp.phase != session->age) {
                spin_unlock_bh(&session->lock);
                mutex_unlock(&session->eh_mutex);
+               ISCSI_DBG_SESSION(session, "failing abort due to dropped "
+                                 "session.\n");
                return FAILED;
        }
 
@@ -1829,7 +1886,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
        }
 
        if (task->state == ISCSI_TASK_PENDING) {
-               fail_command(conn, task, DID_ABORT << 16);
+               fail_scsi_task(task, DID_ABORT);
                goto success;
        }
 
@@ -1860,7 +1917,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
                 * then sent more data for the cmd.
                 */
                spin_lock(&session->lock);
-               fail_command(conn, task, DID_ABORT << 16);
+               fail_scsi_task(task, DID_ABORT);
                conn->tmf_state = TMF_INITIAL;
                spin_unlock(&session->lock);
                iscsi_start_tx(conn);
@@ -1967,7 +2024,7 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
        iscsi_suspend_tx(conn);
 
        spin_lock_bh(&session->lock);
-       fail_all_commands(conn, sc->device->lun, DID_ERROR);
+       fail_scsi_tasks(conn, sc->device->lun, DID_ERROR);
        conn->tmf_state = TMF_INITIAL;
        spin_unlock_bh(&session->lock);
 
@@ -2274,6 +2331,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost,
                if (cmd_task_size)
                        task->dd_data = &task[1];
                task->itt = cmd_i;
+               task->state = ISCSI_TASK_FREE;
                INIT_LIST_HEAD(&task->running);
        }
 
@@ -2360,10 +2418,8 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size,
        conn->transport_timer.data = (unsigned long)conn;
        conn->transport_timer.function = iscsi_check_transport_timeouts;
 
-       INIT_LIST_HEAD(&conn->run_list);
-       INIT_LIST_HEAD(&conn->mgmt_run_list);
        INIT_LIST_HEAD(&conn->mgmtqueue);
-       INIT_LIST_HEAD(&conn->xmitqueue);
+       INIT_LIST_HEAD(&conn->cmdqueue);
        INIT_LIST_HEAD(&conn->requeue);
        INIT_WORK(&conn->xmitwork, iscsi_xmitworker);
 
@@ -2531,27 +2587,28 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
 EXPORT_SYMBOL_GPL(iscsi_conn_start);
 
 static void
-flush_control_queues(struct iscsi_session *session, struct iscsi_conn *conn)
+fail_mgmt_tasks(struct iscsi_session *session, struct iscsi_conn *conn)
 {
-       struct iscsi_task *task, *tmp;
+       struct iscsi_task *task;
+       int i, state;
 
-       /* handle pending */
-       list_for_each_entry_safe(task, tmp, &conn->mgmtqueue, running) {
-               ISCSI_DBG_SESSION(session, "flushing pending mgmt task "
-                                 "itt 0x%x\n", task->itt);
-               /* release ref from prep task */
-               __iscsi_put_task(task);
-       }
+       for (i = 0; i < conn->session->cmds_max; i++) {
+               task = conn->session->cmds[i];
+               if (task->sc)
+                       continue;
 
-       /* handle running */
-       list_for_each_entry_safe(task, tmp, &conn->mgmt_run_list, running) {
-               ISCSI_DBG_SESSION(session, "flushing running mgmt task "
-                                 "itt 0x%x\n", task->itt);
-               /* release ref from prep task */
-               __iscsi_put_task(task);
-       }
+               if (task->state == ISCSI_TASK_FREE)
+                       continue;
+
+               ISCSI_DBG_SESSION(conn->session,
+                                 "failing mgmt itt 0x%x state %d\n",
+                                 task->itt, task->state);
+               state = ISCSI_TASK_ABRT_SESS_RECOV;
+               if (task->state == ISCSI_TASK_PENDING)
+                       state = ISCSI_TASK_COMPLETED;
+               iscsi_complete_task(task, state);
 
-       conn->task = NULL;
+       }
 }
 
 static void iscsi_start_session_recovery(struct iscsi_session *session,
@@ -2559,8 +2616,6 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
 {
        int old_stop_stage;
 
-       del_timer_sync(&conn->transport_timer);
-
        mutex_lock(&session->eh_mutex);
        spin_lock_bh(&session->lock);
        if (conn->stop_stage == STOP_CONN_TERM) {
@@ -2578,13 +2633,17 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
                session->state = ISCSI_STATE_TERMINATE;
        else if (conn->stop_stage != STOP_CONN_RECOVER)
                session->state = ISCSI_STATE_IN_RECOVERY;
+       spin_unlock_bh(&session->lock);
+
+       del_timer_sync(&conn->transport_timer);
+       iscsi_suspend_tx(conn);
 
+       spin_lock_bh(&session->lock);
        old_stop_stage = conn->stop_stage;
        conn->stop_stage = flag;
        conn->c_stage = ISCSI_CONN_STOPPED;
        spin_unlock_bh(&session->lock);
 
-       iscsi_suspend_tx(conn);
        /*
         * for connection level recovery we should not calculate
         * header digest. conn->hdr_size used for optimization
@@ -2605,11 +2664,8 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
         * flush queues.
         */
        spin_lock_bh(&session->lock);
-       if (flag == STOP_CONN_RECOVER)
-               fail_all_commands(conn, -1, DID_TRANSPORT_DISRUPTED);
-       else
-               fail_all_commands(conn, -1, DID_ERROR);
-       flush_control_queues(session, conn);
+       fail_scsi_tasks(conn, -1, DID_TRANSPORT_DISRUPTED);
+       fail_mgmt_tasks(session, conn);
        spin_unlock_bh(&session->lock);
        mutex_unlock(&session->eh_mutex);
 }
@@ -2651,6 +2707,23 @@ int iscsi_conn_bind(struct iscsi_cls_session *cls_session,
 }
 EXPORT_SYMBOL_GPL(iscsi_conn_bind);
 
+static int iscsi_switch_str_param(char **param, char *new_val_buf)
+{
+       char *new_val;
+
+       if (*param) {
+               if (!strcmp(*param, new_val_buf))
+                       return 0;
+       }
+
+       new_val = kstrdup(new_val_buf, GFP_NOIO);
+       if (!new_val)
+               return -ENOMEM;
+
+       kfree(*param);
+       *param = new_val;
+       return 0;
+}
 
 int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
                    enum iscsi_param param, char *buf, int buflen)
@@ -2723,38 +2796,15 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
                sscanf(buf, "%u", &conn->exp_statsn);
                break;
        case ISCSI_PARAM_USERNAME:
-               kfree(session->username);
-               session->username = kstrdup(buf, GFP_KERNEL);
-               if (!session->username)
-                       return -ENOMEM;
-               break;
+               return iscsi_switch_str_param(&session->username, buf);
        case ISCSI_PARAM_USERNAME_IN:
-               kfree(session->username_in);
-               session->username_in = kstrdup(buf, GFP_KERNEL);
-               if (!session->username_in)
-                       return -ENOMEM;
-               break;
+               return iscsi_switch_str_param(&session->username_in, buf);
        case ISCSI_PARAM_PASSWORD:
-               kfree(session->password);
-               session->password = kstrdup(buf, GFP_KERNEL);
-               if (!session->password)
-                       return -ENOMEM;
-               break;
+               return iscsi_switch_str_param(&session->password, buf);
        case ISCSI_PARAM_PASSWORD_IN:
-               kfree(session->password_in);
-               session->password_in = kstrdup(buf, GFP_KERNEL);
-               if (!session->password_in)
-                       return -ENOMEM;
-               break;
+               return iscsi_switch_str_param(&session->password_in, buf);
        case ISCSI_PARAM_TARGET_NAME:
-               /* this should not change between logins */
-               if (session->targetname)
-                       break;
-
-               session->targetname = kstrdup(buf, GFP_KERNEL);
-               if (!session->targetname)
-                       return -ENOMEM;
-               break;
+               return iscsi_switch_str_param(&session->targetname, buf);
        case ISCSI_PARAM_TPGT:
                sscanf(buf, "%d", &session->tpgt);
                break;
@@ -2762,25 +2812,11 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
                sscanf(buf, "%d", &conn->persistent_port);
                break;
        case ISCSI_PARAM_PERSISTENT_ADDRESS:
-               /*
-                * this is the address returned in discovery so it should
-                * not change between logins.
-                */
-               if (conn->persistent_address)
-                       break;
-
-               conn->persistent_address = kstrdup(buf, GFP_KERNEL);
-               if (!conn->persistent_address)
-                       return -ENOMEM;
-               break;
+               return iscsi_switch_str_param(&conn->persistent_address, buf);
        case ISCSI_PARAM_IFACE_NAME:
-               if (!session->ifacename)
-                       session->ifacename = kstrdup(buf, GFP_KERNEL);
-               break;
+               return iscsi_switch_str_param(&session->ifacename, buf);
        case ISCSI_PARAM_INITIATOR_NAME:
-               if (!session->initiatorname)
-                       session->initiatorname = kstrdup(buf, GFP_KERNEL);
-               break;
+               return iscsi_switch_str_param(&session->initiatorname, buf);
        default:
                return -ENOSYS;
        }
@@ -2851,10 +2887,7 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
                len = sprintf(buf, "%s\n", session->ifacename);
                break;
        case ISCSI_PARAM_INITIATOR_NAME:
-               if (!session->initiatorname)
-                       len = sprintf(buf, "%s\n", "unknown");
-               else
-                       len = sprintf(buf, "%s\n", session->initiatorname);
+               len = sprintf(buf, "%s\n", session->initiatorname);
                break;
        default:
                return -ENOSYS;
@@ -2920,29 +2953,16 @@ int iscsi_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param,
 
        switch (param) {
        case ISCSI_HOST_PARAM_NETDEV_NAME:
-               if (!ihost->netdev)
-                       len = sprintf(buf, "%s\n", "default");
-               else
-                       len = sprintf(buf, "%s\n", ihost->netdev);
+               len = sprintf(buf, "%s\n", ihost->netdev);
                break;
        case ISCSI_HOST_PARAM_HWADDRESS:
-               if (!ihost->hwaddress)
-                       len = sprintf(buf, "%s\n", "default");
-               else
-                       len = sprintf(buf, "%s\n", ihost->hwaddress);
+               len = sprintf(buf, "%s\n", ihost->hwaddress);
                break;
        case ISCSI_HOST_PARAM_INITIATOR_NAME:
-               if (!ihost->initiatorname)
-                       len = sprintf(buf, "%s\n", "unknown");
-               else
-                       len = sprintf(buf, "%s\n", ihost->initiatorname);
+               len = sprintf(buf, "%s\n", ihost->initiatorname);
                break;
        case ISCSI_HOST_PARAM_IPADDRESS:
-               if (!strlen(ihost->local_address))
-                       len = sprintf(buf, "%s\n", "unknown");
-               else
-                       len = sprintf(buf, "%s\n",
-                                     ihost->local_address);
+               len = sprintf(buf, "%s\n", ihost->local_address);
                break;
        default:
                return -ENOSYS;
@@ -2959,17 +2979,11 @@ int iscsi_host_set_param(struct Scsi_Host *shost, enum iscsi_host_param param,
 
        switch (param) {
        case ISCSI_HOST_PARAM_NETDEV_NAME:
-               if (!ihost->netdev)
-                       ihost->netdev = kstrdup(buf, GFP_KERNEL);
-               break;
+               return iscsi_switch_str_param(&ihost->netdev, buf);
        case ISCSI_HOST_PARAM_HWADDRESS:
-               if (!ihost->hwaddress)
-                       ihost->hwaddress = kstrdup(buf, GFP_KERNEL);
-               break;
+               return iscsi_switch_str_param(&ihost->hwaddress, buf);
        case ISCSI_HOST_PARAM_INITIATOR_NAME:
-               if (!ihost->initiatorname)
-                       ihost->initiatorname = kstrdup(buf, GFP_KERNEL);
-               break;
+               return iscsi_switch_str_param(&ihost->initiatorname, buf);
        default:
                return -ENOSYS;
        }
index b579ca9f4836a9c976b2c76856e1d0709650e1e4..2bc07090321da30562b3b21ef6c2b6b8dc51e887 100644 (file)
@@ -440,8 +440,8 @@ void iscsi_tcp_cleanup_task(struct iscsi_task *task)
        struct iscsi_tcp_task *tcp_task = task->dd_data;
        struct iscsi_r2t_info *r2t;
 
-       /* nothing to do for mgmt or pending tasks */
-       if (!task->sc || task->state == ISCSI_TASK_PENDING)
+       /* nothing to do for mgmt */
+       if (!task->sc)
                return;
 
        /* flush task's r2t queues */
@@ -473,7 +473,13 @@ static int iscsi_tcp_data_in(struct iscsi_conn *conn, struct iscsi_task *task)
        int datasn = be32_to_cpu(rhdr->datasn);
        unsigned total_in_length = scsi_in(task->sc)->length;
 
-       iscsi_update_cmdsn(conn->session, (struct iscsi_nopin*)rhdr);
+       /*
+        * lib iscsi will update this in the completion handling if there
+        * is status.
+        */
+       if (!(rhdr->flags & ISCSI_FLAG_DATA_STATUS))
+               iscsi_update_cmdsn(conn->session, (struct iscsi_nopin*)rhdr);
+
        if (tcp_conn->in.datalen == 0)
                return 0;
 
@@ -857,6 +863,12 @@ int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb,
        int rc = 0;
 
        ISCSI_DBG_TCP(conn, "in %d bytes\n", skb->len - offset);
+       /*
+        * Update for each skb instead of pdu, because over slow networks a
+        * data_in's data could take a while to read in. We also want to
+        * account for r2ts.
+        */
+       conn->last_recv = jiffies;
 
        if (unlikely(conn->suspend_rx)) {
                ISCSI_DBG_TCP(conn, "Rx suspended!\n");
index 1105f9a111ba416d956f89802b71f85de2b7f447..540569849099fc88cd774b2ce2549f1f586d0196 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2008 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2009 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
 
 struct lpfc_sli2_slim;
 
+#define LPFC_PCI_DEV_LP                0x1
+#define LPFC_PCI_DEV_OC                0x2
+
+#define LPFC_SLI_REV2          2
+#define LPFC_SLI_REV3          3
+#define LPFC_SLI_REV4          4
+
 #define LPFC_MAX_TARGET                4096    /* max number of targets supported */
 #define LPFC_MAX_DISC_THREADS  64      /* max outstanding discovery els
                                           requests */
@@ -98,9 +105,11 @@ struct lpfc_dma_pool {
 };
 
 struct hbq_dmabuf {
+       struct lpfc_dmabuf hbuf;
        struct lpfc_dmabuf dbuf;
        uint32_t size;
        uint32_t tag;
+       struct lpfc_rcqe rcqe;
 };
 
 /* Priority bit.  Set value to exceed low water mark in lpfc_mem. */
@@ -134,7 +143,10 @@ typedef struct lpfc_vpd {
        } rev;
        struct {
 #ifdef __BIG_ENDIAN_BITFIELD
-               uint32_t rsvd2  :24;  /* Reserved                             */
+               uint32_t rsvd3  :19;  /* Reserved                             */
+               uint32_t cdss   : 1;  /* Configure Data Security SLI          */
+               uint32_t rsvd2  : 3;  /* Reserved                             */
+               uint32_t cbg    : 1;  /* Configure BlockGuard                 */
                uint32_t cmv    : 1;  /* Configure Max VPIs                   */
                uint32_t ccrp   : 1;  /* Config Command Ring Polling          */
                uint32_t csah   : 1;  /* Configure Synchronous Abort Handling */
@@ -152,7 +164,10 @@ typedef struct lpfc_vpd {
                uint32_t csah   : 1;  /* Configure Synchronous Abort Handling */
                uint32_t ccrp   : 1;  /* Config Command Ring Polling          */
                uint32_t cmv    : 1;  /* Configure Max VPIs                   */
-               uint32_t rsvd2  :24;  /* Reserved                             */
+               uint32_t cbg    : 1;  /* Configure BlockGuard                 */
+               uint32_t rsvd2  : 3;  /* Reserved                             */
+               uint32_t cdss   : 1;  /* Configure Data Security SLI          */
+               uint32_t rsvd3  :19;  /* Reserved                             */
 #endif
        } sli3Feat;
 } lpfc_vpd_t;
@@ -264,8 +279,8 @@ enum hba_state {
 };
 
 struct lpfc_vport {
-       struct list_head listentry;
        struct lpfc_hba *phba;
+       struct list_head listentry;
        uint8_t port_type;
 #define LPFC_PHYSICAL_PORT 1
 #define LPFC_NPIV_PORT  2
@@ -273,6 +288,9 @@ struct lpfc_vport {
        enum discovery_state port_state;
 
        uint16_t vpi;
+       uint16_t vfi;
+       uint8_t vfi_state;
+#define LPFC_VFI_REGISTERED    0x1
 
        uint32_t fc_flag;       /* FC flags */
 /* Several of these flags are HBA centric and should be moved to
@@ -385,6 +403,9 @@ struct lpfc_vport {
 #endif
        uint8_t stat_data_enabled;
        uint8_t stat_data_blocked;
+       struct list_head rcv_buffer_list;
+       uint32_t vport_flag;
+#define STATIC_VPORT   1
 };
 
 struct hbq_s {
@@ -420,8 +441,66 @@ enum intr_type_t {
 };
 
 struct lpfc_hba {
+       /* SCSI interface function jump table entries */
+       int (*lpfc_new_scsi_buf)
+               (struct lpfc_vport *, int);
+       struct lpfc_scsi_buf * (*lpfc_get_scsi_buf)
+               (struct lpfc_hba *);
+       int (*lpfc_scsi_prep_dma_buf)
+               (struct lpfc_hba *, struct lpfc_scsi_buf *);
+       void (*lpfc_scsi_unprep_dma_buf)
+               (struct lpfc_hba *, struct lpfc_scsi_buf *);
+       void (*lpfc_release_scsi_buf)
+               (struct lpfc_hba *, struct lpfc_scsi_buf *);
+       void (*lpfc_rampdown_queue_depth)
+               (struct lpfc_hba *);
+       void (*lpfc_scsi_prep_cmnd)
+               (struct lpfc_vport *, struct lpfc_scsi_buf *,
+                struct lpfc_nodelist *);
+       int (*lpfc_scsi_prep_task_mgmt_cmd)
+               (struct lpfc_vport *, struct lpfc_scsi_buf *,
+                unsigned int, uint8_t);
+
+       /* IOCB interface function jump table entries */
+       int (*__lpfc_sli_issue_iocb)
+               (struct lpfc_hba *, uint32_t,
+                struct lpfc_iocbq *, uint32_t);
+       void (*__lpfc_sli_release_iocbq)(struct lpfc_hba *,
+                        struct lpfc_iocbq *);
+       int (*lpfc_hba_down_post)(struct lpfc_hba *phba);
+
+
+       IOCB_t * (*lpfc_get_iocb_from_iocbq)
+               (struct lpfc_iocbq *);
+       void (*lpfc_scsi_cmd_iocb_cmpl)
+               (struct lpfc_hba *, struct lpfc_iocbq *, struct lpfc_iocbq *);
+
+       /* MBOX interface function jump table entries */
+       int (*lpfc_sli_issue_mbox)
+               (struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
+       /* Slow-path IOCB process function jump table entries */
+       void (*lpfc_sli_handle_slow_ring_event)
+               (struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+                uint32_t mask);
+       /* INIT device interface function jump table entries */
+       int (*lpfc_sli_hbq_to_firmware)
+               (struct lpfc_hba *, uint32_t, struct hbq_dmabuf *);
+       int (*lpfc_sli_brdrestart)
+               (struct lpfc_hba *);
+       int (*lpfc_sli_brdready)
+               (struct lpfc_hba *, uint32_t);
+       void (*lpfc_handle_eratt)
+               (struct lpfc_hba *);
+       void (*lpfc_stop_port)
+               (struct lpfc_hba *);
+
+
+       /* SLI4 specific HBA data structure */
+       struct lpfc_sli4_hba sli4_hba;
+
        struct lpfc_sli sli;
-       uint32_t sli_rev;               /* SLI2 or SLI3 */
+       uint8_t pci_dev_grp;    /* lpfc PCI dev group: 0x0, 0x1, 0x2,... */
+       uint32_t sli_rev;               /* SLI2, SLI3, or SLI4 */
        uint32_t sli3_options;          /* Mask of enabled SLI3 options */
 #define LPFC_SLI3_HBQ_ENABLED          0x01
 #define LPFC_SLI3_NPIV_ENABLED         0x02
@@ -429,6 +508,7 @@ struct lpfc_hba {
 #define LPFC_SLI3_CRP_ENABLED          0x08
 #define LPFC_SLI3_INB_ENABLED          0x10
 #define LPFC_SLI3_BG_ENABLED           0x20
+#define LPFC_SLI3_DSS_ENABLED          0x40
        uint32_t iocb_cmd_size;
        uint32_t iocb_rsp_size;
 
@@ -442,8 +522,13 @@ struct lpfc_hba {
 
        uint32_t hba_flag;      /* hba generic flags */
 #define HBA_ERATT_HANDLED      0x1 /* This flag is set when eratt handled */
-
-#define DEFER_ERATT            0x4 /* Deferred error attention in progress */
+#define DEFER_ERATT            0x2 /* Deferred error attention in progress */
+#define HBA_FCOE_SUPPORT       0x4 /* HBA function supports FCOE */
+#define HBA_RECEIVE_BUFFER     0x8 /* Rcv buffer posted to worker thread */
+#define HBA_POST_RECEIVE_BUFFER 0x10 /* Rcv buffers need to be posted */
+#define FCP_XRI_ABORT_EVENT    0x20
+#define ELS_XRI_ABORT_EVENT    0x40
+#define ASYNC_EVENT            0x80
        struct lpfc_dmabuf slim2p;
 
        MAILBOX_t *mbox;
@@ -502,6 +587,9 @@ struct lpfc_hba {
        uint32_t cfg_poll;
        uint32_t cfg_poll_tmo;
        uint32_t cfg_use_msi;
+       uint32_t cfg_fcp_imax;
+       uint32_t cfg_fcp_wq_count;
+       uint32_t cfg_fcp_eq_count;
        uint32_t cfg_sg_seg_cnt;
        uint32_t cfg_prot_sg_seg_cnt;
        uint32_t cfg_sg_dma_buf_size;
@@ -511,6 +599,8 @@ struct lpfc_hba {
        uint32_t cfg_enable_hba_reset;
        uint32_t cfg_enable_hba_heartbeat;
        uint32_t cfg_enable_bg;
+       uint32_t cfg_enable_fip;
+       uint32_t cfg_log_verbose;
 
        lpfc_vpd_t vpd;         /* vital product data */
 
@@ -526,11 +616,12 @@ struct lpfc_hba {
        unsigned long data_flags;
 
        uint32_t hbq_in_use;            /* HBQs in use flag */
-       struct list_head hbqbuf_in_list;  /* in-fly hbq buffer list */
+       struct list_head rb_pend_list;  /* Received buffers to be processed */
        uint32_t hbq_count;             /* Count of configured HBQs */
        struct hbq_s hbqs[LPFC_MAX_HBQS]; /* local copy of hbq indicies  */
 
        unsigned long pci_bar0_map;     /* Physical address for PCI BAR0 */
+       unsigned long pci_bar1_map;     /* Physical address for PCI BAR1 */
        unsigned long pci_bar2_map;     /* Physical address for PCI BAR2 */
        void __iomem *slim_memmap_p;    /* Kernel memory mapped address for
                                           PCI BAR0 */
@@ -593,7 +684,8 @@ struct lpfc_hba {
        /* pci_mem_pools */
        struct pci_pool *lpfc_scsi_dma_buf_pool;
        struct pci_pool *lpfc_mbuf_pool;
-       struct pci_pool *lpfc_hbq_pool;
+       struct pci_pool *lpfc_hrb_pool; /* header receive buffer pool */
+       struct pci_pool *lpfc_drb_pool; /* data receive buffer pool */
        struct lpfc_dma_pool lpfc_mbuf_safety_pool;
 
        mempool_t *mbox_mem_pool;
@@ -609,6 +701,14 @@ struct lpfc_hba {
        struct lpfc_vport *pport;       /* physical lpfc_vport pointer */
        uint16_t max_vpi;               /* Maximum virtual nports */
 #define LPFC_MAX_VPI 0xFFFF            /* Max number of VPI supported */
+       uint16_t max_vports;            /*
+                                        * For IOV HBAs max_vpi can change
+                                        * after a reset. max_vports is max
+                                        * number of vports present. This can
+                                        * be greater than max_vpi.
+                                        */
+       uint16_t vpi_base;
+       uint16_t vfi_base;
        unsigned long *vpi_bmask;       /* vpi allocation table */
 
        /* Data structure used by fabric iocb scheduler */
@@ -667,6 +767,11 @@ struct lpfc_hba {
 /* Maximum number of events that can be outstanding at any time*/
 #define LPFC_MAX_EVT_COUNT 512
        atomic_t fast_event_count;
+       struct lpfc_fcf fcf;
+       uint8_t fc_map[3];
+       uint8_t valid_vlan;
+       uint16_t vlan_id;
+       struct list_head fcf_conn_rec_list;
 };
 
 static inline struct Scsi_Host *
index c14f0cbdb125024372d765c746585c4c8844882b..d73e677201f8df229784fce5fc48e039dba7ae56 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2008 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2009 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsi_transport_fc.h>
 
+#include "lpfc_hw4.h"
 #include "lpfc_hw.h"
 #include "lpfc_sli.h"
+#include "lpfc_sli4.h"
 #include "lpfc_nl.h"
 #include "lpfc_disc.h"
 #include "lpfc_scsi.h"
@@ -505,12 +507,14 @@ lpfc_issue_lip(struct Scsi_Host *shost)
                return -ENOMEM;
 
        memset((void *)pmboxq, 0, sizeof (LPFC_MBOXQ_t));
-       pmboxq->mb.mbxCommand = MBX_DOWN_LINK;
-       pmboxq->mb.mbxOwner = OWN_HOST;
+       pmboxq->u.mb.mbxCommand = MBX_DOWN_LINK;
+       pmboxq->u.mb.mbxOwner = OWN_HOST;
 
        mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, LPFC_MBOX_TMO * 2);
 
-       if ((mbxstatus == MBX_SUCCESS) && (pmboxq->mb.mbxStatus == 0)) {
+       if ((mbxstatus == MBX_SUCCESS) &&
+           (pmboxq->u.mb.mbxStatus == 0 ||
+            pmboxq->u.mb.mbxStatus == MBXERR_LINK_DOWN)) {
                memset((void *)pmboxq, 0, sizeof (LPFC_MBOXQ_t));
                lpfc_init_link(phba, pmboxq, phba->cfg_topology,
                               phba->cfg_link_speed);
@@ -789,7 +793,8 @@ lpfc_get_hba_info(struct lpfc_hba *phba,
                  uint32_t *mrpi, uint32_t *arpi,
                  uint32_t *mvpi, uint32_t *avpi)
 {
-       struct lpfc_sli   *psli = &phba->sli;
+       struct lpfc_sli *psli = &phba->sli;
+       struct lpfc_mbx_read_config *rd_config;
        LPFC_MBOXQ_t *pmboxq;
        MAILBOX_t *pmb;
        int rc = 0;
@@ -800,7 +805,7 @@ lpfc_get_hba_info(struct lpfc_hba *phba,
         */
        if (phba->link_state < LPFC_LINK_DOWN ||
            !phba->mbox_mem_pool ||
-           (phba->sli.sli_flag & LPFC_SLI2_ACTIVE) == 0)
+           (phba->sli.sli_flag & LPFC_SLI_ACTIVE) == 0)
                return 0;
 
        if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO)
@@ -811,13 +816,13 @@ lpfc_get_hba_info(struct lpfc_hba *phba,
                return 0;
        memset(pmboxq, 0, sizeof (LPFC_MBOXQ_t));
 
-       pmb = &pmboxq->mb;
+       pmb = &pmboxq->u.mb;
        pmb->mbxCommand = MBX_READ_CONFIG;
        pmb->mbxOwner = OWN_HOST;
        pmboxq->context1 = NULL;
 
        if ((phba->pport->fc_flag & FC_OFFLINE_MODE) ||
-               (!(psli->sli_flag & LPFC_SLI2_ACTIVE)))
+               (!(psli->sli_flag & LPFC_SLI_ACTIVE)))
                rc = MBX_NOT_FINISHED;
        else
                rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
@@ -828,18 +833,37 @@ lpfc_get_hba_info(struct lpfc_hba *phba,
                return 0;
        }
 
-       if (mrpi)
-               *mrpi = pmb->un.varRdConfig.max_rpi;
-       if (arpi)
-               *arpi = pmb->un.varRdConfig.avail_rpi;
-       if (mxri)
-               *mxri = pmb->un.varRdConfig.max_xri;
-       if (axri)
-               *axri = pmb->un.varRdConfig.avail_xri;
-       if (mvpi)
-               *mvpi = pmb->un.varRdConfig.max_vpi;
-       if (avpi)
-               *avpi = pmb->un.varRdConfig.avail_vpi;
+       if (phba->sli_rev == LPFC_SLI_REV4) {
+               rd_config = &pmboxq->u.mqe.un.rd_config;
+               if (mrpi)
+                       *mrpi = bf_get(lpfc_mbx_rd_conf_rpi_count, rd_config);
+               if (arpi)
+                       *arpi = bf_get(lpfc_mbx_rd_conf_rpi_count, rd_config) -
+                                       phba->sli4_hba.max_cfg_param.rpi_used;
+               if (mxri)
+                       *mxri = bf_get(lpfc_mbx_rd_conf_xri_count, rd_config);
+               if (axri)
+                       *axri = bf_get(lpfc_mbx_rd_conf_xri_count, rd_config) -
+                                       phba->sli4_hba.max_cfg_param.xri_used;
+               if (mvpi)
+                       *mvpi = bf_get(lpfc_mbx_rd_conf_vpi_count, rd_config);
+               if (avpi)
+                       *avpi = bf_get(lpfc_mbx_rd_conf_vpi_count, rd_config) -
+                                       phba->sli4_hba.max_cfg_param.vpi_used;
+       } else {
+               if (mrpi)
+                       *mrpi = pmb->un.varRdConfig.max_rpi;
+               if (arpi)
+                       *arpi = pmb->un.varRdConfig.avail_rpi;
+               if (mxri)
+                       *mxri = pmb->un.varRdConfig.max_xri;
+               if (axri)
+                       *axri = pmb->un.varRdConfig.avail_xri;
+               if (mvpi)
+                       *mvpi = pmb->un.varRdConfig.max_vpi;
+               if (avpi)
+                       *avpi = pmb->un.varRdConfig.avail_vpi;
+       }
 
        mempool_free(pmboxq, phba->mbox_mem_pool);
        return 1;
@@ -2021,22 +2045,9 @@ static DEVICE_ATTR(lpfc_devloss_tmo, S_IRUGO | S_IWUSR,
 # 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_FCP                       0x40       FCP traffic history
-# LOG_NODE                      0x80       Node table events
-# LOG_BG                        0x200      BlockBuard events
-# LOG_MISC                      0x400      Miscellaneous events
-# LOG_SLI                       0x800      SLI events
-# LOG_FCP_ERROR                 0x1000     Only log FCP errors
-# LOG_LIBDFC                    0x2000     LIBDFC events
-# LOG_ALL_MSG                   0xffff     LOG all messages
+# See lpfc_logmsh.h for definitions.
 */
-LPFC_VPORT_ATTR_HEX_RW(log_verbose, 0x0, 0x0, 0xffff,
+LPFC_VPORT_ATTR_HEX_RW(log_verbose, 0x0, 0x0, 0xffffffff,
                       "Verbose logging bit-mask");
 
 /*
@@ -2266,6 +2277,36 @@ lpfc_param_init(topology, 0, 0, 6)
 static DEVICE_ATTR(lpfc_topology, S_IRUGO | S_IWUSR,
                lpfc_topology_show, lpfc_topology_store);
 
+/**
+ * lpfc_static_vport_show: Read callback function for
+ *   lpfc_static_vport sysfs file.
+ * @dev: Pointer to class device object.
+ * @attr: device attribute structure.
+ * @buf: Data buffer.
+ *
+ * This function is the read call back function for
+ * lpfc_static_vport sysfs file. The lpfc_static_vport
+ * sysfs file report the mageability of the vport.
+ **/
+static ssize_t
+lpfc_static_vport_show(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       struct Scsi_Host  *shost = class_to_shost(dev);
+       struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+       if (vport->vport_flag & STATIC_VPORT)
+               sprintf(buf, "1\n");
+       else
+               sprintf(buf, "0\n");
+
+       return strlen(buf);
+}
+
+/*
+ * Sysfs attribute to control the statistical data collection.
+ */
+static DEVICE_ATTR(lpfc_static_vport, S_IRUGO,
+                  lpfc_static_vport_show, NULL);
 
 /**
  * lpfc_stat_data_ctrl_store - write call back for lpfc_stat_data_ctrl sysfs file
@@ -2341,7 +2382,7 @@ lpfc_stat_data_ctrl_store(struct device *dev, struct device_attribute *attr,
                if (vports == NULL)
                        return -ENOMEM;
 
-               for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+               for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
                        v_shost = lpfc_shost_from_vport(vports[i]);
                        spin_lock_irq(v_shost->host_lock);
                        /* Block and reset data collection */
@@ -2356,7 +2397,7 @@ lpfc_stat_data_ctrl_store(struct device *dev, struct device_attribute *attr,
                phba->bucket_base = base;
                phba->bucket_step = step;
 
-               for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+               for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
                        v_shost = lpfc_shost_from_vport(vports[i]);
 
                        /* Unblock data collection */
@@ -2373,7 +2414,7 @@ lpfc_stat_data_ctrl_store(struct device *dev, struct device_attribute *attr,
                if (vports == NULL)
                        return -ENOMEM;
 
-               for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+               for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
                        v_shost = lpfc_shost_from_vport(vports[i]);
                        spin_lock_irq(shost->host_lock);
                        vports[i]->stat_data_blocked = 1;
@@ -2844,14 +2885,38 @@ LPFC_ATTR_RW(poll_tmo, 10, 1, 255,
 /*
 # lpfc_use_msi: Use MSI (Message Signaled Interrupts) in systems that
 #              support this feature
-#       0  = MSI disabled
+#       0  = MSI disabled (default)
 #       1  = MSI enabled
-#       2  = MSI-X enabled (default)
-# Value range is [0,2]. Default value is 2.
+#       2  = MSI-X enabled
+# Value range is [0,2]. Default value is 0.
 */
-LPFC_ATTR_R(use_msi, 2, 0, 2, "Use Message Signaled Interrupts (1) or "
+LPFC_ATTR_R(use_msi, 0, 0, 2, "Use Message Signaled Interrupts (1) or "
            "MSI-X (2), if possible");
 
+/*
+# lpfc_fcp_imax: Set the maximum number of fast-path FCP interrupts per second
+#
+# Value range is [636,651042]. Default value is 10000.
+*/
+LPFC_ATTR_R(fcp_imax, LPFC_FP_DEF_IMAX, LPFC_MIM_IMAX, LPFC_DMULT_CONST,
+           "Set the maximum number of fast-path FCP interrupts per second");
+
+/*
+# lpfc_fcp_wq_count: Set the number of fast-path FCP work queues
+#
+# Value range is [1,31]. Default value is 4.
+*/
+LPFC_ATTR_R(fcp_wq_count, LPFC_FP_WQN_DEF, LPFC_FP_WQN_MIN, LPFC_FP_WQN_MAX,
+           "Set the number of fast-path FCP work queues, if possible");
+
+/*
+# lpfc_fcp_eq_count: Set the number of fast-path FCP event queues
+#
+# Value range is [1,7]. Default value is 1.
+*/
+LPFC_ATTR_R(fcp_eq_count, LPFC_FP_EQN_DEF, LPFC_FP_EQN_MIN, LPFC_FP_EQN_MAX,
+           "Set the number of fast-path FCP event queues, if possible");
+
 /*
 # lpfc_enable_hba_reset: Allow or prevent HBA resets to the hardware.
 #       0  = HBA resets disabled
@@ -2876,6 +2941,14 @@ LPFC_ATTR_R(enable_hba_heartbeat, 1, 0, 1, "Enable HBA Heartbeat.");
 */
 LPFC_ATTR_R(enable_bg, 0, 0, 1, "Enable BlockGuard Support");
 
+/*
+# lpfc_enable_fip: When set, FIP is required to start discovery. If not
+# set, the driver will add an FCF record manually if the port has no
+# FCF records available and start discovery.
+# Value range is [0,1]. Default value is 1 (enabled)
+*/
+LPFC_ATTR_RW(enable_fip, 0, 0, 1, "Enable FIP Discovery");
+
 
 /*
 # lpfc_prot_mask: i
@@ -2942,6 +3015,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
        &dev_attr_lpfc_peer_port_login,
        &dev_attr_lpfc_nodev_tmo,
        &dev_attr_lpfc_devloss_tmo,
+       &dev_attr_lpfc_enable_fip,
        &dev_attr_lpfc_fcp_class,
        &dev_attr_lpfc_use_adisc,
        &dev_attr_lpfc_ack0,
@@ -2969,6 +3043,9 @@ struct device_attribute *lpfc_hba_attrs[] = {
        &dev_attr_lpfc_poll,
        &dev_attr_lpfc_poll_tmo,
        &dev_attr_lpfc_use_msi,
+       &dev_attr_lpfc_fcp_imax,
+       &dev_attr_lpfc_fcp_wq_count,
+       &dev_attr_lpfc_fcp_eq_count,
        &dev_attr_lpfc_enable_bg,
        &dev_attr_lpfc_soft_wwnn,
        &dev_attr_lpfc_soft_wwpn,
@@ -2991,6 +3068,7 @@ struct device_attribute *lpfc_vport_attrs[] = {
        &dev_attr_lpfc_lun_queue_depth,
        &dev_attr_lpfc_nodev_tmo,
        &dev_attr_lpfc_devloss_tmo,
+       &dev_attr_lpfc_enable_fip,
        &dev_attr_lpfc_hba_queue_depth,
        &dev_attr_lpfc_peer_port_login,
        &dev_attr_lpfc_restrict_login,
@@ -3003,6 +3081,7 @@ struct device_attribute *lpfc_vport_attrs[] = {
        &dev_attr_lpfc_enable_da_id,
        &dev_attr_lpfc_max_scsicmpl_time,
        &dev_attr_lpfc_stat_data_ctrl,
+       &dev_attr_lpfc_static_vport,
        NULL,
 };
 
@@ -3199,7 +3278,7 @@ sysfs_mbox_write(struct kobject *kobj, struct bin_attribute *bin_attr,
                }
        }
 
-       memcpy((uint8_t *) & phba->sysfs_mbox.mbox->mb + off,
+       memcpy((uint8_t *) &phba->sysfs_mbox.mbox->u.mb + off,
               buf, count);
 
        phba->sysfs_mbox.offset = off + count;
@@ -3241,6 +3320,7 @@ sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr,
        struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
        struct lpfc_hba   *phba = vport->phba;
        int rc;
+       MAILBOX_t *pmb;
 
        if (off > MAILBOX_CMD_SIZE)
                return -ERANGE;
@@ -3265,8 +3345,8 @@ sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr,
        if (off == 0 &&
            phba->sysfs_mbox.state  == SMBOX_WRITING &&
            phba->sysfs_mbox.offset >= 2 * sizeof(uint32_t)) {
-
-               switch (phba->sysfs_mbox.mbox->mb.mbxCommand) {
+               pmb = &phba->sysfs_mbox.mbox->u.mb;
+               switch (pmb->mbxCommand) {
                        /* Offline only */
                case MBX_INIT_LINK:
                case MBX_DOWN_LINK:
@@ -3283,7 +3363,7 @@ sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr,
                        if (!(vport->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);
+                                      pmb->mbxCommand);
                                sysfs_mbox_idle(phba);
                                spin_unlock_irq(&phba->hbalock);
                                return -EPERM;
@@ -3319,13 +3399,13 @@ sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr,
                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);
+                              pmb->mbxCommand);
                        sysfs_mbox_idle(phba);
                        spin_unlock_irq(&phba->hbalock);
                        return -EPERM;
                default:
                        printk(KERN_WARNING "mbox_read: Unknown Command 0x%x\n",
-                              phba->sysfs_mbox.mbox->mb.mbxCommand);
+                              pmb->mbxCommand);
                        sysfs_mbox_idle(phba);
                        spin_unlock_irq(&phba->hbalock);
                        return -EPERM;
@@ -3335,14 +3415,14 @@ sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr,
                 * or RESTART mailbox commands until the HBA is restarted.
                 */
                if (phba->pport->stopped &&
-                   phba->sysfs_mbox.mbox->mb.mbxCommand != MBX_DUMP_MEMORY &&
-                   phba->sysfs_mbox.mbox->mb.mbxCommand != MBX_RESTART &&
-                   phba->sysfs_mbox.mbox->mb.mbxCommand != MBX_WRITE_VPARMS &&
-                   phba->sysfs_mbox.mbox->mb.mbxCommand != MBX_WRITE_WWN)
+                   pmb->mbxCommand != MBX_DUMP_MEMORY &&
+                   pmb->mbxCommand != MBX_RESTART &&
+                   pmb->mbxCommand != MBX_WRITE_VPARMS &&
+                   pmb->mbxCommand != MBX_WRITE_WWN)
                        lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
                                        "1259 mbox: Issued mailbox cmd "
                                        "0x%x while in stopped state.\n",
-                                       phba->sysfs_mbox.mbox->mb.mbxCommand);
+                                       pmb->mbxCommand);
 
                phba->sysfs_mbox.mbox->vport = vport;
 
@@ -3356,7 +3436,7 @@ sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr,
                }
 
                if ((vport->fc_flag & FC_OFFLINE_MODE) ||
-                   (!(phba->sli.sli_flag & LPFC_SLI2_ACTIVE))){
+                   (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))) {
 
                        spin_unlock_irq(&phba->hbalock);
                        rc = lpfc_sli_issue_mbox (phba,
@@ -3368,8 +3448,7 @@ sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr,
                        spin_unlock_irq(&phba->hbalock);
                        rc = lpfc_sli_issue_mbox_wait (phba,
                                                       phba->sysfs_mbox.mbox,
-                               lpfc_mbox_tmo_val(phba,
-                                   phba->sysfs_mbox.mbox->mb.mbxCommand) * HZ);
+                               lpfc_mbox_tmo_val(phba, pmb->mbxCommand) * HZ);
                        spin_lock_irq(&phba->hbalock);
                }
 
@@ -3391,7 +3470,7 @@ sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr,
                return -EAGAIN;
        }
 
-       memcpy(buf, (uint8_t *) & phba->sysfs_mbox.mbox->mb + off, count);
+       memcpy(buf, (uint8_t *) &pmb + off, count);
 
        phba->sysfs_mbox.offset = off + count;
 
@@ -3585,6 +3664,9 @@ lpfc_get_host_speed(struct Scsi_Host *shost)
                        case LA_8GHZ_LINK:
                                fc_host_speed(shost) = FC_PORTSPEED_8GBIT;
                        break;
+                       case LA_10GHZ_LINK:
+                               fc_host_speed(shost) = FC_PORTSPEED_10GBIT;
+                       break;
                        default:
                                fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
                        break;
@@ -3652,7 +3734,7 @@ lpfc_get_stats(struct Scsi_Host *shost)
         */
        if (phba->link_state < LPFC_LINK_DOWN ||
            !phba->mbox_mem_pool ||
-           (phba->sli.sli_flag & LPFC_SLI2_ACTIVE) == 0)
+           (phba->sli.sli_flag & LPFC_SLI_ACTIVE) == 0)
                return NULL;
 
        if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO)
@@ -3663,14 +3745,14 @@ lpfc_get_stats(struct Scsi_Host *shost)
                return NULL;
        memset(pmboxq, 0, sizeof (LPFC_MBOXQ_t));
 
-       pmb = &pmboxq->mb;
+       pmb = &pmboxq->u.mb;
        pmb->mbxCommand = MBX_READ_STATUS;
        pmb->mbxOwner = OWN_HOST;
        pmboxq->context1 = NULL;
        pmboxq->vport = vport;
 
        if ((vport->fc_flag & FC_OFFLINE_MODE) ||
-               (!(psli->sli_flag & LPFC_SLI2_ACTIVE)))
+               (!(psli->sli_flag & LPFC_SLI_ACTIVE)))
                rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
        else
                rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
@@ -3695,7 +3777,7 @@ lpfc_get_stats(struct Scsi_Host *shost)
        pmboxq->vport = vport;
 
        if ((vport->fc_flag & FC_OFFLINE_MODE) ||
-           (!(psli->sli_flag & LPFC_SLI2_ACTIVE)))
+           (!(psli->sli_flag & LPFC_SLI_ACTIVE)))
                rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
        else
                rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
@@ -3769,7 +3851,7 @@ lpfc_reset_stats(struct Scsi_Host *shost)
                return;
        memset(pmboxq, 0, sizeof(LPFC_MBOXQ_t));
 
-       pmb = &pmboxq->mb;
+       pmb = &pmboxq->u.mb;
        pmb->mbxCommand = MBX_READ_STATUS;
        pmb->mbxOwner = OWN_HOST;
        pmb->un.varWords[0] = 0x1; /* reset request */
@@ -3777,7 +3859,7 @@ lpfc_reset_stats(struct Scsi_Host *shost)
        pmboxq->vport = vport;
 
        if ((vport->fc_flag & FC_OFFLINE_MODE) ||
-               (!(psli->sli_flag & LPFC_SLI2_ACTIVE)))
+               (!(psli->sli_flag & LPFC_SLI_ACTIVE)))
                rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
        else
                rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
@@ -3795,7 +3877,7 @@ lpfc_reset_stats(struct Scsi_Host *shost)
        pmboxq->vport = vport;
 
        if ((vport->fc_flag & FC_OFFLINE_MODE) ||
-           (!(psli->sli_flag & LPFC_SLI2_ACTIVE)))
+           (!(psli->sli_flag & LPFC_SLI_ACTIVE)))
                rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
        else
                rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
@@ -3962,6 +4044,21 @@ lpfc_set_vport_symbolic_name(struct fc_vport *fc_vport)
                lpfc_ns_cmd(vport, SLI_CTNS_RSPN_ID, 0, 0);
 }
 
+/**
+ * lpfc_hba_log_verbose_init - Set hba's log verbose level
+ * @phba: Pointer to lpfc_hba struct.
+ *
+ * This function is called by the lpfc_get_cfgparam() routine to set the
+ * module lpfc_log_verbose into the @phba cfg_log_verbose for use with
+ * log messsage according to the module's lpfc_log_verbose parameter setting
+ * before hba port or vport created.
+ **/
+static void
+lpfc_hba_log_verbose_init(struct lpfc_hba *phba, uint32_t verbose)
+{
+       phba->cfg_log_verbose = verbose;
+}
+
 struct fc_function_template lpfc_transport_functions = {
        /* fixed attributes the driver supports */
        .show_host_node_name = 1,
@@ -4105,6 +4202,9 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
        lpfc_poll_tmo_init(phba, lpfc_poll_tmo);
        lpfc_enable_npiv_init(phba, lpfc_enable_npiv);
        lpfc_use_msi_init(phba, lpfc_use_msi);
+       lpfc_fcp_imax_init(phba, lpfc_fcp_imax);
+       lpfc_fcp_wq_count_init(phba, lpfc_fcp_wq_count);
+       lpfc_fcp_eq_count_init(phba, lpfc_fcp_eq_count);
        lpfc_enable_hba_reset_init(phba, lpfc_enable_hba_reset);
        lpfc_enable_hba_heartbeat_init(phba, lpfc_enable_hba_heartbeat);
        lpfc_enable_bg_init(phba, lpfc_enable_bg);
@@ -4113,26 +4213,10 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
        phba->cfg_soft_wwpn = 0L;
        lpfc_sg_seg_cnt_init(phba, lpfc_sg_seg_cnt);
        lpfc_prot_sg_seg_cnt_init(phba, lpfc_prot_sg_seg_cnt);
-       /*
-        * Since the sg_tablesize is module parameter, the sg_dma_buf_size
-        * used to create the sg_dma_buf_pool must be dynamically calculated.
-        * 2 segments are added since the IOCB needs a command and response bde.
-        */
-       phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd) +
-                       sizeof(struct fcp_rsp) +
-                       ((phba->cfg_sg_seg_cnt + 2) * sizeof(struct ulp_bde64));
-
-       if (phba->cfg_enable_bg) {
-               phba->cfg_sg_seg_cnt = LPFC_MAX_SG_SEG_CNT;
-               phba->cfg_sg_dma_buf_size +=
-                       phba->cfg_prot_sg_seg_cnt * sizeof(struct ulp_bde64);
-       }
-
-       /* Also reinitialize the host templates with new values. */
-       lpfc_vport_template.sg_tablesize = phba->cfg_sg_seg_cnt;
-       lpfc_template.sg_tablesize = phba->cfg_sg_seg_cnt;
-
        lpfc_hba_queue_depth_init(phba, lpfc_hba_queue_depth);
+       lpfc_enable_fip_init(phba, lpfc_enable_fip);
+       lpfc_hba_log_verbose_init(phba, lpfc_log_verbose);
+
        return;
 }
 
index f88ce3f261900c50fce0d33a0abff80ade5d6ad9..d2a922997c0fa17faf05299fd4a85c4894e39e7c 100644 (file)
@@ -23,6 +23,8 @@ typedef int (*node_filter)(struct lpfc_nodelist *, void *);
 struct fc_rport;
 void lpfc_dump_mem(struct lpfc_hba *, LPFC_MBOXQ_t *, uint16_t);
 void lpfc_dump_wakeup_param(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_dump_static_vport(struct lpfc_hba *, LPFC_MBOXQ_t *, uint16_t);
+int lpfc_dump_fcoe_param(struct lpfc_hba *, struct lpfcMboxq *);
 void lpfc_read_nv(struct lpfc_hba *, LPFC_MBOXQ_t *);
 void lpfc_config_async(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
 
@@ -35,17 +37,19 @@ int lpfc_config_msi(struct lpfc_hba *, LPFC_MBOXQ_t *);
 int lpfc_read_sparam(struct lpfc_hba *, LPFC_MBOXQ_t *, int);
 void lpfc_read_config(struct lpfc_hba *, LPFC_MBOXQ_t *);
 void lpfc_read_lnk_stat(struct lpfc_hba *, LPFC_MBOXQ_t *);
-int lpfc_reg_login(struct lpfc_hba *, uint16_t, uint32_t, uint8_t *,
-                  LPFC_MBOXQ_t *, uint32_t);
+int lpfc_reg_rpi(struct lpfc_hba *, uint16_t, uint32_t, uint8_t *,
+                LPFC_MBOXQ_t *, uint32_t);
 void lpfc_unreg_login(struct lpfc_hba *, uint16_t, uint32_t, LPFC_MBOXQ_t *);
 void lpfc_unreg_did(struct lpfc_hba *, uint16_t, uint32_t, LPFC_MBOXQ_t *);
-void lpfc_reg_vpi(struct lpfc_hba *, uint16_t, uint32_t, LPFC_MBOXQ_t *);
+void lpfc_reg_vpi(struct lpfc_vport *, LPFC_MBOXQ_t *);
 void lpfc_unreg_vpi(struct lpfc_hba *, uint16_t, LPFC_MBOXQ_t *);
 void lpfc_init_link(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t);
+void lpfc_request_features(struct lpfc_hba *, struct lpfcMboxq *);
 
 struct lpfc_vport *lpfc_find_vport_by_did(struct lpfc_hba *, uint32_t);
 void lpfc_cleanup_rpis(struct lpfc_vport *, int);
 int lpfc_linkdown(struct lpfc_hba *);
+void lpfc_linkdown_port(struct lpfc_vport *);
 void lpfc_port_link_failure(struct lpfc_vport *);
 void lpfc_mbx_cmpl_read_la(struct lpfc_hba *, LPFC_MBOXQ_t *);
 
@@ -54,6 +58,7 @@ void lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *, LPFC_MBOXQ_t *);
 void lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *);
 void lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *);
 void lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_mbx_cmpl_reg_vfi(struct lpfc_hba *, LPFC_MBOXQ_t *);
 void lpfc_enqueue_node(struct lpfc_vport *, struct lpfc_nodelist *);
 void lpfc_dequeue_node(struct lpfc_vport *, struct lpfc_nodelist *);
 struct lpfc_nodelist *lpfc_enable_node(struct lpfc_vport *,
@@ -105,6 +110,7 @@ int lpfc_issue_els_adisc(struct lpfc_vport *, struct lpfc_nodelist *, uint8_t);
 int lpfc_issue_els_logo(struct lpfc_vport *, struct lpfc_nodelist *, uint8_t);
 int lpfc_issue_els_npiv_logo(struct lpfc_vport *, struct lpfc_nodelist *);
 int lpfc_issue_els_scr(struct lpfc_vport *, uint32_t, uint8_t);
+int lpfc_issue_fabric_reglogin(struct lpfc_vport *);
 int lpfc_els_free_iocb(struct lpfc_hba *, struct lpfc_iocbq *);
 int lpfc_ct_free_iocb(struct lpfc_hba *, struct lpfc_iocbq *);
 int lpfc_els_rsp_acc(struct lpfc_vport *, uint32_t, struct lpfc_iocbq *,
@@ -149,15 +155,19 @@ int lpfc_online(struct lpfc_hba *);
 void lpfc_unblock_mgmt_io(struct lpfc_hba *);
 void lpfc_offline_prep(struct lpfc_hba *);
 void lpfc_offline(struct lpfc_hba *);
+void lpfc_reset_hba(struct lpfc_hba *);
 
 int lpfc_sli_setup(struct lpfc_hba *);
 int lpfc_sli_queue_setup(struct lpfc_hba *);
 
 void lpfc_handle_eratt(struct lpfc_hba *);
 void lpfc_handle_latt(struct lpfc_hba *);
-irqreturn_t lpfc_intr_handler(int, void *);
-irqreturn_t lpfc_sp_intr_handler(int, void *);
-irqreturn_t lpfc_fp_intr_handler(int, void *);
+irqreturn_t lpfc_sli_intr_handler(int, void *);
+irqreturn_t lpfc_sli_sp_intr_handler(int, void *);
+irqreturn_t lpfc_sli_fp_intr_handler(int, void *);
+irqreturn_t lpfc_sli4_intr_handler(int, void *);
+irqreturn_t lpfc_sli4_sp_intr_handler(int, void *);
+irqreturn_t lpfc_sli4_fp_intr_handler(int, void *);
 
 void lpfc_read_rev(struct lpfc_hba *, LPFC_MBOXQ_t *);
 void lpfc_config_ring(struct lpfc_hba *, int, LPFC_MBOXQ_t *);
@@ -165,16 +175,32 @@ void lpfc_config_port(struct lpfc_hba *, LPFC_MBOXQ_t *);
 void lpfc_kill_board(struct lpfc_hba *, LPFC_MBOXQ_t *);
 void lpfc_mbox_put(struct lpfc_hba *, LPFC_MBOXQ_t *);
 LPFC_MBOXQ_t *lpfc_mbox_get(struct lpfc_hba *);
+void __lpfc_mbox_cmpl_put(struct lpfc_hba *, LPFC_MBOXQ_t *);
 void lpfc_mbox_cmpl_put(struct lpfc_hba *, LPFC_MBOXQ_t *);
+int lpfc_mbox_cmd_check(struct lpfc_hba *, LPFC_MBOXQ_t *);
+int lpfc_mbox_dev_check(struct lpfc_hba *);
 int lpfc_mbox_tmo_val(struct lpfc_hba *, int);
+void lpfc_init_vfi(struct lpfcMboxq *, struct lpfc_vport *);
+void lpfc_reg_vfi(struct lpfcMboxq *, struct lpfc_vport *, dma_addr_t);
+void lpfc_init_vpi(struct lpfcMboxq *, uint16_t);
+void lpfc_unreg_vfi(struct lpfcMboxq *, uint16_t);
+void lpfc_reg_fcfi(struct lpfc_hba *, struct lpfcMboxq *);
+void lpfc_unreg_fcfi(struct lpfcMboxq *, uint16_t);
+void lpfc_resume_rpi(struct lpfcMboxq *, struct lpfc_nodelist *);
 
 void lpfc_config_hbq(struct lpfc_hba *, uint32_t, struct lpfc_hbq_init *,
        uint32_t , LPFC_MBOXQ_t *);
 struct hbq_dmabuf *lpfc_els_hbq_alloc(struct lpfc_hba *);
 void lpfc_els_hbq_free(struct lpfc_hba *, struct hbq_dmabuf *);
+struct hbq_dmabuf *lpfc_sli4_rb_alloc(struct lpfc_hba *);
+void lpfc_sli4_rb_free(struct lpfc_hba *, struct hbq_dmabuf *);
+void lpfc_sli4_build_dflt_fcf_record(struct lpfc_hba *, struct fcf_record *,
+                       uint16_t);
+void lpfc_unregister_unused_fcf(struct lpfc_hba *);
 
-int lpfc_mem_alloc(struct lpfc_hba *);
+int lpfc_mem_alloc(struct lpfc_hba *, int align);
 void lpfc_mem_free(struct lpfc_hba *);
+void lpfc_mem_free_all(struct lpfc_hba *);
 void lpfc_stop_vport_timers(struct lpfc_vport *);
 
 void lpfc_poll_timeout(unsigned long ptr);
@@ -186,6 +212,7 @@ void lpfc_sli_release_iocbq(struct lpfc_hba *, struct lpfc_iocbq *);
 uint16_t lpfc_sli_next_iotag(struct lpfc_hba *, struct lpfc_iocbq *);
 void lpfc_sli_cancel_iocbs(struct lpfc_hba *, struct list_head *, uint32_t,
                           uint32_t);
+void lpfc_sli_wake_mbox_wait(struct lpfc_hba *, LPFC_MBOXQ_t *);
 
 void lpfc_reset_barrier(struct lpfc_hba * phba);
 int lpfc_sli_brdready(struct lpfc_hba *, uint32_t);
@@ -198,12 +225,13 @@ int lpfc_sli_host_down(struct lpfc_vport *);
 int lpfc_sli_hba_down(struct lpfc_hba *);
 int lpfc_sli_issue_mbox(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
 int lpfc_sli_handle_mb_event(struct lpfc_hba *);
-int lpfc_sli_flush_mbox_queue(struct lpfc_hba *);
+void lpfc_sli_mbox_sys_shutdown(struct lpfc_hba *);
 int lpfc_sli_check_eratt(struct lpfc_hba *);
-int lpfc_sli_handle_slow_ring_event(struct lpfc_hba *,
+void lpfc_sli_handle_slow_ring_event(struct lpfc_hba *,
                                    struct lpfc_sli_ring *, uint32_t);
+int lpfc_sli4_handle_received_buffer(struct lpfc_hba *);
 void lpfc_sli_def_mbox_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *);
-int lpfc_sli_issue_iocb(struct lpfc_hba *, struct lpfc_sli_ring *,
+int lpfc_sli_issue_iocb(struct lpfc_hba *, uint32_t,
                        struct lpfc_iocbq *, uint32_t);
 void lpfc_sli_pcimem_bcopy(void *, void *, uint32_t);
 void lpfc_sli_abort_iocb_ring(struct lpfc_hba *, struct lpfc_sli_ring *);
@@ -237,7 +265,7 @@ struct lpfc_nodelist *lpfc_findnode_wwpn(struct lpfc_vport *,
 
 int lpfc_sli_issue_mbox_wait(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
 
-int lpfc_sli_issue_iocb_wait(struct lpfc_hba *, struct lpfc_sli_ring *,
+int lpfc_sli_issue_iocb_wait(struct lpfc_hba *, uint32_t,
                             struct lpfc_iocbq *, struct lpfc_iocbq *,
                             uint32_t);
 void lpfc_sli_abort_fcp_cmpl(struct lpfc_hba *, struct lpfc_iocbq *,
@@ -254,6 +282,12 @@ void lpfc_in_buf_free(struct lpfc_hba *, struct lpfc_dmabuf *);
 const char* lpfc_info(struct Scsi_Host *);
 int lpfc_scan_finished(struct Scsi_Host *, unsigned long);
 
+int lpfc_init_api_table_setup(struct lpfc_hba *, uint8_t);
+int lpfc_sli_api_table_setup(struct lpfc_hba *, uint8_t);
+int lpfc_scsi_api_table_setup(struct lpfc_hba *, uint8_t);
+int lpfc_mbox_api_table_setup(struct lpfc_hba *, uint8_t);
+int lpfc_api_table_setup(struct lpfc_hba *, uint8_t);
+
 void lpfc_get_cfgparam(struct lpfc_hba *);
 void lpfc_get_vport_cfgparam(struct lpfc_vport *);
 int lpfc_alloc_sysfs_attr(struct lpfc_vport *);
@@ -314,8 +348,15 @@ lpfc_send_els_failure_event(struct lpfc_hba *, struct lpfc_iocbq *,
                                struct lpfc_iocbq *);
 struct lpfc_fast_path_event *lpfc_alloc_fast_evt(struct lpfc_hba *);
 void lpfc_free_fast_evt(struct lpfc_hba *, struct lpfc_fast_path_event *);
+void lpfc_create_static_vport(struct lpfc_hba *);
+void lpfc_stop_hba_timers(struct lpfc_hba *);
+void lpfc_stop_port(struct lpfc_hba *);
+void lpfc_parse_fcoe_conf(struct lpfc_hba *, uint8_t *, uint32_t);
+int lpfc_parse_vpd(struct lpfc_hba *, uint8_t *, int);
+void lpfc_start_fdiscs(struct lpfc_hba *phba);
 
 #define ScsiResult(host_code, scsi_code) (((host_code) << 16) | scsi_code)
 #define HBA_EVENT_RSCN                   5
 #define HBA_EVENT_LINK_UP                2
 #define HBA_EVENT_LINK_DOWN              3
+
index 896c7b0351e51dcc2c529056442982129d0d02bf..1dbccfd3d022a6339bad81a04f291ec41abe89fb 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2008 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2009 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  *                                                                 *
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport_fc.h>
 
+#include "lpfc_hw4.h"
 #include "lpfc_hw.h"
 #include "lpfc_sli.h"
+#include "lpfc_sli4.h"
 #include "lpfc_nl.h"
 #include "lpfc_disc.h"
 #include "lpfc_scsi.h"
@@ -267,8 +269,6 @@ lpfc_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp,
             uint32_t tmo, uint8_t retry)
 {
        struct lpfc_hba  *phba = vport->phba;
-       struct lpfc_sli  *psli = &phba->sli;
-       struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING];
        IOCB_t *icmd;
        struct lpfc_iocbq *geniocb;
        int rc;
@@ -331,7 +331,7 @@ lpfc_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp,
        geniocb->drvrTimeout = icmd->ulpTimeout + LPFC_DRVR_TIMEOUT;
        geniocb->vport = vport;
        geniocb->retry = retry;
-       rc = lpfc_sli_issue_iocb(phba, pring, geniocb, 0);
+       rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, geniocb, 0);
 
        if (rc == IOCB_ERROR) {
                lpfc_sli_release_iocbq(phba, geniocb);
@@ -1578,6 +1578,9 @@ lpfc_fdmi_cmd(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, int cmdcode)
                                case LA_8GHZ_LINK:
                                        ae->un.PortSpeed = HBA_PORTSPEED_8GBIT;
                                break;
+                               case LA_10GHZ_LINK:
+                                       ae->un.PortSpeed = HBA_PORTSPEED_10GBIT;
+                               break;
                                default:
                                        ae->un.PortSpeed =
                                                HBA_PORTSPEED_UNKNOWN;
@@ -1730,7 +1733,7 @@ lpfc_decode_firmware_rev(struct lpfc_hba *phba, char *fwrevision, int flag)
        uint8_t *fwname;
 
        if (vp->rev.rBit) {
-               if (psli->sli_flag & LPFC_SLI2_ACTIVE)
+               if (psli->sli_flag & LPFC_SLI_ACTIVE)
                        rev = vp->rev.sli2FwRev;
                else
                        rev = vp->rev.sli1FwRev;
@@ -1756,7 +1759,7 @@ lpfc_decode_firmware_rev(struct lpfc_hba *phba, char *fwrevision, int flag)
                }
                b4 = (rev & 0x0000000f);
 
-               if (psli->sli_flag & LPFC_SLI2_ACTIVE)
+               if (psli->sli_flag & LPFC_SLI_ACTIVE)
                        fwname = vp->rev.sli2FwName;
                else
                        fwname = vp->rev.sli1FwName;
index 52be5644e07ad6a9d6538c68a78e6ebe907e3f26..2b02b1fb39a09842a1b18216699ac31b83eef3bb 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2007-2008 Emulex.  All rights reserved.           *
+ * Copyright (C) 2007-2009 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  *                                                                 *
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport_fc.h>
 
+#include "lpfc_hw4.h"
 #include "lpfc_hw.h"
 #include "lpfc_sli.h"
+#include "lpfc_sli4.h"
 #include "lpfc_nl.h"
 #include "lpfc_disc.h"
 #include "lpfc_scsi.h"
@@ -280,6 +282,8 @@ lpfc_debugfs_hbqinfo_data(struct lpfc_hba *phba, char *buf, int size)
        struct lpfc_dmabuf *d_buf;
        struct hbq_dmabuf *hbq_buf;
 
+       if (phba->sli_rev != 3)
+               return 0;
        cnt = LPFC_HBQINFO_SIZE;
        spin_lock_irq(&phba->hbalock);
 
@@ -489,12 +493,15 @@ lpfc_debugfs_dumpHostSlim_data(struct lpfc_hba *phba, char *buf, int size)
                                 pring->next_cmdidx, pring->local_getidx,
                                 pring->flag, pgpp->rspPutInx, pring->numRiocb);
        }
-       word0 = readl(phba->HAregaddr);
-       word1 = readl(phba->CAregaddr);
-       word2 = readl(phba->HSregaddr);
-       word3 = readl(phba->HCregaddr);
-       len +=  snprintf(buf+len, size-len, "HA:%08x CA:%08x HS:%08x HC:%08x\n",
-       word0, word1, word2, word3);
+
+       if (phba->sli_rev <= LPFC_SLI_REV3) {
+               word0 = readl(phba->HAregaddr);
+               word1 = readl(phba->CAregaddr);
+               word2 = readl(phba->HSregaddr);
+               word3 = readl(phba->HCregaddr);
+               len +=  snprintf(buf+len, size-len, "HA:%08x CA:%08x HS:%08x "
+                                "HC:%08x\n", word0, word1, word2, word3);
+       }
        spin_unlock_irq(&phba->hbalock);
        return len;
 }
index ffd1089720728a7732a3c8f0707e068138ca71b6..1142070e948424c8f6b678ef5b5b93cc500dfb6b 100644 (file)
@@ -135,6 +135,7 @@ struct lpfc_nodelist {
 #define NLP_NODEV_REMOVE   0x08000000  /* Defer removal till discovery ends */
 #define NLP_TARGET_REMOVE  0x10000000   /* Target remove in process */
 #define NLP_SC_REQ         0x20000000  /* Target requires authentication */
+#define NLP_RPI_VALID      0x80000000  /* nlp_rpi is valid */
 
 /* ndlp usage management macros */
 #define NLP_CHK_NODE_ACT(ndlp)         (((ndlp)->nlp_usg_map \
index b8b34cf5c3d2fd2129c24a16c9fdb08d12229884..6bdeb14878a2b094eb7f175bea7982cdd337b957 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2008 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2009 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport_fc.h>
 
+#include "lpfc_hw4.h"
 #include "lpfc_hw.h"
 #include "lpfc_sli.h"
+#include "lpfc_sli4.h"
 #include "lpfc_nl.h"
 #include "lpfc_disc.h"
 #include "lpfc_scsi.h"
@@ -84,7 +86,8 @@ lpfc_els_chk_latt(struct lpfc_vport *vport)
        uint32_t ha_copy;
 
        if (vport->port_state >= LPFC_VPORT_READY ||
-           phba->link_state == LPFC_LINK_DOWN)
+           phba->link_state == LPFC_LINK_DOWN ||
+           phba->sli_rev > LPFC_SLI_REV3)
                return 0;
 
        /* Read the HBA Host Attention Register */
@@ -219,7 +222,7 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
                icmd->un.elsreq64.myID = vport->fc_myDID;
 
                /* For ELS_REQUEST64_CR, use the VPI by default */
-               icmd->ulpContext = vport->vpi;
+               icmd->ulpContext = vport->vpi + phba->vpi_base;
                icmd->ulpCt_h = 0;
                /* The CT field must be 0=INVALID_RPI for the ECHO cmd */
                if (elscmd == ELS_CMD_ECHO)
@@ -305,7 +308,7 @@ els_iocb_free_pcmb_exit:
  *   0 - successfully issued fabric registration login for @vport
  *   -ENXIO -- failed to issue fabric registration login for @vport
  **/
-static int
+int
 lpfc_issue_fabric_reglogin(struct lpfc_vport *vport)
 {
        struct lpfc_hba  *phba = vport->phba;
@@ -345,8 +348,7 @@ lpfc_issue_fabric_reglogin(struct lpfc_vport *vport)
                err = 4;
                goto fail;
        }
-       rc = lpfc_reg_login(phba, vport->vpi, Fabric_DID, (uint8_t *)sp, mbox,
-                           0);
+       rc = lpfc_reg_rpi(phba, vport->vpi, Fabric_DID, (uint8_t *)sp, mbox, 0);
        if (rc) {
                err = 5;
                goto fail_free_mbox;
@@ -385,6 +387,75 @@ fail:
        return -ENXIO;
 }
 
+/**
+ * lpfc_issue_reg_vfi - Register VFI for this vport's fabric login
+ * @vport: pointer to a host virtual N_Port data structure.
+ *
+ * This routine issues a REG_VFI mailbox for the vfi, vpi, fcfi triplet for
+ * the @vport. This mailbox command is necessary for FCoE only.
+ *
+ * Return code
+ *   0 - successfully issued REG_VFI for @vport
+ *   A failure code otherwise.
+ **/
+static int
+lpfc_issue_reg_vfi(struct lpfc_vport *vport)
+{
+       struct lpfc_hba  *phba = vport->phba;
+       LPFC_MBOXQ_t *mboxq;
+       struct lpfc_nodelist *ndlp;
+       struct serv_parm *sp;
+       struct lpfc_dmabuf *dmabuf;
+       int rc = 0;
+
+       sp = &phba->fc_fabparam;
+       ndlp = lpfc_findnode_did(vport, Fabric_DID);
+       if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) {
+               rc = -ENODEV;
+               goto fail;
+       }
+
+       dmabuf = kzalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+       if (!dmabuf) {
+               rc = -ENOMEM;
+               goto fail;
+       }
+       dmabuf->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &dmabuf->phys);
+       if (!dmabuf->virt) {
+               rc = -ENOMEM;
+               goto fail_free_dmabuf;
+       }
+       mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mboxq) {
+               rc = -ENOMEM;
+               goto fail_free_coherent;
+       }
+       vport->port_state = LPFC_FABRIC_CFG_LINK;
+       memcpy(dmabuf->virt, &phba->fc_fabparam, sizeof(vport->fc_sparam));
+       lpfc_reg_vfi(mboxq, vport, dmabuf->phys);
+       mboxq->mbox_cmpl = lpfc_mbx_cmpl_reg_vfi;
+       mboxq->vport = vport;
+       mboxq->context1 = dmabuf;
+       rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
+       if (rc == MBX_NOT_FINISHED) {
+               rc = -ENXIO;
+               goto fail_free_mbox;
+       }
+       return 0;
+
+fail_free_mbox:
+       mempool_free(mboxq, phba->mbox_mem_pool);
+fail_free_coherent:
+       lpfc_mbuf_free(phba, dmabuf->virt, dmabuf->phys);
+fail_free_dmabuf:
+       kfree(dmabuf);
+fail:
+       lpfc_vport_set_state(vport, FC_VPORT_FAILED);
+       lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+               "0289 Issue Register VFI failed: Err %d\n", rc);
+       return rc;
+}
+
 /**
  * lpfc_cmpl_els_flogi_fabric - Completion function for flogi to a fabric port
  * @vport: pointer to a host virtual N_Port data structure.
@@ -497,17 +568,24 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
                }
        }
 
-       lpfc_nlp_set_state(vport, ndlp, NLP_STE_REG_LOGIN_ISSUE);
-
-       if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED &&
-           vport->fc_flag & FC_VPORT_NEEDS_REG_VPI) {
-               lpfc_register_new_vport(phba, vport, ndlp);
-               return 0;
+       if (phba->sli_rev < LPFC_SLI_REV4) {
+               lpfc_nlp_set_state(vport, ndlp, NLP_STE_REG_LOGIN_ISSUE);
+               if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED &&
+                   vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)
+                       lpfc_register_new_vport(phba, vport, ndlp);
+               else
+                       lpfc_issue_fabric_reglogin(vport);
+       } else {
+               ndlp->nlp_type |= NLP_FABRIC;
+               lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
+               if (vport->vfi_state & LPFC_VFI_REGISTERED) {
+                       lpfc_start_fdiscs(phba);
+                       lpfc_do_scr_ns_plogi(phba, vport);
+               } else
+                       lpfc_issue_reg_vfi(vport);
        }
-       lpfc_issue_fabric_reglogin(vport);
        return 0;
 }
-
 /**
  * lpfc_cmpl_els_flogi_nport - Completion function for flogi to an N_Port
  * @vport: pointer to a host virtual N_Port data structure.
@@ -815,9 +893,14 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
        if (sp->cmn.fcphHigh < FC_PH3)
                sp->cmn.fcphHigh = FC_PH3;
 
-       if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {
+       if  (phba->sli_rev == LPFC_SLI_REV4) {
+               elsiocb->iocb.ulpCt_h = ((SLI4_CT_FCFI >> 1) & 1);
+               elsiocb->iocb.ulpCt_l = (SLI4_CT_FCFI & 1);
+               /* FLOGI needs to be 3 for WQE FCFI */
+               /* Set the fcfi to the fcfi we registered with */
+               elsiocb->iocb.ulpContext = phba->fcf.fcfi;
+       } else if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {
                sp->cmn.request_multiple_Nport = 1;
-
                /* For FLOGI, Let FLOGI rsp set the NPortID for VPI 0 */
                icmd->ulpCt_h = 1;
                icmd->ulpCt_l = 0;
@@ -930,6 +1013,8 @@ lpfc_initial_flogi(struct lpfc_vport *vport)
                if (!ndlp)
                        return 0;
                lpfc_nlp_init(vport, ndlp, Fabric_DID);
+               /* Set the node type */
+               ndlp->nlp_type |= NLP_FABRIC;
                /* Put ndlp onto node list */
                lpfc_enqueue_node(vport, ndlp);
        } else if (!NLP_CHK_NODE_ACT(ndlp)) {
@@ -1350,14 +1435,12 @@ lpfc_issue_els_plogi(struct lpfc_vport *vport, uint32_t did, uint8_t retry)
        IOCB_t *icmd;
        struct lpfc_nodelist *ndlp;
        struct lpfc_iocbq *elsiocb;
-       struct lpfc_sli_ring *pring;
        struct lpfc_sli *psli;
        uint8_t *pcmd;
        uint16_t cmdsize;
        int ret;
 
        psli = &phba->sli;
-       pring = &psli->ring[LPFC_ELS_RING];     /* ELS ring */
 
        ndlp = lpfc_findnode_did(vport, did);
        if (ndlp && !NLP_CHK_NODE_ACT(ndlp))
@@ -1391,7 +1474,7 @@ lpfc_issue_els_plogi(struct lpfc_vport *vport, uint32_t did, uint8_t retry)
 
        phba->fc_stat.elsXmitPLOGI++;
        elsiocb->iocb_cmpl = lpfc_cmpl_els_plogi;
-       ret = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
+       ret = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
 
        if (ret == IOCB_ERROR) {
                lpfc_els_free_iocb(phba, elsiocb);
@@ -1501,14 +1584,9 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
        PRLI *npr;
        IOCB_t *icmd;
        struct lpfc_iocbq *elsiocb;
-       struct lpfc_sli_ring *pring;
-       struct lpfc_sli *psli;
        uint8_t *pcmd;
        uint16_t cmdsize;
 
-       psli = &phba->sli;
-       pring = &psli->ring[LPFC_ELS_RING];     /* ELS ring */
-
        cmdsize = (sizeof(uint32_t) + sizeof(PRLI));
        elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp,
                                     ndlp->nlp_DID, ELS_CMD_PRLI);
@@ -1550,7 +1628,8 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
        spin_lock_irq(shost->host_lock);
        ndlp->nlp_flag |= NLP_PRLI_SND;
        spin_unlock_irq(shost->host_lock);
-       if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+       if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
+           IOCB_ERROR) {
                spin_lock_irq(shost->host_lock);
                ndlp->nlp_flag &= ~NLP_PRLI_SND;
                spin_unlock_irq(shost->host_lock);
@@ -1608,7 +1687,8 @@ lpfc_adisc_done(struct lpfc_vport *vport)
         * and continue discovery.
         */
        if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) &&
-           !(vport->fc_flag & FC_RSCN_MODE)) {
+           !(vport->fc_flag & FC_RSCN_MODE) &&
+           (phba->sli_rev < LPFC_SLI_REV4)) {
                lpfc_issue_reg_vpi(phba, vport);
                return;
        }
@@ -1788,8 +1868,6 @@ lpfc_issue_els_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
        ADISC *ap;
        IOCB_t *icmd;
        struct lpfc_iocbq *elsiocb;
-       struct lpfc_sli *psli = &phba->sli;
-       struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING];
        uint8_t *pcmd;
        uint16_t cmdsize;
 
@@ -1822,7 +1900,8 @@ lpfc_issue_els_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
        spin_lock_irq(shost->host_lock);
        ndlp->nlp_flag |= NLP_ADISC_SND;
        spin_unlock_irq(shost->host_lock);
-       if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+       if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
+           IOCB_ERROR) {
                spin_lock_irq(shost->host_lock);
                ndlp->nlp_flag &= ~NLP_ADISC_SND;
                spin_unlock_irq(shost->host_lock);
@@ -1937,15 +2016,10 @@ lpfc_issue_els_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
        struct lpfc_hba  *phba = vport->phba;
        IOCB_t *icmd;
        struct lpfc_iocbq *elsiocb;
-       struct lpfc_sli_ring *pring;
-       struct lpfc_sli *psli;
        uint8_t *pcmd;
        uint16_t cmdsize;
        int rc;
 
-       psli = &phba->sli;
-       pring = &psli->ring[LPFC_ELS_RING];
-
        spin_lock_irq(shost->host_lock);
        if (ndlp->nlp_flag & NLP_LOGO_SND) {
                spin_unlock_irq(shost->host_lock);
@@ -1978,7 +2052,7 @@ lpfc_issue_els_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
        spin_lock_irq(shost->host_lock);
        ndlp->nlp_flag |= NLP_LOGO_SND;
        spin_unlock_irq(shost->host_lock);
-       rc = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
+       rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
 
        if (rc == IOCB_ERROR) {
                spin_lock_irq(shost->host_lock);
@@ -2058,14 +2132,12 @@ lpfc_issue_els_scr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
        struct lpfc_hba  *phba = vport->phba;
        IOCB_t *icmd;
        struct lpfc_iocbq *elsiocb;
-       struct lpfc_sli_ring *pring;
        struct lpfc_sli *psli;
        uint8_t *pcmd;
        uint16_t cmdsize;
        struct lpfc_nodelist *ndlp;
 
        psli = &phba->sli;
-       pring = &psli->ring[LPFC_ELS_RING];     /* ELS ring */
        cmdsize = (sizeof(uint32_t) + sizeof(SCR));
 
        ndlp = lpfc_findnode_did(vport, nportid);
@@ -2108,7 +2180,8 @@ lpfc_issue_els_scr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
 
        phba->fc_stat.elsXmitSCR++;
        elsiocb->iocb_cmpl = lpfc_cmpl_els_cmd;
-       if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+       if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
+           IOCB_ERROR) {
                /* The additional lpfc_nlp_put will cause the following
                 * lpfc_els_free_iocb routine to trigger the rlease of
                 * the node.
@@ -2152,7 +2225,6 @@ lpfc_issue_els_farpr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
        struct lpfc_hba  *phba = vport->phba;
        IOCB_t *icmd;
        struct lpfc_iocbq *elsiocb;
-       struct lpfc_sli_ring *pring;
        struct lpfc_sli *psli;
        FARP *fp;
        uint8_t *pcmd;
@@ -2162,7 +2234,6 @@ lpfc_issue_els_farpr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
        struct lpfc_nodelist *ndlp;
 
        psli = &phba->sli;
-       pring = &psli->ring[LPFC_ELS_RING];     /* ELS ring */
        cmdsize = (sizeof(uint32_t) + sizeof(FARP));
 
        ndlp = lpfc_findnode_did(vport, nportid);
@@ -2219,7 +2290,8 @@ lpfc_issue_els_farpr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
 
        phba->fc_stat.elsXmitFARPR++;
        elsiocb->iocb_cmpl = lpfc_cmpl_els_cmd;
-       if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+       if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
+           IOCB_ERROR) {
                /* The additional lpfc_nlp_put will cause the following
                 * lpfc_els_free_iocb routine to trigger the release of
                 * the node.
@@ -2949,6 +3021,14 @@ lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
        struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1);
        struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) pmb->context2;
 
+       /*
+        * This routine is used to register and unregister in previous SLI
+        * modes.
+        */
+       if ((pmb->u.mb.mbxCommand == MBX_UNREG_LOGIN) &&
+           (phba->sli_rev == LPFC_SLI_REV4))
+               lpfc_sli4_free_rpi(phba, pmb->u.mb.un.varUnregLogin.rpi);
+
        pmb->context1 = NULL;
        lpfc_mbuf_free(phba, mp->virt, mp->phys);
        kfree(mp);
@@ -2961,6 +3041,7 @@ lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
                 */
                lpfc_nlp_not_used(ndlp);
        }
+
        return;
 }
 
@@ -3170,7 +3251,6 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag,
        IOCB_t *icmd;
        IOCB_t *oldcmd;
        struct lpfc_iocbq *elsiocb;
-       struct lpfc_sli_ring *pring;
        struct lpfc_sli *psli;
        uint8_t *pcmd;
        uint16_t cmdsize;
@@ -3178,7 +3258,6 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag,
        ELS_PKT *els_pkt_ptr;
 
        psli = &phba->sli;
-       pring = &psli->ring[LPFC_ELS_RING];     /* ELS ring */
        oldcmd = &oldiocb->iocb;
 
        switch (flag) {
@@ -3266,7 +3345,7 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag,
        }
 
        phba->fc_stat.elsXmitACC++;
-       rc = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
+       rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
        if (rc == IOCB_ERROR) {
                lpfc_els_free_iocb(phba, elsiocb);
                return 1;
@@ -3305,15 +3384,12 @@ lpfc_els_rsp_reject(struct lpfc_vport *vport, uint32_t rejectError,
        IOCB_t *icmd;
        IOCB_t *oldcmd;
        struct lpfc_iocbq *elsiocb;
-       struct lpfc_sli_ring *pring;
        struct lpfc_sli *psli;
        uint8_t *pcmd;
        uint16_t cmdsize;
        int rc;
 
        psli = &phba->sli;
-       pring = &psli->ring[LPFC_ELS_RING];     /* ELS ring */
-
        cmdsize = 2 * sizeof(uint32_t);
        elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, oldiocb->retry, ndlp,
                                     ndlp->nlp_DID, ELS_CMD_LS_RJT);
@@ -3346,7 +3422,7 @@ lpfc_els_rsp_reject(struct lpfc_vport *vport, uint32_t rejectError,
 
        phba->fc_stat.elsXmitLSRJT++;
        elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
-       rc = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
+       rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
 
        if (rc == IOCB_ERROR) {
                lpfc_els_free_iocb(phba, elsiocb);
@@ -3379,8 +3455,6 @@ lpfc_els_rsp_adisc_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb,
                       struct lpfc_nodelist *ndlp)
 {
        struct lpfc_hba  *phba = vport->phba;
-       struct lpfc_sli  *psli = &phba->sli;
-       struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING];
        ADISC *ap;
        IOCB_t *icmd, *oldcmd;
        struct lpfc_iocbq *elsiocb;
@@ -3422,7 +3496,7 @@ lpfc_els_rsp_adisc_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb,
 
        phba->fc_stat.elsXmitACC++;
        elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
-       rc = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
+       rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
        if (rc == IOCB_ERROR) {
                lpfc_els_free_iocb(phba, elsiocb);
                return 1;
@@ -3459,14 +3533,12 @@ lpfc_els_rsp_prli_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb,
        IOCB_t *icmd;
        IOCB_t *oldcmd;
        struct lpfc_iocbq *elsiocb;
-       struct lpfc_sli_ring *pring;
        struct lpfc_sli *psli;
        uint8_t *pcmd;
        uint16_t cmdsize;
        int rc;
 
        psli = &phba->sli;
-       pring = &psli->ring[LPFC_ELS_RING];     /* ELS ring */
 
        cmdsize = sizeof(uint32_t) + sizeof(PRLI);
        elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, oldiocb->retry, ndlp,
@@ -3520,7 +3592,7 @@ lpfc_els_rsp_prli_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb,
        phba->fc_stat.elsXmitACC++;
        elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
 
-       rc = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
+       rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
        if (rc == IOCB_ERROR) {
                lpfc_els_free_iocb(phba, elsiocb);
                return 1;
@@ -3562,15 +3634,12 @@ lpfc_els_rsp_rnid_acc(struct lpfc_vport *vport, uint8_t format,
        RNID *rn;
        IOCB_t *icmd, *oldcmd;
        struct lpfc_iocbq *elsiocb;
-       struct lpfc_sli_ring *pring;
        struct lpfc_sli *psli;
        uint8_t *pcmd;
        uint16_t cmdsize;
        int rc;
 
        psli = &phba->sli;
-       pring = &psli->ring[LPFC_ELS_RING];
-
        cmdsize = sizeof(uint32_t) + sizeof(uint32_t)
                                        + (2 * sizeof(struct lpfc_name));
        if (format)
@@ -3626,7 +3695,7 @@ lpfc_els_rsp_rnid_acc(struct lpfc_vport *vport, uint8_t format,
        elsiocb->context1 = NULL;  /* Don't need ndlp for cmpl,
                                    * it could be freed */
 
-       rc = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
+       rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
        if (rc == IOCB_ERROR) {
                lpfc_els_free_iocb(phba, elsiocb);
                return 1;
@@ -3839,7 +3908,9 @@ lpfc_rscn_payload_check(struct lpfc_vport *vport, uint32_t did)
                        payload_len -= sizeof(uint32_t);
                        switch (rscn_did.un.b.resv & RSCN_ADDRESS_FORMAT_MASK) {
                        case RSCN_ADDRESS_FORMAT_PORT:
-                               if (ns_did.un.word == rscn_did.un.word)
+                               if ((ns_did.un.b.domain == rscn_did.un.b.domain)
+                                   && (ns_did.un.b.area == rscn_did.un.b.area)
+                                   && (ns_did.un.b.id == rscn_did.un.b.id))
                                        goto return_did_out;
                                break;
                        case RSCN_ADDRESS_FORMAT_AREA:
@@ -4300,7 +4371,7 @@ lpfc_els_rcv_flogi(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
                        lpfc_init_link(phba, mbox,
                                       phba->cfg_topology,
                                       phba->cfg_link_speed);
-                       mbox->mb.un.varInitLnk.lipsr_AL_PA = 0;
+                       mbox->u.mb.un.varInitLnk.lipsr_AL_PA = 0;
                        mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
                        mbox->vport = vport;
                        rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
@@ -4440,8 +4511,6 @@ lpfc_els_rcv_lirr(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
 static void
 lpfc_els_rsp_rps_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
 {
-       struct lpfc_sli *psli = &phba->sli;
-       struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING];
        MAILBOX_t *mb;
        IOCB_t *icmd;
        RPS_RSP *rps_rsp;
@@ -4451,7 +4520,7 @@ lpfc_els_rsp_rps_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
        uint16_t xri, status;
        uint32_t cmdsize;
 
-       mb = &pmb->mb;
+       mb = &pmb->u.mb;
 
        ndlp = (struct lpfc_nodelist *) pmb->context2;
        xri = (uint16_t) ((unsigned long)(pmb->context1));
@@ -4507,7 +4576,7 @@ lpfc_els_rsp_rps_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
                         ndlp->nlp_rpi);
        elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
        phba->fc_stat.elsXmitACC++;
-       if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR)
+       if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) == IOCB_ERROR)
                lpfc_els_free_iocb(phba, elsiocb);
        return;
 }
@@ -4616,8 +4685,6 @@ lpfc_els_rsp_rpl_acc(struct lpfc_vport *vport, uint16_t cmdsize,
        IOCB_t *icmd, *oldcmd;
        RPL_RSP rpl_rsp;
        struct lpfc_iocbq *elsiocb;
-       struct lpfc_sli *psli = &phba->sli;
-       struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING];
        uint8_t *pcmd;
 
        elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, oldiocb->retry, ndlp,
@@ -4654,7 +4721,8 @@ lpfc_els_rsp_rpl_acc(struct lpfc_vport *vport, uint16_t cmdsize,
                         ndlp->nlp_rpi);
        elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
        phba->fc_stat.elsXmitACC++;
-       if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+       if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
+           IOCB_ERROR) {
                lpfc_els_free_iocb(phba, elsiocb);
                return 1;
        }
@@ -4883,7 +4951,10 @@ lpfc_els_rcv_fan(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
                } else {
                        /* FAN verified - skip FLOGI */
                        vport->fc_myDID = vport->fc_prevDID;
-                       lpfc_issue_fabric_reglogin(vport);
+                       if (phba->sli_rev < LPFC_SLI_REV4)
+                               lpfc_issue_fabric_reglogin(vport);
+                       else
+                               lpfc_issue_reg_vfi(vport);
                }
        }
        return 0;
@@ -5566,11 +5637,10 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
 
 dropit:
        if (vport && !(vport->load_flag & FC_UNLOADING))
-               lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
-                       "(%d):0111 Dropping received ELS cmd "
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+                       "0111 Dropping received ELS cmd "
                        "Data: x%x x%x x%x\n",
-                       vport->vpi, icmd->ulpStatus,
-                       icmd->un.ulpWord[4], icmd->ulpTimeout);
+                       icmd->ulpStatus, icmd->un.ulpWord[4], icmd->ulpTimeout);
        phba->fc_stat.elsRcvDrop++;
 }
 
@@ -5646,10 +5716,9 @@ lpfc_els_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
             icmd->ulpCommand == CMD_IOCB_RCV_SEQ64_CX)) {
                if (icmd->unsli3.rcvsli3.vpi == 0xffff)
                        vport = phba->pport;
-               else {
-                       uint16_t vpi = icmd->unsli3.rcvsli3.vpi;
-                       vport = lpfc_find_vport_by_vpid(phba, vpi);
-               }
+               else
+                       vport = lpfc_find_vport_by_vpid(phba,
+                               icmd->unsli3.rcvsli3.vpi - phba->vpi_base);
        }
        /* If there are no BDEs associated
         * with this IOCB, there is nothing to do.
@@ -5781,7 +5850,7 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
        struct lpfc_vport *vport = pmb->vport;
        struct Scsi_Host  *shost = lpfc_shost_from_vport(vport);
        struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) pmb->context2;
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
 
        spin_lock_irq(shost->host_lock);
        vport->fc_flag &= ~FC_VPORT_NEEDS_REG_VPI;
@@ -5818,7 +5887,10 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
 
        } else {
                if (vport == phba->pport)
-                       lpfc_issue_fabric_reglogin(vport);
+                       if (phba->sli_rev < LPFC_SLI_REV4)
+                               lpfc_issue_fabric_reglogin(vport);
+                       else
+                               lpfc_issue_reg_vfi(vport);
                else
                        lpfc_do_scr_ns_plogi(phba, vport);
        }
@@ -5850,7 +5922,7 @@ lpfc_register_new_vport(struct lpfc_hba *phba, struct lpfc_vport *vport,
 
        mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
        if (mbox) {
-               lpfc_reg_vpi(phba, vport->vpi, vport->fc_myDID, mbox);
+               lpfc_reg_vpi(vport, mbox);
                mbox->vport = vport;
                mbox->context2 = lpfc_nlp_get(ndlp);
                mbox->mbox_cmpl = lpfc_cmpl_reg_new_vport;
@@ -6139,7 +6211,6 @@ lpfc_issue_els_npiv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
 {
        struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
        struct lpfc_hba  *phba = vport->phba;
-       struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
        IOCB_t *icmd;
        struct lpfc_iocbq *elsiocb;
        uint8_t *pcmd;
@@ -6169,7 +6240,8 @@ lpfc_issue_els_npiv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
        spin_lock_irq(shost->host_lock);
        ndlp->nlp_flag |= NLP_LOGO_SND;
        spin_unlock_irq(shost->host_lock);
-       if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+       if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
+           IOCB_ERROR) {
                spin_lock_irq(shost->host_lock);
                ndlp->nlp_flag &= ~NLP_LOGO_SND;
                spin_unlock_irq(shost->host_lock);
@@ -6224,7 +6296,6 @@ lpfc_resume_fabric_iocbs(struct lpfc_hba *phba)
        struct lpfc_iocbq *iocb;
        unsigned long iflags;
        int ret;
-       struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
        IOCB_t *cmd;
 
 repeat:
@@ -6248,7 +6319,7 @@ repeat:
                        "Fabric sched1:   ste:x%x",
                        iocb->vport->port_state, 0, 0);
 
-               ret = lpfc_sli_issue_iocb(phba, pring, iocb, 0);
+               ret = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, iocb, 0);
 
                if (ret == IOCB_ERROR) {
                        iocb->iocb_cmpl = iocb->fabric_iocb_cmpl;
@@ -6394,7 +6465,6 @@ static int
 lpfc_issue_fabric_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *iocb)
 {
        unsigned long iflags;
-       struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
        int ready;
        int ret;
 
@@ -6418,7 +6488,7 @@ lpfc_issue_fabric_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *iocb)
                        "Fabric sched2:   ste:x%x",
                        iocb->vport->port_state, 0, 0);
 
-               ret = lpfc_sli_issue_iocb(phba, pring, iocb, 0);
+               ret = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, iocb, 0);
 
                if (ret == IOCB_ERROR) {
                        iocb->iocb_cmpl = iocb->fabric_iocb_cmpl;
@@ -6524,3 +6594,38 @@ void lpfc_fabric_abort_hba(struct lpfc_hba *phba)
        lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
                              IOERR_SLI_ABORTED);
 }
+
+/**
+ * lpfc_sli4_els_xri_aborted - Slow-path process of els xri abort
+ * @phba: pointer to lpfc hba data structure.
+ * @axri: pointer to the els xri abort wcqe structure.
+ *
+ * This routine is invoked by the worker thread to process a SLI4 slow-path
+ * ELS aborted xri.
+ **/
+void
+lpfc_sli4_els_xri_aborted(struct lpfc_hba *phba,
+                         struct sli4_wcqe_xri_aborted *axri)
+{
+       uint16_t xri = bf_get(lpfc_wcqe_xa_xri, axri);
+       struct lpfc_sglq *sglq_entry = NULL, *sglq_next = NULL;
+       unsigned long iflag = 0;
+
+       spin_lock_irqsave(&phba->sli4_hba.abts_sgl_list_lock, iflag);
+       list_for_each_entry_safe(sglq_entry, sglq_next,
+                       &phba->sli4_hba.lpfc_abts_els_sgl_list, list) {
+               if (sglq_entry->sli4_xritag == xri) {
+                       list_del(&sglq_entry->list);
+                       spin_unlock_irqrestore(
+                                       &phba->sli4_hba.abts_sgl_list_lock,
+                                        iflag);
+                       spin_lock_irqsave(&phba->hbalock, iflag);
+
+                       list_add_tail(&sglq_entry->list,
+                               &phba->sli4_hba.lpfc_sgl_list);
+                       spin_unlock_irqrestore(&phba->hbalock, iflag);
+                       return;
+               }
+       }
+       spin_unlock_irqrestore(&phba->sli4_hba.abts_sgl_list_lock, iflag);
+}
index e764ce0bf7049e2f074bc8d00e1b8d329504e489..35c41ae75be248a15f7344734f1b77210b99bedf 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2008 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2009 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport_fc.h>
 
+#include "lpfc_hw4.h"
 #include "lpfc_hw.h"
 #include "lpfc_nl.h"
 #include "lpfc_disc.h"
 #include "lpfc_sli.h"
+#include "lpfc_sli4.h"
 #include "lpfc_scsi.h"
 #include "lpfc.h"
 #include "lpfc_logmsg.h"
@@ -273,6 +275,8 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
            !(ndlp->nlp_flag & NLP_NPR_2B_DISC) &&
            (ndlp->nlp_state != NLP_STE_UNMAPPED_NODE))
                lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM);
+
+       lpfc_unregister_unused_fcf(phba);
 }
 
 /**
@@ -295,10 +299,11 @@ lpfc_alloc_fast_evt(struct lpfc_hba *phba) {
 
        ret = kzalloc(sizeof(struct lpfc_fast_path_event),
                        GFP_ATOMIC);
-       if (ret)
+       if (ret) {
                atomic_inc(&phba->fast_event_count);
-       INIT_LIST_HEAD(&ret->work_evt.evt_listp);
-       ret->work_evt.evt = LPFC_EVT_FASTPATH_MGMT_EVT;
+               INIT_LIST_HEAD(&ret->work_evt.evt_listp);
+               ret->work_evt.evt = LPFC_EVT_FASTPATH_MGMT_EVT;
+       }
        return ret;
 }
 
@@ -491,6 +496,10 @@ lpfc_work_done(struct lpfc_hba *phba)
        phba->work_ha = 0;
        spin_unlock_irq(&phba->hbalock);
 
+       /* First, try to post the next mailbox command to SLI4 device */
+       if (phba->pci_dev_grp == LPFC_PCI_DEV_OC)
+               lpfc_sli4_post_async_mbox(phba);
+
        if (ha_copy & HA_ERATT)
                /* Handle the error attention event */
                lpfc_handle_eratt(phba);
@@ -501,9 +510,27 @@ lpfc_work_done(struct lpfc_hba *phba)
        if (ha_copy & HA_LATT)
                lpfc_handle_latt(phba);
 
+       /* Process SLI4 events */
+       if (phba->pci_dev_grp == LPFC_PCI_DEV_OC) {
+               if (phba->hba_flag & FCP_XRI_ABORT_EVENT)
+                       lpfc_sli4_fcp_xri_abort_event_proc(phba);
+               if (phba->hba_flag & ELS_XRI_ABORT_EVENT)
+                       lpfc_sli4_els_xri_abort_event_proc(phba);
+               if (phba->hba_flag & ASYNC_EVENT)
+                       lpfc_sli4_async_event_proc(phba);
+               if (phba->hba_flag & HBA_POST_RECEIVE_BUFFER) {
+                       spin_lock_irq(&phba->hbalock);
+                       phba->hba_flag &= ~HBA_POST_RECEIVE_BUFFER;
+                       spin_unlock_irq(&phba->hbalock);
+                       lpfc_sli_hbqbuf_add_hbqs(phba, LPFC_ELS_HBQ);
+               }
+               if (phba->hba_flag & HBA_RECEIVE_BUFFER)
+                       lpfc_sli4_handle_received_buffer(phba);
+       }
+
        vports = lpfc_create_vport_work_array(phba);
        if (vports != NULL)
-               for(i = 0; i <= phba->max_vpi; i++) {
+               for (i = 0; i <= phba->max_vports; i++) {
                        /*
                         * We could have no vports in array if unloading, so if
                         * this happens then just use the pport
@@ -555,23 +582,24 @@ lpfc_work_done(struct lpfc_hba *phba)
                /*
                 * Turn on Ring interrupts
                 */
-               spin_lock_irq(&phba->hbalock);
-               control = readl(phba->HCregaddr);
-               if (!(control & (HC_R0INT_ENA << LPFC_ELS_RING))) {
-                       lpfc_debugfs_slow_ring_trc(phba,
-                               "WRK Enable ring: cntl:x%x hacopy:x%x",
-                               control, ha_copy, 0);
-
-                       control |= (HC_R0INT_ENA << LPFC_ELS_RING);
-                       writel(control, phba->HCregaddr);
-                       readl(phba->HCregaddr); /* flush */
-               }
-               else {
-                       lpfc_debugfs_slow_ring_trc(phba,
-                               "WRK Ring ok:     cntl:x%x hacopy:x%x",
-                               control, ha_copy, 0);
+               if (phba->sli_rev <= LPFC_SLI_REV3) {
+                       spin_lock_irq(&phba->hbalock);
+                       control = readl(phba->HCregaddr);
+                       if (!(control & (HC_R0INT_ENA << LPFC_ELS_RING))) {
+                               lpfc_debugfs_slow_ring_trc(phba,
+                                       "WRK Enable ring: cntl:x%x hacopy:x%x",
+                                       control, ha_copy, 0);
+
+                               control |= (HC_R0INT_ENA << LPFC_ELS_RING);
+                               writel(control, phba->HCregaddr);
+                               readl(phba->HCregaddr); /* flush */
+                       } else {
+                               lpfc_debugfs_slow_ring_trc(phba,
+                                       "WRK Ring ok:     cntl:x%x hacopy:x%x",
+                                       control, ha_copy, 0);
+                       }
+                       spin_unlock_irq(&phba->hbalock);
                }
-               spin_unlock_irq(&phba->hbalock);
        }
        lpfc_work_list_done(phba);
 }
@@ -689,7 +717,7 @@ lpfc_port_link_failure(struct lpfc_vport *vport)
        lpfc_can_disctmo(vport);
 }
 
-static void
+void
 lpfc_linkdown_port(struct lpfc_vport *vport)
 {
        struct Scsi_Host  *shost = lpfc_shost_from_vport(vport);
@@ -716,6 +744,7 @@ lpfc_linkdown(struct lpfc_hba *phba)
        if (phba->link_state == LPFC_LINK_DOWN)
                return 0;
        spin_lock_irq(&phba->hbalock);
+       phba->fcf.fcf_flag &= ~(FCF_AVAILABLE | FCF_DISCOVERED);
        if (phba->link_state > LPFC_LINK_DOWN) {
                phba->link_state = LPFC_LINK_DOWN;
                phba->pport->fc_flag &= ~FC_LBIT;
@@ -723,7 +752,7 @@ lpfc_linkdown(struct lpfc_hba *phba)
        spin_unlock_irq(&phba->hbalock);
        vports = lpfc_create_vport_work_array(phba);
        if (vports != NULL)
-               for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+               for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
                        /* Issue a LINK DOWN event to all nodes */
                        lpfc_linkdown_port(vports[i]);
                }
@@ -833,10 +862,11 @@ lpfc_linkup(struct lpfc_hba *phba)
 
        vports = lpfc_create_vport_work_array(phba);
        if (vports != NULL)
-               for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++)
+               for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++)
                        lpfc_linkup_port(vports[i]);
        lpfc_destroy_vport_work_array(phba, vports);
-       if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)
+       if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) &&
+           (phba->sli_rev < LPFC_SLI_REV4))
                lpfc_issue_clear_la(phba, phba->pport);
 
        return 0;
@@ -854,7 +884,7 @@ lpfc_mbx_cmpl_clear_la(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
        struct lpfc_vport *vport = pmb->vport;
        struct Scsi_Host  *shost = lpfc_shost_from_vport(vport);
        struct lpfc_sli   *psli = &phba->sli;
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
        uint32_t control;
 
        /* Since we don't do discovery right now, turn these off here */
@@ -917,7 +947,7 @@ lpfc_mbx_cmpl_local_config_link(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
 {
        struct lpfc_vport *vport = pmb->vport;
 
-       if (pmb->mb.mbxStatus)
+       if (pmb->u.mb.mbxStatus)
                goto out;
 
        mempool_free(pmb, phba->mbox_mem_pool);
@@ -945,7 +975,7 @@ out:
        lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
                         "0306 CONFIG_LINK mbxStatus error x%x "
                         "HBA state x%x\n",
-                        pmb->mb.mbxStatus, vport->port_state);
+                        pmb->u.mb.mbxStatus, vport->port_state);
        mempool_free(pmb, phba->mbox_mem_pool);
 
        lpfc_linkdown(phba);
@@ -958,10 +988,593 @@ out:
        return;
 }
 
+static void
+lpfc_mbx_cmpl_reg_fcfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+       struct lpfc_vport *vport = mboxq->vport;
+       unsigned long flags;
+
+       if (mboxq->u.mb.mbxStatus) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
+                        "2017 REG_FCFI mbxStatus error x%x "
+                        "HBA state x%x\n",
+                        mboxq->u.mb.mbxStatus, vport->port_state);
+               mempool_free(mboxq, phba->mbox_mem_pool);
+               return;
+       }
+
+       /* Start FCoE discovery by sending a FLOGI. */
+       phba->fcf.fcfi = bf_get(lpfc_reg_fcfi_fcfi, &mboxq->u.mqe.un.reg_fcfi);
+       /* Set the FCFI registered flag */
+       spin_lock_irqsave(&phba->hbalock, flags);
+       phba->fcf.fcf_flag |= FCF_REGISTERED;
+       spin_unlock_irqrestore(&phba->hbalock, flags);
+       if (vport->port_state != LPFC_FLOGI) {
+               spin_lock_irqsave(&phba->hbalock, flags);
+               phba->fcf.fcf_flag |= (FCF_DISCOVERED | FCF_IN_USE);
+               spin_unlock_irqrestore(&phba->hbalock, flags);
+               lpfc_initial_flogi(vport);
+       }
+
+       mempool_free(mboxq, phba->mbox_mem_pool);
+       return;
+}
+
+/**
+ * lpfc_fab_name_match - Check if the fcf fabric name match.
+ * @fab_name: pointer to fabric name.
+ * @new_fcf_record: pointer to fcf record.
+ *
+ * This routine compare the fcf record's fabric name with provided
+ * fabric name. If the fabric name are identical this function
+ * returns 1 else return 0.
+ **/
+static uint32_t
+lpfc_fab_name_match(uint8_t *fab_name, struct fcf_record *new_fcf_record)
+{
+       if ((fab_name[0] ==
+               bf_get(lpfc_fcf_record_fab_name_0, new_fcf_record)) &&
+           (fab_name[1] ==
+               bf_get(lpfc_fcf_record_fab_name_1, new_fcf_record)) &&
+           (fab_name[2] ==
+               bf_get(lpfc_fcf_record_fab_name_2, new_fcf_record)) &&
+           (fab_name[3] ==
+               bf_get(lpfc_fcf_record_fab_name_3, new_fcf_record)) &&
+           (fab_name[4] ==
+               bf_get(lpfc_fcf_record_fab_name_4, new_fcf_record)) &&
+           (fab_name[5] ==
+               bf_get(lpfc_fcf_record_fab_name_5, new_fcf_record)) &&
+           (fab_name[6] ==
+               bf_get(lpfc_fcf_record_fab_name_6, new_fcf_record)) &&
+           (fab_name[7] ==
+               bf_get(lpfc_fcf_record_fab_name_7, new_fcf_record)))
+               return 1;
+       else
+               return 0;
+}
+
+/**
+ * lpfc_mac_addr_match - Check if the fcf mac address match.
+ * @phba: pointer to lpfc hba data structure.
+ * @new_fcf_record: pointer to fcf record.
+ *
+ * This routine compare the fcf record's mac address with HBA's
+ * FCF mac address. If the mac addresses are identical this function
+ * returns 1 else return 0.
+ **/
+static uint32_t
+lpfc_mac_addr_match(struct lpfc_hba *phba, struct fcf_record *new_fcf_record)
+{
+       if ((phba->fcf.mac_addr[0] ==
+               bf_get(lpfc_fcf_record_mac_0, new_fcf_record)) &&
+           (phba->fcf.mac_addr[1] ==
+               bf_get(lpfc_fcf_record_mac_1, new_fcf_record)) &&
+           (phba->fcf.mac_addr[2] ==
+               bf_get(lpfc_fcf_record_mac_2, new_fcf_record)) &&
+           (phba->fcf.mac_addr[3] ==
+               bf_get(lpfc_fcf_record_mac_3, new_fcf_record)) &&
+           (phba->fcf.mac_addr[4] ==
+               bf_get(lpfc_fcf_record_mac_4, new_fcf_record)) &&
+           (phba->fcf.mac_addr[5] ==
+               bf_get(lpfc_fcf_record_mac_5, new_fcf_record)))
+               return 1;
+       else
+               return 0;
+}
+
+/**
+ * lpfc_copy_fcf_record - Copy fcf information to lpfc_hba.
+ * @phba: pointer to lpfc hba data structure.
+ * @new_fcf_record: pointer to fcf record.
+ *
+ * This routine copies the FCF information from the FCF
+ * record to lpfc_hba data structure.
+ **/
+static void
+lpfc_copy_fcf_record(struct lpfc_hba *phba, struct fcf_record *new_fcf_record)
+{
+       phba->fcf.fabric_name[0] =
+               bf_get(lpfc_fcf_record_fab_name_0, new_fcf_record);
+       phba->fcf.fabric_name[1] =
+               bf_get(lpfc_fcf_record_fab_name_1, new_fcf_record);
+       phba->fcf.fabric_name[2] =
+               bf_get(lpfc_fcf_record_fab_name_2, new_fcf_record);
+       phba->fcf.fabric_name[3] =
+               bf_get(lpfc_fcf_record_fab_name_3, new_fcf_record);
+       phba->fcf.fabric_name[4] =
+               bf_get(lpfc_fcf_record_fab_name_4, new_fcf_record);
+       phba->fcf.fabric_name[5] =
+               bf_get(lpfc_fcf_record_fab_name_5, new_fcf_record);
+       phba->fcf.fabric_name[6] =
+               bf_get(lpfc_fcf_record_fab_name_6, new_fcf_record);
+       phba->fcf.fabric_name[7] =
+               bf_get(lpfc_fcf_record_fab_name_7, new_fcf_record);
+       phba->fcf.mac_addr[0] =
+               bf_get(lpfc_fcf_record_mac_0, new_fcf_record);
+       phba->fcf.mac_addr[1] =
+               bf_get(lpfc_fcf_record_mac_1, new_fcf_record);
+       phba->fcf.mac_addr[2] =
+               bf_get(lpfc_fcf_record_mac_2, new_fcf_record);
+       phba->fcf.mac_addr[3] =
+               bf_get(lpfc_fcf_record_mac_3, new_fcf_record);
+       phba->fcf.mac_addr[4] =
+               bf_get(lpfc_fcf_record_mac_4, new_fcf_record);
+       phba->fcf.mac_addr[5] =
+               bf_get(lpfc_fcf_record_mac_5, new_fcf_record);
+       phba->fcf.fcf_indx = bf_get(lpfc_fcf_record_fcf_index, new_fcf_record);
+       phba->fcf.priority = new_fcf_record->fip_priority;
+}
+
+/**
+ * lpfc_register_fcf - Register the FCF with hba.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine issues a register fcfi mailbox command to register
+ * the fcf with HBA.
+ **/
+static void
+lpfc_register_fcf(struct lpfc_hba *phba)
+{
+       LPFC_MBOXQ_t *fcf_mbxq;
+       int rc;
+       unsigned long flags;
+
+       spin_lock_irqsave(&phba->hbalock, flags);
+
+       /* If the FCF is not availabe do nothing. */
+       if (!(phba->fcf.fcf_flag & FCF_AVAILABLE)) {
+               spin_unlock_irqrestore(&phba->hbalock, flags);
+               return;
+       }
+
+       /* The FCF is already registered, start discovery */
+       if (phba->fcf.fcf_flag & FCF_REGISTERED) {
+               phba->fcf.fcf_flag |= (FCF_DISCOVERED | FCF_IN_USE);
+               spin_unlock_irqrestore(&phba->hbalock, flags);
+               if (phba->pport->port_state != LPFC_FLOGI)
+                       lpfc_initial_flogi(phba->pport);
+               return;
+       }
+       spin_unlock_irqrestore(&phba->hbalock, flags);
+
+       fcf_mbxq = mempool_alloc(phba->mbox_mem_pool,
+               GFP_KERNEL);
+       if (!fcf_mbxq)
+               return;
+
+       lpfc_reg_fcfi(phba, fcf_mbxq);
+       fcf_mbxq->vport = phba->pport;
+       fcf_mbxq->mbox_cmpl = lpfc_mbx_cmpl_reg_fcfi;
+       rc = lpfc_sli_issue_mbox(phba, fcf_mbxq, MBX_NOWAIT);
+       if (rc == MBX_NOT_FINISHED)
+               mempool_free(fcf_mbxq, phba->mbox_mem_pool);
+
+       return;
+}
+
+/**
+ * lpfc_match_fcf_conn_list - Check if the FCF record can be used for discovery.
+ * @phba: pointer to lpfc hba data structure.
+ * @new_fcf_record: pointer to fcf record.
+ * @boot_flag: Indicates if this record used by boot bios.
+ * @addr_mode: The address mode to be used by this FCF
+ *
+ * This routine compare the fcf record with connect list obtained from the
+ * config region to decide if this FCF can be used for SAN discovery. It returns
+ * 1 if this record can be used for SAN discovery else return zero. If this FCF
+ * record can be used for SAN discovery, the boot_flag will indicate if this FCF
+ * is used by boot bios and addr_mode will indicate the addressing mode to be
+ * used for this FCF when the function returns.
+ * If the FCF record need to be used with a particular vlan id, the vlan is
+ * set in the vlan_id on return of the function. If not VLAN tagging need to
+ * be used with the FCF vlan_id will be set to 0xFFFF;
+ **/
+static int
+lpfc_match_fcf_conn_list(struct lpfc_hba *phba,
+                       struct fcf_record *new_fcf_record,
+                       uint32_t *boot_flag, uint32_t *addr_mode,
+                       uint16_t *vlan_id)
+{
+       struct lpfc_fcf_conn_entry *conn_entry;
+
+       if (!phba->cfg_enable_fip) {
+               *boot_flag = 0;
+               *addr_mode = bf_get(lpfc_fcf_record_mac_addr_prov,
+                               new_fcf_record);
+               if (phba->valid_vlan)
+                       *vlan_id = phba->vlan_id;
+               else
+                       *vlan_id = 0xFFFF;
+               return 1;
+       }
+
+       /*
+        * If there are no FCF connection table entry, driver connect to all
+        * FCFs.
+        */
+       if (list_empty(&phba->fcf_conn_rec_list)) {
+               *boot_flag = 0;
+               *addr_mode = bf_get(lpfc_fcf_record_mac_addr_prov,
+                       new_fcf_record);
+               *vlan_id = 0xFFFF;
+               return 1;
+       }
+
+       list_for_each_entry(conn_entry, &phba->fcf_conn_rec_list, list) {
+               if (!(conn_entry->conn_rec.flags & FCFCNCT_VALID))
+                       continue;
+
+               if ((conn_entry->conn_rec.flags & FCFCNCT_FBNM_VALID) &&
+                       !lpfc_fab_name_match(conn_entry->conn_rec.fabric_name,
+                               new_fcf_record))
+                       continue;
+
+               if (conn_entry->conn_rec.flags & FCFCNCT_VLAN_VALID) {
+                       /*
+                        * If the vlan bit map does not have the bit set for the
+                        * vlan id to be used, then it is not a match.
+                        */
+                       if (!(new_fcf_record->vlan_bitmap
+                               [conn_entry->conn_rec.vlan_tag / 8] &
+                               (1 << (conn_entry->conn_rec.vlan_tag % 8))))
+                               continue;
+               }
+
+               /*
+                * Check if the connection record specifies a required
+                * addressing mode.
+                */
+               if ((conn_entry->conn_rec.flags & FCFCNCT_AM_VALID) &&
+                       !(conn_entry->conn_rec.flags & FCFCNCT_AM_PREFERRED)) {
+
+                       /*
+                        * If SPMA required but FCF not support this continue.
+                        */
+                       if ((conn_entry->conn_rec.flags & FCFCNCT_AM_SPMA) &&
+                               !(bf_get(lpfc_fcf_record_mac_addr_prov,
+                                       new_fcf_record) & LPFC_FCF_SPMA))
+                               continue;
+
+                       /*
+                        * If FPMA required but FCF not support this continue.
+                        */
+                       if (!(conn_entry->conn_rec.flags & FCFCNCT_AM_SPMA) &&
+                               !(bf_get(lpfc_fcf_record_mac_addr_prov,
+                               new_fcf_record) & LPFC_FCF_FPMA))
+                               continue;
+               }
+
+               /*
+                * This fcf record matches filtering criteria.
+                */
+               if (conn_entry->conn_rec.flags & FCFCNCT_BOOT)
+                       *boot_flag = 1;
+               else
+                       *boot_flag = 0;
+
+               *addr_mode = bf_get(lpfc_fcf_record_mac_addr_prov,
+                               new_fcf_record);
+               /*
+                * If the user specified a required address mode, assign that
+                * address mode
+                */
+               if ((conn_entry->conn_rec.flags & FCFCNCT_AM_VALID) &&
+                       (!(conn_entry->conn_rec.flags & FCFCNCT_AM_PREFERRED)))
+                       *addr_mode = (conn_entry->conn_rec.flags &
+                               FCFCNCT_AM_SPMA) ?
+                               LPFC_FCF_SPMA : LPFC_FCF_FPMA;
+               /*
+                * If the user specified a prefered address mode, use the
+                * addr mode only if FCF support the addr_mode.
+                */
+               else if ((conn_entry->conn_rec.flags & FCFCNCT_AM_VALID) &&
+                       (conn_entry->conn_rec.flags & FCFCNCT_AM_PREFERRED) &&
+                       (conn_entry->conn_rec.flags & FCFCNCT_AM_SPMA) &&
+                       (*addr_mode & LPFC_FCF_SPMA))
+                               *addr_mode = LPFC_FCF_SPMA;
+               else if ((conn_entry->conn_rec.flags & FCFCNCT_AM_VALID) &&
+                       (conn_entry->conn_rec.flags & FCFCNCT_AM_PREFERRED) &&
+                       !(conn_entry->conn_rec.flags & FCFCNCT_AM_SPMA) &&
+                       (*addr_mode & LPFC_FCF_FPMA))
+                               *addr_mode = LPFC_FCF_FPMA;
+               /*
+                * If user did not specify any addressing mode, use FPMA if
+                * possible else use SPMA.
+                */
+               else if (*addr_mode & LPFC_FCF_FPMA)
+                       *addr_mode = LPFC_FCF_FPMA;
+
+               if (conn_entry->conn_rec.flags & FCFCNCT_VLAN_VALID)
+                       *vlan_id = conn_entry->conn_rec.vlan_tag;
+               else
+                       *vlan_id = 0xFFFF;
+
+               return 1;
+       }
+
+       return 0;
+}
+
+/**
+ * lpfc_mbx_cmpl_read_fcf_record - Completion handler for read_fcf mbox.
+ * @phba: pointer to lpfc hba data structure.
+ * @mboxq: pointer to mailbox object.
+ *
+ * This function iterate through all the fcf records available in
+ * HBA and choose the optimal FCF record for discovery. After finding
+ * the FCF for discovery it register the FCF record and kick start
+ * discovery.
+ * If FCF_IN_USE flag is set in currently used FCF, the routine try to
+ * use a FCF record which match fabric name and mac address of the
+ * currently used FCF record.
+ * If the driver support only one FCF, it will try to use the FCF record
+ * used by BOOT_BIOS.
+ */
+void
+lpfc_mbx_cmpl_read_fcf_record(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+       void *virt_addr;
+       dma_addr_t phys_addr;
+       uint8_t *bytep;
+       struct lpfc_mbx_sge sge;
+       struct lpfc_mbx_read_fcf_tbl *read_fcf;
+       uint32_t shdr_status, shdr_add_status;
+       union lpfc_sli4_cfg_shdr *shdr;
+       struct fcf_record *new_fcf_record;
+       int rc;
+       uint32_t boot_flag, addr_mode;
+       uint32_t next_fcf_index;
+       unsigned long flags;
+       uint16_t vlan_id;
+
+       /* Get the first SGE entry from the non-embedded DMA memory. This
+        * routine only uses a single SGE.
+        */
+       lpfc_sli4_mbx_sge_get(mboxq, 0, &sge);
+       phys_addr = getPaddr(sge.pa_hi, sge.pa_lo);
+       if (unlikely(!mboxq->sge_array)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+                               "2524 Failed to get the non-embedded SGE "
+                               "virtual address\n");
+               goto out;
+       }
+       virt_addr = mboxq->sge_array->addr[0];
+
+       shdr = (union lpfc_sli4_cfg_shdr *)virt_addr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,
+                                &shdr->response);
+       /*
+        * The FCF Record was read and there is no reason for the driver
+        * to maintain the FCF record data or memory. Instead, just need
+        * to book keeping the FCFIs can be used.
+        */
+       if (shdr_status || shdr_add_status) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2521 READ_FCF_RECORD mailbox failed "
+                               "with status x%x add_status x%x, mbx\n",
+                               shdr_status, shdr_add_status);
+               goto out;
+       }
+       /* Interpreting the returned information of FCF records */
+       read_fcf = (struct lpfc_mbx_read_fcf_tbl *)virt_addr;
+       lpfc_sli_pcimem_bcopy(read_fcf, read_fcf,
+                             sizeof(struct lpfc_mbx_read_fcf_tbl));
+       next_fcf_index = bf_get(lpfc_mbx_read_fcf_tbl_nxt_vindx, read_fcf);
+
+       new_fcf_record = (struct fcf_record *)(virt_addr +
+                         sizeof(struct lpfc_mbx_read_fcf_tbl));
+       lpfc_sli_pcimem_bcopy(new_fcf_record, new_fcf_record,
+                             sizeof(struct fcf_record));
+       bytep = virt_addr + sizeof(union lpfc_sli4_cfg_shdr);
+
+       rc = lpfc_match_fcf_conn_list(phba, new_fcf_record,
+                                     &boot_flag, &addr_mode,
+                                       &vlan_id);
+       /*
+        * If the fcf record does not match with connect list entries
+        * read the next entry.
+        */
+       if (!rc)
+               goto read_next_fcf;
+       /*
+        * If this is not the first FCF discovery of the HBA, use last
+        * FCF record for the discovery.
+        */
+       spin_lock_irqsave(&phba->hbalock, flags);
+       if (phba->fcf.fcf_flag & FCF_IN_USE) {
+               if (lpfc_fab_name_match(phba->fcf.fabric_name,
+                       new_fcf_record) &&
+                   lpfc_mac_addr_match(phba, new_fcf_record)) {
+                       phba->fcf.fcf_flag |= FCF_AVAILABLE;
+                       spin_unlock_irqrestore(&phba->hbalock, flags);
+                       goto out;
+               }
+               spin_unlock_irqrestore(&phba->hbalock, flags);
+               goto read_next_fcf;
+       }
+       if (phba->fcf.fcf_flag & FCF_AVAILABLE) {
+               /*
+                * If the current FCF record does not have boot flag
+                * set and new fcf record has boot flag set, use the
+                * new fcf record.
+                */
+               if (boot_flag && !(phba->fcf.fcf_flag & FCF_BOOT_ENABLE)) {
+                       /* Use this FCF record */
+                       lpfc_copy_fcf_record(phba, new_fcf_record);
+                       phba->fcf.addr_mode = addr_mode;
+                       phba->fcf.fcf_flag |= FCF_BOOT_ENABLE;
+                       if (vlan_id != 0xFFFF) {
+                               phba->fcf.fcf_flag |= FCF_VALID_VLAN;
+                               phba->fcf.vlan_id = vlan_id;
+                       }
+                       spin_unlock_irqrestore(&phba->hbalock, flags);
+                       goto read_next_fcf;
+               }
+               /*
+                * If the current FCF record has boot flag set and the
+                * new FCF record does not have boot flag, read the next
+                * FCF record.
+                */
+               if (!boot_flag && (phba->fcf.fcf_flag & FCF_BOOT_ENABLE)) {
+                       spin_unlock_irqrestore(&phba->hbalock, flags);
+                       goto read_next_fcf;
+               }
+               /*
+                * If there is a record with lower priority value for
+                * the current FCF, use that record.
+                */
+               if (lpfc_fab_name_match(phba->fcf.fabric_name, new_fcf_record)
+                       && (new_fcf_record->fip_priority <
+                               phba->fcf.priority)) {
+                       /* Use this FCF record */
+                       lpfc_copy_fcf_record(phba, new_fcf_record);
+                       phba->fcf.addr_mode = addr_mode;
+                       if (vlan_id != 0xFFFF) {
+                               phba->fcf.fcf_flag |= FCF_VALID_VLAN;
+                               phba->fcf.vlan_id = vlan_id;
+                       }
+                       spin_unlock_irqrestore(&phba->hbalock, flags);
+                       goto read_next_fcf;
+               }
+               spin_unlock_irqrestore(&phba->hbalock, flags);
+               goto read_next_fcf;
+       }
+       /*
+        * This is the first available FCF record, use this
+        * record.
+        */
+       lpfc_copy_fcf_record(phba, new_fcf_record);
+       phba->fcf.addr_mode = addr_mode;
+       if (boot_flag)
+               phba->fcf.fcf_flag |= FCF_BOOT_ENABLE;
+       phba->fcf.fcf_flag |= FCF_AVAILABLE;
+       if (vlan_id != 0xFFFF) {
+               phba->fcf.fcf_flag |= FCF_VALID_VLAN;
+               phba->fcf.vlan_id = vlan_id;
+       }
+       spin_unlock_irqrestore(&phba->hbalock, flags);
+       goto read_next_fcf;
+
+read_next_fcf:
+       lpfc_sli4_mbox_cmd_free(phba, mboxq);
+       if (next_fcf_index == LPFC_FCOE_FCF_NEXT_NONE || next_fcf_index == 0)
+               lpfc_register_fcf(phba);
+       else
+               lpfc_sli4_read_fcf_record(phba, next_fcf_index);
+       return;
+
+out:
+       lpfc_sli4_mbox_cmd_free(phba, mboxq);
+       lpfc_register_fcf(phba);
+
+       return;
+}
+
+/**
+ * lpfc_start_fdiscs - send fdiscs for each vports on this port.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This function loops through the list of vports on the @phba and issues an
+ * FDISC if possible.
+ */
+void
+lpfc_start_fdiscs(struct lpfc_hba *phba)
+{
+       struct lpfc_vport **vports;
+       int i;
+
+       vports = lpfc_create_vport_work_array(phba);
+       if (vports != NULL) {
+               for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
+                       if (vports[i]->port_type == LPFC_PHYSICAL_PORT)
+                               continue;
+                       /* There are no vpi for this vport */
+                       if (vports[i]->vpi > phba->max_vpi) {
+                               lpfc_vport_set_state(vports[i],
+                                                    FC_VPORT_FAILED);
+                               continue;
+                       }
+                       if (phba->fc_topology == TOPOLOGY_LOOP) {
+                               lpfc_vport_set_state(vports[i],
+                                                    FC_VPORT_LINKDOWN);
+                               continue;
+                       }
+                       if (phba->link_flag & LS_NPIV_FAB_SUPPORTED)
+                               lpfc_initial_fdisc(vports[i]);
+                       else {
+                               lpfc_vport_set_state(vports[i],
+                                                    FC_VPORT_NO_FABRIC_SUPP);
+                               lpfc_printf_vlog(vports[i], KERN_ERR,
+                                                LOG_ELS,
+                                                "0259 No NPIV "
+                                                "Fabric support\n");
+                       }
+               }
+       }
+       lpfc_destroy_vport_work_array(phba, vports);
+}
+
+void
+lpfc_mbx_cmpl_reg_vfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+       struct lpfc_dmabuf *dmabuf = mboxq->context1;
+       struct lpfc_vport *vport = mboxq->vport;
+
+       if (mboxq->u.mb.mbxStatus) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
+                        "2018 REG_VFI mbxStatus error x%x "
+                        "HBA state x%x\n",
+                        mboxq->u.mb.mbxStatus, vport->port_state);
+               if (phba->fc_topology == TOPOLOGY_LOOP) {
+                       /* FLOGI failed, use loop map to make discovery list */
+                       lpfc_disc_list_loopmap(vport);
+                       /* Start discovery */
+                       lpfc_disc_start(vport);
+                       goto fail_free_mem;
+               }
+               lpfc_vport_set_state(vport, FC_VPORT_FAILED);
+               goto fail_free_mem;
+       }
+       /* Mark the vport has registered with its VFI */
+       vport->vfi_state |= LPFC_VFI_REGISTERED;
+
+       if (vport->port_state == LPFC_FABRIC_CFG_LINK) {
+               lpfc_start_fdiscs(phba);
+               lpfc_do_scr_ns_plogi(phba, vport);
+       }
+
+fail_free_mem:
+       mempool_free(mboxq, phba->mbox_mem_pool);
+       lpfc_mbuf_free(phba, dmabuf->virt, dmabuf->phys);
+       kfree(dmabuf);
+       return;
+}
+
 static void
 lpfc_mbx_cmpl_read_sparam(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
 {
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
        struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) pmb->context1;
        struct lpfc_vport  *vport = pmb->vport;
 
@@ -1012,13 +1625,13 @@ static void
 lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la)
 {
        struct lpfc_vport *vport = phba->pport;
-       LPFC_MBOXQ_t *sparam_mbox, *cfglink_mbox;
+       LPFC_MBOXQ_t *sparam_mbox, *cfglink_mbox = NULL;
        int i;
        struct lpfc_dmabuf *mp;
        int rc;
+       struct fcf_record *fcf_record;
 
        sparam_mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
-       cfglink_mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
 
        spin_lock_irq(&phba->hbalock);
        switch (la->UlnkSpeed) {
@@ -1034,6 +1647,9 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la)
        case LA_8GHZ_LINK:
                phba->fc_linkspeed = LA_8GHZ_LINK;
                break;
+       case LA_10GHZ_LINK:
+               phba->fc_linkspeed = LA_10GHZ_LINK;
+               break;
        default:
                phba->fc_linkspeed = LA_UNKNW_LINK;
                break;
@@ -1115,22 +1731,66 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la)
                        lpfc_mbuf_free(phba, mp->virt, mp->phys);
                        kfree(mp);
                        mempool_free(sparam_mbox, phba->mbox_mem_pool);
-                       if (cfglink_mbox)
-                               mempool_free(cfglink_mbox, phba->mbox_mem_pool);
                        goto out;
                }
        }
 
-       if (cfglink_mbox) {
+       if (!(phba->hba_flag & HBA_FCOE_SUPPORT)) {
+               cfglink_mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+               if (!cfglink_mbox)
+                       goto out;
                vport->port_state = LPFC_LOCAL_CFG_LINK;
                lpfc_config_link(phba, cfglink_mbox);
                cfglink_mbox->vport = vport;
                cfglink_mbox->mbox_cmpl = lpfc_mbx_cmpl_local_config_link;
                rc = lpfc_sli_issue_mbox(phba, cfglink_mbox, MBX_NOWAIT);
-               if (rc != MBX_NOT_FINISHED)
-                       return;
-               mempool_free(cfglink_mbox, phba->mbox_mem_pool);
+               if (rc == MBX_NOT_FINISHED) {
+                       mempool_free(cfglink_mbox, phba->mbox_mem_pool);
+                       goto out;
+               }
+       } else {
+               /*
+                * Add the driver's default FCF record at FCF index 0 now. This
+                * is phase 1 implementation that support FCF index 0 and driver
+                * defaults.
+                */
+               if (phba->cfg_enable_fip == 0) {
+                       fcf_record = kzalloc(sizeof(struct fcf_record),
+                                       GFP_KERNEL);
+                       if (unlikely(!fcf_record)) {
+                               lpfc_printf_log(phba, KERN_ERR,
+                                       LOG_MBOX | LOG_SLI,
+                                       "2554 Could not allocate memmory for "
+                                       "fcf record\n");
+                               rc = -ENODEV;
+                               goto out;
+                       }
+
+                       lpfc_sli4_build_dflt_fcf_record(phba, fcf_record,
+                                               LPFC_FCOE_FCF_DEF_INDEX);
+                       rc = lpfc_sli4_add_fcf_record(phba, fcf_record);
+                       if (unlikely(rc)) {
+                               lpfc_printf_log(phba, KERN_ERR,
+                                       LOG_MBOX | LOG_SLI,
+                                       "2013 Could not manually add FCF "
+                                       "record 0, status %d\n", rc);
+                               rc = -ENODEV;
+                               kfree(fcf_record);
+                               goto out;
+                       }
+                       kfree(fcf_record);
+               }
+               /*
+                * The driver is expected to do FIP/FCF. Call the port
+                * and get the FCF Table.
+                */
+               rc = lpfc_sli4_read_fcf_record(phba,
+                                       LPFC_FCOE_FCF_GET_FIRST);
+               if (rc)
+                       goto out;
        }
+
+       return;
 out:
        lpfc_vport_set_state(vport, FC_VPORT_FAILED);
        lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
@@ -1147,10 +1807,12 @@ lpfc_enable_la(struct lpfc_hba *phba)
        struct lpfc_sli *psli = &phba->sli;
        spin_lock_irq(&phba->hbalock);
        psli->sli_flag |= LPFC_PROCESS_LA;
-       control = readl(phba->HCregaddr);
-       control |= HC_LAINT_ENA;
-       writel(control, phba->HCregaddr);
-       readl(phba->HCregaddr); /* flush */
+       if (phba->sli_rev <= LPFC_SLI_REV3) {
+               control = readl(phba->HCregaddr);
+               control |= HC_LAINT_ENA;
+               writel(control, phba->HCregaddr);
+               readl(phba->HCregaddr); /* flush */
+       }
        spin_unlock_irq(&phba->hbalock);
 }
 
@@ -1159,6 +1821,7 @@ lpfc_mbx_issue_link_down(struct lpfc_hba *phba)
 {
        lpfc_linkdown(phba);
        lpfc_enable_la(phba);
+       lpfc_unregister_unused_fcf(phba);
        /* turn on Link Attention interrupts - no CLEAR_LA needed */
 }
 
@@ -1175,7 +1838,7 @@ lpfc_mbx_cmpl_read_la(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
        struct lpfc_vport *vport = pmb->vport;
        struct Scsi_Host  *shost = lpfc_shost_from_vport(vport);
        READ_LA_VAR *la;
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
        struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1);
 
        /* Unblock ELS traffic */
@@ -1190,7 +1853,7 @@ lpfc_mbx_cmpl_read_la(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
                goto lpfc_mbx_cmpl_read_la_free_mbuf;
        }
 
-       la = (READ_LA_VAR *) & pmb->mb.un.varReadLA;
+       la = (READ_LA_VAR *) &pmb->u.mb.un.varReadLA;
 
        memcpy(&phba->alpa_map[0], mp->virt, 128);
 
@@ -1328,7 +1991,7 @@ lpfc_mbx_cmpl_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
 static void
 lpfc_mbx_cmpl_unreg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
 {
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
        struct lpfc_vport *vport = pmb->vport;
        struct Scsi_Host  *shost = lpfc_shost_from_vport(vport);
 
@@ -1381,7 +2044,7 @@ lpfc_mbx_cmpl_reg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
 {
        struct lpfc_vport *vport = pmb->vport;
        struct Scsi_Host  *shost = lpfc_shost_from_vport(vport);
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
 
        switch (mb->mbxStatus) {
        case 0x0011:
@@ -1416,6 +2079,128 @@ out:
        return;
 }
 
+/**
+ * lpfc_create_static_vport - Read HBA config region to create static vports.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine issue a DUMP mailbox command for config region 22 to get
+ * the list of static vports to be created. The function create vports
+ * based on the information returned from the HBA.
+ **/
+void
+lpfc_create_static_vport(struct lpfc_hba *phba)
+{
+       LPFC_MBOXQ_t *pmb = NULL;
+       MAILBOX_t *mb;
+       struct static_vport_info *vport_info;
+       int rc, i;
+       struct fc_vport_identifiers vport_id;
+       struct fc_vport *new_fc_vport;
+       struct Scsi_Host *shost;
+       struct lpfc_vport *vport;
+       uint16_t offset = 0;
+       uint8_t *vport_buff;
+
+       pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!pmb) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0542 lpfc_create_static_vport failed to"
+                               " allocate mailbox memory\n");
+               return;
+       }
+
+       mb = &pmb->u.mb;
+
+       vport_info = kzalloc(sizeof(struct static_vport_info), GFP_KERNEL);
+       if (!vport_info) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0543 lpfc_create_static_vport failed to"
+                               " allocate vport_info\n");
+               mempool_free(pmb, phba->mbox_mem_pool);
+               return;
+       }
+
+       vport_buff = (uint8_t *) vport_info;
+       do {
+               lpfc_dump_static_vport(phba, pmb, offset);
+               pmb->vport = phba->pport;
+               rc = lpfc_sli_issue_mbox_wait(phba, pmb, LPFC_MBOX_TMO);
+
+               if ((rc != MBX_SUCCESS) || mb->mbxStatus) {
+                       lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+                               "0544 lpfc_create_static_vport failed to"
+                               " issue dump mailbox command ret 0x%x "
+                               "status 0x%x\n",
+                               rc, mb->mbxStatus);
+                       goto out;
+               }
+
+               if (mb->un.varDmp.word_cnt >
+                       sizeof(struct static_vport_info) - offset)
+                       mb->un.varDmp.word_cnt =
+                       sizeof(struct static_vport_info) - offset;
+
+               lpfc_sli_pcimem_bcopy(((uint8_t *)mb) + DMP_RSP_OFFSET,
+                       vport_buff + offset,
+                       mb->un.varDmp.word_cnt);
+               offset += mb->un.varDmp.word_cnt;
+
+       } while (mb->un.varDmp.word_cnt &&
+               offset < sizeof(struct static_vport_info));
+
+
+       if ((le32_to_cpu(vport_info->signature) != VPORT_INFO_SIG) ||
+               ((le32_to_cpu(vport_info->rev) & VPORT_INFO_REV_MASK)
+                       != VPORT_INFO_REV)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                       "0545 lpfc_create_static_vport bad"
+                       " information header 0x%x 0x%x\n",
+                       le32_to_cpu(vport_info->signature),
+                       le32_to_cpu(vport_info->rev) & VPORT_INFO_REV_MASK);
+
+               goto out;
+       }
+
+       shost = lpfc_shost_from_vport(phba->pport);
+
+       for (i = 0; i < MAX_STATIC_VPORT_COUNT; i++) {
+               memset(&vport_id, 0, sizeof(vport_id));
+               vport_id.port_name = wwn_to_u64(vport_info->vport_list[i].wwpn);
+               vport_id.node_name = wwn_to_u64(vport_info->vport_list[i].wwnn);
+               if (!vport_id.port_name || !vport_id.node_name)
+                       continue;
+
+               vport_id.roles = FC_PORT_ROLE_FCP_INITIATOR;
+               vport_id.vport_type = FC_PORTTYPE_NPIV;
+               vport_id.disable = false;
+               new_fc_vport = fc_vport_create(shost, 0, &vport_id);
+
+               if (!new_fc_vport) {
+                       lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+                               "0546 lpfc_create_static_vport failed to"
+                               " create vport \n");
+                       continue;
+               }
+
+               vport = *(struct lpfc_vport **)new_fc_vport->dd_data;
+               vport->vport_flag |= STATIC_VPORT;
+       }
+
+out:
+       /*
+        * If this is timed out command, setting NULL to context2 tell SLI
+        * layer not to use this buffer.
+        */
+       spin_lock_irq(&phba->hbalock);
+       pmb->context2 = NULL;
+       spin_unlock_irq(&phba->hbalock);
+       kfree(vport_info);
+       if (rc != MBX_TIMEOUT)
+               mempool_free(pmb, phba->mbox_mem_pool);
+
+       return;
+}
+
 /*
  * This routine handles processing a Fabric REG_LOGIN mailbox
  * command upon completion. It is setup in the LPFC_MBOXQ
@@ -1426,16 +2211,17 @@ void
 lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
 {
        struct lpfc_vport *vport = pmb->vport;
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
        struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1);
        struct lpfc_nodelist *ndlp;
-       struct lpfc_vport **vports;
-       int i;
 
        ndlp = (struct lpfc_nodelist *) pmb->context2;
        pmb->context1 = NULL;
        pmb->context2 = NULL;
        if (mb->mbxStatus) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
+                                "0258 Register Fabric login error: 0x%x\n",
+                                mb->mbxStatus);
                lpfc_mbuf_free(phba, mp->virt, mp->phys);
                kfree(mp);
                mempool_free(pmb, phba->mbox_mem_pool);
@@ -1454,9 +2240,6 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
                }
 
                lpfc_vport_set_state(vport, FC_VPORT_FAILED);
-               lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
-                                "0258 Register Fabric login error: 0x%x\n",
-                                mb->mbxStatus);
                /* Decrement the reference count to ndlp after the reference
                 * to the ndlp are done.
                 */
@@ -1465,34 +2248,12 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
        }
 
        ndlp->nlp_rpi = mb->un.varWords[0];
+       ndlp->nlp_flag |= NLP_RPI_VALID;
        ndlp->nlp_type |= NLP_FABRIC;
        lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
 
        if (vport->port_state == LPFC_FABRIC_CFG_LINK) {
-               vports = lpfc_create_vport_work_array(phba);
-               if (vports != NULL)
-                       for(i = 0;
-                           i <= phba->max_vpi && vports[i] != NULL;
-                           i++) {
-                               if (vports[i]->port_type == LPFC_PHYSICAL_PORT)
-                                       continue;
-                               if (phba->fc_topology == TOPOLOGY_LOOP) {
-                                       lpfc_vport_set_state(vports[i],
-                                                       FC_VPORT_LINKDOWN);
-                                       continue;
-                               }
-                               if (phba->link_flag & LS_NPIV_FAB_SUPPORTED)
-                                       lpfc_initial_fdisc(vports[i]);
-                               else {
-                                       lpfc_vport_set_state(vports[i],
-                                               FC_VPORT_NO_FABRIC_SUPP);
-                                       lpfc_printf_vlog(vport, KERN_ERR,
-                                                        LOG_ELS,
-                                                       "0259 No NPIV "
-                                                       "Fabric support\n");
-                               }
-                       }
-               lpfc_destroy_vport_work_array(phba, vports);
+               lpfc_start_fdiscs(phba);
                lpfc_do_scr_ns_plogi(phba, vport);
        }
 
@@ -1516,13 +2277,16 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
 void
 lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
 {
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
        struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1);
        struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) pmb->context2;
        struct lpfc_vport *vport = pmb->vport;
 
        if (mb->mbxStatus) {
 out:
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+                                "0260 Register NameServer error: 0x%x\n",
+                                mb->mbxStatus);
                /* decrement the node reference count held for this
                 * callback function.
                 */
@@ -1546,15 +2310,13 @@ out:
                        return;
                }
                lpfc_vport_set_state(vport, FC_VPORT_FAILED);
-               lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
-                                "0260 Register NameServer error: 0x%x\n",
-                                mb->mbxStatus);
                return;
        }
 
        pmb->context1 = NULL;
 
        ndlp->nlp_rpi = mb->un.varWords[0];
+       ndlp->nlp_flag |= NLP_RPI_VALID;
        ndlp->nlp_type |= NLP_FABRIC;
        lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
 
@@ -2055,7 +2817,7 @@ lpfc_check_sli_ndlp(struct lpfc_hba *phba,
        if (pring->ringno == LPFC_ELS_RING) {
                switch (icmd->ulpCommand) {
                case CMD_GEN_REQUEST64_CR:
-                       if (icmd->ulpContext == (volatile ushort)ndlp->nlp_rpi)
+                       if (iocb->context_un.ndlp == ndlp)
                                return 1;
                case CMD_ELS_REQUEST64_CR:
                        if (icmd->un.elsreq64.remoteID == ndlp->nlp_DID)
@@ -2102,7 +2864,7 @@ lpfc_no_rpi(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
         */
        psli = &phba->sli;
        rpi = ndlp->nlp_rpi;
-       if (rpi) {
+       if (ndlp->nlp_flag & NLP_RPI_VALID) {
                /* Now process each ring */
                for (i = 0; i < psli->num_rings; i++) {
                        pring = &psli->ring[i];
@@ -2150,7 +2912,7 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
        LPFC_MBOXQ_t    *mbox;
        int rc;
 
-       if (ndlp->nlp_rpi) {
+       if (ndlp->nlp_flag & NLP_RPI_VALID) {
                mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
                if (mbox) {
                        lpfc_unreg_login(phba, vport->vpi, ndlp->nlp_rpi, mbox);
@@ -2162,6 +2924,7 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
                }
                lpfc_no_rpi(phba, ndlp);
                ndlp->nlp_rpi = 0;
+               ndlp->nlp_flag &= ~NLP_RPI_VALID;
                return 1;
        }
        return 0;
@@ -2252,7 +3015,7 @@ lpfc_cleanup_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
 
        /* cleanup any ndlp on mbox q waiting for reglogin cmpl */
        if ((mb = phba->sli.mbox_active)) {
-               if ((mb->mb.mbxCommand == MBX_REG_LOGIN64) &&
+               if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) &&
                   (ndlp == (struct lpfc_nodelist *) mb->context2)) {
                        mb->context2 = NULL;
                        mb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
@@ -2261,7 +3024,7 @@ lpfc_cleanup_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
 
        spin_lock_irq(&phba->hbalock);
        list_for_each_entry_safe(mb, nextmb, &phba->sli.mboxq, list) {
-               if ((mb->mb.mbxCommand == MBX_REG_LOGIN64) &&
+               if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) &&
                    (ndlp == (struct lpfc_nodelist *) mb->context2)) {
                        mp = (struct lpfc_dmabuf *) (mb->context1);
                        if (mp) {
@@ -2309,13 +3072,14 @@ lpfc_nlp_remove(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
        int rc;
 
        lpfc_cancel_retry_delay_tmo(vport, ndlp);
-       if (ndlp->nlp_flag & NLP_DEFER_RM && !ndlp->nlp_rpi) {
+       if ((ndlp->nlp_flag & NLP_DEFER_RM) &&
+           !(ndlp->nlp_flag & NLP_RPI_VALID)) {
                /* For this case we need to cleanup the default rpi
                 * allocated by the firmware.
                 */
                if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL))
                        != NULL) {
-                       rc = lpfc_reg_login(phba, vport->vpi, ndlp->nlp_DID,
+                       rc = lpfc_reg_rpi(phba, vport->vpi, ndlp->nlp_DID,
                            (uint8_t *) &vport->fc_sparam, mbox, 0);
                        if (rc) {
                                mempool_free(mbox, phba->mbox_mem_pool);
@@ -2553,7 +3317,8 @@ lpfc_issue_clear_la(struct lpfc_hba *phba, struct lpfc_vport *vport)
         * clear_la then don't send it.
         */
        if ((phba->link_state >= LPFC_CLEAR_LA) ||
-           (vport->port_type != LPFC_PHYSICAL_PORT))
+           (vport->port_type != LPFC_PHYSICAL_PORT) ||
+               (phba->sli_rev == LPFC_SLI_REV4))
                return;
 
                        /* Link up discovery */
@@ -2582,7 +3347,7 @@ lpfc_issue_reg_vpi(struct lpfc_hba *phba, struct lpfc_vport *vport)
 
        regvpimbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
        if (regvpimbox) {
-               lpfc_reg_vpi(phba, vport->vpi, vport->fc_myDID, regvpimbox);
+               lpfc_reg_vpi(vport, regvpimbox);
                regvpimbox->mbox_cmpl = lpfc_mbx_cmpl_reg_vpi;
                regvpimbox->vport = vport;
                if (lpfc_sli_issue_mbox(phba, regvpimbox, MBX_NOWAIT)
@@ -2642,7 +3407,8 @@ lpfc_disc_start(struct lpfc_vport *vport)
         */
        if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) &&
            !(vport->fc_flag & FC_PT2PT) &&
-           !(vport->fc_flag & FC_RSCN_MODE)) {
+           !(vport->fc_flag & FC_RSCN_MODE) &&
+           (phba->sli_rev < LPFC_SLI_REV4)) {
                lpfc_issue_reg_vpi(phba, vport);
                return;
        }
@@ -2919,11 +3685,13 @@ restart_disc:
                 * set port_state to PORT_READY if SLI2.
                 * cmpl_reg_vpi will set port_state to READY for SLI3.
                 */
-               if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)
-                       lpfc_issue_reg_vpi(phba, vport);
-               else  { /* NPIV Not enabled */
-                       lpfc_issue_clear_la(phba, vport);
-                       vport->port_state = LPFC_VPORT_READY;
+               if (phba->sli_rev < LPFC_SLI_REV4) {
+                       if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)
+                               lpfc_issue_reg_vpi(phba, vport);
+                       else  { /* NPIV Not enabled */
+                               lpfc_issue_clear_la(phba, vport);
+                               vport->port_state = LPFC_VPORT_READY;
+                       }
                }
 
                /* Setup and issue mailbox INITIALIZE LINK command */
@@ -2939,7 +3707,7 @@ restart_disc:
                lpfc_linkdown(phba);
                lpfc_init_link(phba, initlinkmbox, phba->cfg_topology,
                               phba->cfg_link_speed);
-               initlinkmbox->mb.un.varInitLnk.lipsr_AL_PA = 0;
+               initlinkmbox->u.mb.un.varInitLnk.lipsr_AL_PA = 0;
                initlinkmbox->vport = vport;
                initlinkmbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
                rc = lpfc_sli_issue_mbox(phba, initlinkmbox, MBX_NOWAIT);
@@ -2959,11 +3727,13 @@ restart_disc:
                 * set port_state to PORT_READY if SLI2.
                 * cmpl_reg_vpi will set port_state to READY for SLI3.
                 */
-               if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)
-                       lpfc_issue_reg_vpi(phba, vport);
-               else {  /* NPIV Not enabled */
-                       lpfc_issue_clear_la(phba, vport);
-                       vport->port_state = LPFC_VPORT_READY;
+               if (phba->sli_rev < LPFC_SLI_REV4) {
+                       if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)
+                               lpfc_issue_reg_vpi(phba, vport);
+                       else  { /* NPIV Not enabled */
+                               lpfc_issue_clear_la(phba, vport);
+                               vport->port_state = LPFC_VPORT_READY;
+                       }
                }
                break;
 
@@ -3036,7 +3806,7 @@ restart_disc:
 void
 lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
 {
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
        struct lpfc_dmabuf   *mp = (struct lpfc_dmabuf *) (pmb->context1);
        struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) pmb->context2;
        struct lpfc_vport    *vport = pmb->vport;
@@ -3044,6 +3814,7 @@ lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
        pmb->context1 = NULL;
 
        ndlp->nlp_rpi = mb->un.varWords[0];
+       ndlp->nlp_flag |= NLP_RPI_VALID;
        ndlp->nlp_type |= NLP_FABRIC;
        lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
 
@@ -3297,3 +4068,395 @@ lpfc_nlp_not_used(struct lpfc_nodelist *ndlp)
                        return 1;
        return 0;
 }
+
+/**
+ * lpfc_fcf_inuse - Check if FCF can be unregistered.
+ * @phba: Pointer to hba context object.
+ *
+ * This function iterate through all FC nodes associated
+ * will all vports to check if there is any node with
+ * fc_rports associated with it. If there is an fc_rport
+ * associated with the node, then the node is either in
+ * discovered state or its devloss_timer is pending.
+ */
+static int
+lpfc_fcf_inuse(struct lpfc_hba *phba)
+{
+       struct lpfc_vport **vports;
+       int i, ret = 0;
+       struct lpfc_nodelist *ndlp;
+       struct Scsi_Host  *shost;
+
+       vports = lpfc_create_vport_work_array(phba);
+
+       for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
+               shost = lpfc_shost_from_vport(vports[i]);
+               spin_lock_irq(shost->host_lock);
+               list_for_each_entry(ndlp, &vports[i]->fc_nodes, nlp_listp) {
+                       if (NLP_CHK_NODE_ACT(ndlp) && ndlp->rport &&
+                         (ndlp->rport->roles & FC_RPORT_ROLE_FCP_TARGET)) {
+                               ret = 1;
+                               spin_unlock_irq(shost->host_lock);
+                               goto out;
+                       }
+               }
+               spin_unlock_irq(shost->host_lock);
+       }
+out:
+       lpfc_destroy_vport_work_array(phba, vports);
+       return ret;
+}
+
+/**
+ * lpfc_unregister_vfi_cmpl - Completion handler for unreg vfi.
+ * @phba: Pointer to hba context object.
+ * @mboxq: Pointer to mailbox object.
+ *
+ * This function frees memory associated with the mailbox command.
+ */
+static void
+lpfc_unregister_vfi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+       struct lpfc_vport *vport = mboxq->vport;
+
+       if (mboxq->u.mb.mbxStatus) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
+                       "2555 UNREG_VFI mbxStatus error x%x "
+                       "HBA state x%x\n",
+                       mboxq->u.mb.mbxStatus, vport->port_state);
+       }
+       mempool_free(mboxq, phba->mbox_mem_pool);
+       return;
+}
+
+/**
+ * lpfc_unregister_fcfi_cmpl - Completion handler for unreg fcfi.
+ * @phba: Pointer to hba context object.
+ * @mboxq: Pointer to mailbox object.
+ *
+ * This function frees memory associated with the mailbox command.
+ */
+static void
+lpfc_unregister_fcfi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+       struct lpfc_vport *vport = mboxq->vport;
+
+       if (mboxq->u.mb.mbxStatus) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
+                       "2550 UNREG_FCFI mbxStatus error x%x "
+                       "HBA state x%x\n",
+                       mboxq->u.mb.mbxStatus, vport->port_state);
+       }
+       mempool_free(mboxq, phba->mbox_mem_pool);
+       return;
+}
+
+/**
+ * lpfc_unregister_unused_fcf - Unregister FCF if all devices are disconnected.
+ * @phba: Pointer to hba context object.
+ *
+ * This function check if there are any connected remote port for the FCF and
+ * if all the devices are disconnected, this function unregister FCFI.
+ * This function also tries to use another FCF for discovery.
+ */
+void
+lpfc_unregister_unused_fcf(struct lpfc_hba *phba)
+{
+       LPFC_MBOXQ_t *mbox;
+       int rc;
+       struct lpfc_vport **vports;
+       int i;
+
+       spin_lock_irq(&phba->hbalock);
+       /*
+        * If HBA is not running in FIP mode or
+        * If HBA does not support FCoE or
+        * If FCF is not registered.
+        * do nothing.
+        */
+       if (!(phba->hba_flag & HBA_FCOE_SUPPORT) ||
+               !(phba->fcf.fcf_flag & FCF_REGISTERED) ||
+               (phba->cfg_enable_fip == 0)) {
+               spin_unlock_irq(&phba->hbalock);
+               return;
+       }
+       spin_unlock_irq(&phba->hbalock);
+
+       if (lpfc_fcf_inuse(phba))
+               return;
+
+
+       /* Unregister VPIs */
+       vports = lpfc_create_vport_work_array(phba);
+       if (vports &&
+               (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED))
+               for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
+                       lpfc_mbx_unreg_vpi(vports[i]);
+                       vports[i]->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
+                       vports[i]->vfi_state &= ~LPFC_VFI_REGISTERED;
+               }
+       lpfc_destroy_vport_work_array(phba, vports);
+
+       /* Unregister VFI */
+       mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mbox) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
+                       "2556 UNREG_VFI mbox allocation failed"
+                       "HBA state x%x\n",
+                       phba->pport->port_state);
+               return;
+       }
+
+       lpfc_unreg_vfi(mbox, phba->pport->vfi);
+       mbox->vport = phba->pport;
+       mbox->mbox_cmpl = lpfc_unregister_vfi_cmpl;
+
+       rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+       if (rc == MBX_NOT_FINISHED) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
+                       "2557 UNREG_VFI issue mbox failed rc x%x "
+                       "HBA state x%x\n",
+                       rc, phba->pport->port_state);
+               mempool_free(mbox, phba->mbox_mem_pool);
+               return;
+       }
+
+       /* Unregister FCF */
+       mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mbox) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
+                       "2551 UNREG_FCFI mbox allocation failed"
+                       "HBA state x%x\n",
+                       phba->pport->port_state);
+               return;
+       }
+
+       lpfc_unreg_fcfi(mbox, phba->fcf.fcfi);
+       mbox->vport = phba->pport;
+       mbox->mbox_cmpl = lpfc_unregister_fcfi_cmpl;
+       rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+
+       if (rc == MBX_NOT_FINISHED) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
+                       "2552 UNREG_FCFI issue mbox failed rc x%x "
+                       "HBA state x%x\n",
+                       rc, phba->pport->port_state);
+               mempool_free(mbox, phba->mbox_mem_pool);
+               return;
+       }
+
+       spin_lock_irq(&phba->hbalock);
+       phba->fcf.fcf_flag &= ~(FCF_AVAILABLE | FCF_REGISTERED |
+               FCF_DISCOVERED | FCF_BOOT_ENABLE | FCF_IN_USE |
+               FCF_VALID_VLAN);
+       spin_unlock_irq(&phba->hbalock);
+
+       /*
+        * If driver is not unloading, check if there is any other
+        * FCF record that can be used for discovery.
+        */
+       if ((phba->pport->load_flag & FC_UNLOADING) ||
+               (phba->link_state < LPFC_LINK_UP))
+               return;
+
+       rc = lpfc_sli4_read_fcf_record(phba, LPFC_FCOE_FCF_GET_FIRST);
+
+       if (rc)
+               lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
+                       "2553 lpfc_unregister_unused_fcf failed to read FCF"
+                       " record HBA state x%x\n",
+                       phba->pport->port_state);
+}
+
+/**
+ * lpfc_read_fcf_conn_tbl - Create driver FCF connection table.
+ * @phba: Pointer to hba context object.
+ * @buff: Buffer containing the FCF connection table as in the config
+ *         region.
+ * This function create driver data structure for the FCF connection
+ * record table read from config region 23.
+ */
+static void
+lpfc_read_fcf_conn_tbl(struct lpfc_hba *phba,
+       uint8_t *buff)
+{
+       struct lpfc_fcf_conn_entry *conn_entry, *next_conn_entry;
+       struct lpfc_fcf_conn_hdr *conn_hdr;
+       struct lpfc_fcf_conn_rec *conn_rec;
+       uint32_t record_count;
+       int i;
+
+       /* Free the current connect table */
+       list_for_each_entry_safe(conn_entry, next_conn_entry,
+               &phba->fcf_conn_rec_list, list)
+               kfree(conn_entry);
+
+       conn_hdr = (struct lpfc_fcf_conn_hdr *) buff;
+       record_count = conn_hdr->length * sizeof(uint32_t)/
+               sizeof(struct lpfc_fcf_conn_rec);
+
+       conn_rec = (struct lpfc_fcf_conn_rec *)
+               (buff + sizeof(struct lpfc_fcf_conn_hdr));
+
+       for (i = 0; i < record_count; i++) {
+               if (!(conn_rec[i].flags & FCFCNCT_VALID))
+                       continue;
+               conn_entry = kzalloc(sizeof(struct lpfc_fcf_conn_entry),
+                       GFP_KERNEL);
+               if (!conn_entry) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2566 Failed to allocate connection"
+                               " table entry\n");
+                       return;
+               }
+
+               memcpy(&conn_entry->conn_rec, &conn_rec[i],
+                       sizeof(struct lpfc_fcf_conn_rec));
+               conn_entry->conn_rec.vlan_tag =
+                       le16_to_cpu(conn_entry->conn_rec.vlan_tag) & 0xFFF;
+               conn_entry->conn_rec.flags =
+                       le16_to_cpu(conn_entry->conn_rec.flags);
+               list_add_tail(&conn_entry->list,
+                       &phba->fcf_conn_rec_list);
+       }
+}
+
+/**
+ * lpfc_read_fcoe_param - Read FCoe parameters from conf region..
+ * @phba: Pointer to hba context object.
+ * @buff: Buffer containing the FCoE parameter data structure.
+ *
+ *  This function update driver data structure with config
+ *  parameters read from config region 23.
+ */
+static void
+lpfc_read_fcoe_param(struct lpfc_hba *phba,
+                       uint8_t *buff)
+{
+       struct lpfc_fip_param_hdr *fcoe_param_hdr;
+       struct lpfc_fcoe_params *fcoe_param;
+
+       fcoe_param_hdr = (struct lpfc_fip_param_hdr *)
+               buff;
+       fcoe_param = (struct lpfc_fcoe_params *)
+               buff + sizeof(struct lpfc_fip_param_hdr);
+
+       if ((fcoe_param_hdr->parm_version != FIPP_VERSION) ||
+               (fcoe_param_hdr->length != FCOE_PARAM_LENGTH))
+               return;
+
+       if (bf_get(lpfc_fip_param_hdr_fipp_mode, fcoe_param_hdr) ==
+                       FIPP_MODE_ON)
+               phba->cfg_enable_fip = 1;
+
+       if (bf_get(lpfc_fip_param_hdr_fipp_mode, fcoe_param_hdr) ==
+               FIPP_MODE_OFF)
+               phba->cfg_enable_fip = 0;
+
+       if (fcoe_param_hdr->parm_flags & FIPP_VLAN_VALID) {
+               phba->valid_vlan = 1;
+               phba->vlan_id = le16_to_cpu(fcoe_param->vlan_tag) &
+                       0xFFF;
+       }
+
+       phba->fc_map[0] = fcoe_param->fc_map[0];
+       phba->fc_map[1] = fcoe_param->fc_map[1];
+       phba->fc_map[2] = fcoe_param->fc_map[2];
+       return;
+}
+
+/**
+ * lpfc_get_rec_conf23 - Get a record type in config region data.
+ * @buff: Buffer containing config region 23 data.
+ * @size: Size of the data buffer.
+ * @rec_type: Record type to be searched.
+ *
+ * This function searches config region data to find the begining
+ * of the record specified by record_type. If record found, this
+ * function return pointer to the record else return NULL.
+ */
+static uint8_t *
+lpfc_get_rec_conf23(uint8_t *buff, uint32_t size, uint8_t rec_type)
+{
+       uint32_t offset = 0, rec_length;
+
+       if ((buff[0] == LPFC_REGION23_LAST_REC) ||
+               (size < sizeof(uint32_t)))
+               return NULL;
+
+       rec_length = buff[offset + 1];
+
+       /*
+        * One TLV record has one word header and number of data words
+        * specified in the rec_length field of the record header.
+        */
+       while ((offset + rec_length * sizeof(uint32_t) + sizeof(uint32_t))
+               <= size) {
+               if (buff[offset] == rec_type)
+                       return &buff[offset];
+
+               if (buff[offset] == LPFC_REGION23_LAST_REC)
+                       return NULL;
+
+               offset += rec_length * sizeof(uint32_t) + sizeof(uint32_t);
+               rec_length = buff[offset + 1];
+       }
+       return NULL;
+}
+
+/**
+ * lpfc_parse_fcoe_conf - Parse FCoE config data read from config region 23.
+ * @phba: Pointer to lpfc_hba data structure.
+ * @buff: Buffer containing config region 23 data.
+ * @size: Size of the data buffer.
+ *
+ * This fuction parse the FCoE config parameters in config region 23 and
+ * populate driver data structure with the parameters.
+ */
+void
+lpfc_parse_fcoe_conf(struct lpfc_hba *phba,
+               uint8_t *buff,
+               uint32_t size)
+{
+       uint32_t offset = 0, rec_length;
+       uint8_t *rec_ptr;
+
+       /*
+        * If data size is less than 2 words signature and version cannot be
+        * verified.
+        */
+       if (size < 2*sizeof(uint32_t))
+               return;
+
+       /* Check the region signature first */
+       if (memcmp(buff, LPFC_REGION23_SIGNATURE, 4)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                       "2567 Config region 23 has bad signature\n");
+               return;
+       }
+
+       offset += 4;
+
+       /* Check the data structure version */
+       if (buff[offset] != LPFC_REGION23_VERSION) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                       "2568 Config region 23 has bad version\n");
+               return;
+       }
+       offset += 4;
+
+       rec_length = buff[offset + 1];
+
+       /* Read FCoE param record */
+       rec_ptr = lpfc_get_rec_conf23(&buff[offset],
+                       size - offset, FCOE_PARAM_TYPE);
+       if (rec_ptr)
+               lpfc_read_fcoe_param(phba, rec_ptr);
+
+       /* Read FCF connection table */
+       rec_ptr = lpfc_get_rec_conf23(&buff[offset],
+               size - offset, FCOE_CONN_TBL_TYPE);
+       if (rec_ptr)
+               lpfc_read_fcf_conn_tbl(phba, rec_ptr);
+
+}
index 4168c7b498b87d6e3b79759eb5887931dbfe5f5f..02aa016b93e926c2f6808f2e83d353e10359df55 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2008 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2009 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  *                                                                 *
@@ -470,6 +470,35 @@ struct serv_parm { /* Structure is in Big Endian format */
        uint8_t vendorVersion[16];
 };
 
+/*
+ * Virtual Fabric Tagging Header
+ */
+struct fc_vft_header {
+        uint32_t word0;
+#define fc_vft_hdr_r_ctl_SHIFT         24
+#define fc_vft_hdr_r_ctl_MASK          0xFF
+#define fc_vft_hdr_r_ctl_WORD          word0
+#define fc_vft_hdr_ver_SHIFT           22
+#define fc_vft_hdr_ver_MASK            0x3
+#define fc_vft_hdr_ver_WORD            word0
+#define fc_vft_hdr_type_SHIFT          18
+#define fc_vft_hdr_type_MASK           0xF
+#define fc_vft_hdr_type_WORD           word0
+#define fc_vft_hdr_e_SHIFT             16
+#define fc_vft_hdr_e_MASK              0x1
+#define fc_vft_hdr_e_WORD              word0
+#define fc_vft_hdr_priority_SHIFT      13
+#define fc_vft_hdr_priority_MASK       0x7
+#define fc_vft_hdr_priority_WORD       word0
+#define fc_vft_hdr_vf_id_SHIFT         1
+#define fc_vft_hdr_vf_id_MASK          0xFFF
+#define fc_vft_hdr_vf_id_WORD          word0
+       uint32_t word1;
+#define fc_vft_hdr_hopct_SHIFT         24
+#define fc_vft_hdr_hopct_MASK          0xFF
+#define fc_vft_hdr_hopct_WORD          word1
+};
+
 /*
  *  Extended Link Service LS_COMMAND codes (Payload Word 0)
  */
@@ -1152,6 +1181,9 @@ typedef struct {
 #define PCI_DEVICE_ID_HORNET        0xfe05
 #define PCI_DEVICE_ID_ZEPHYR_SCSP   0xfe11
 #define PCI_DEVICE_ID_ZEPHYR_DCSP   0xfe12
+#define PCI_VENDOR_ID_SERVERENGINE  0x19a2
+#define PCI_DEVICE_ID_TIGERSHARK    0x0704
+#define PCI_DEVICE_ID_TIGERSHARK_S  0x0705
 
 #define JEDEC_ID_ADDRESS            0x0080001c
 #define FIREFLY_JEDEC_ID            0x1ACC
@@ -1342,15 +1374,21 @@ typedef struct {                /* FireFly BIU registers */
 #define MBX_READ_LA64       0x95
 #define MBX_REG_VPI        0x96
 #define MBX_UNREG_VPI      0x97
-#define MBX_REG_VNPID      0x96
-#define MBX_UNREG_VNPID            0x97
 
 #define MBX_WRITE_WWN       0x98
 #define MBX_SET_DEBUG       0x99
 #define MBX_LOAD_EXP_ROM    0x9C
-
-#define MBX_MAX_CMDS        0x9D
+#define MBX_SLI4_CONFIG            0x9B
+#define MBX_SLI4_REQ_FTRS   0x9D
+#define MBX_MAX_CMDS        0x9E
+#define MBX_RESUME_RPI      0x9E
 #define MBX_SLI2_CMD_MASK   0x80
+#define MBX_REG_VFI         0x9F
+#define MBX_REG_FCFI        0xA0
+#define MBX_UNREG_VFI       0xA1
+#define MBX_UNREG_FCFI     0xA2
+#define MBX_INIT_VFI        0xA3
+#define MBX_INIT_VPI        0xA4
 
 /* IOCB Commands */
 
@@ -1440,6 +1478,16 @@ typedef struct {         /* FireFly BIU registers */
 #define CMD_IOCB_LOGENTRY_CN           0x94
 #define CMD_IOCB_LOGENTRY_ASYNC_CN     0x96
 
+/* Unhandled Data Security SLI Commands */
+#define DSSCMD_IWRITE64_CR             0xD8
+#define DSSCMD_IWRITE64_CX             0xD9
+#define DSSCMD_IREAD64_CR              0xDA
+#define DSSCMD_IREAD64_CX              0xDB
+#define DSSCMD_INVALIDATE_DEK          0xDC
+#define DSSCMD_SET_KEK                 0xDD
+#define DSSCMD_GET_KEK_ID              0xDE
+#define DSSCMD_GEN_XFER                        0xDF
+
 #define CMD_MAX_IOCB_CMD        0xE6
 #define CMD_IOCB_MASK           0xff
 
@@ -1466,6 +1514,7 @@ typedef struct {          /* FireFly BIU registers */
 #define MBXERR_BAD_RCV_LENGTH       14
 #define MBXERR_DMA_ERROR            15
 #define MBXERR_ERROR                16
+#define MBXERR_LINK_DOWN            0x33
 #define MBX_NOT_FINISHED           255
 
 #define MBX_BUSY                   0xffffff /* Attempted cmd to busy Mailbox */
@@ -1504,32 +1553,6 @@ struct ulp_bde {
 #endif
 };
 
-struct ulp_bde64 {     /* SLI-2 */
-       union ULP_BDE_TUS {
-               uint32_t w;
-               struct {
-#ifdef __BIG_ENDIAN_BITFIELD
-                       uint32_t bdeFlags:8;    /* BDE Flags 0 IS A SUPPORTED
-                                                  VALUE !! */
-                       uint32_t bdeSize:24;    /* Size of buffer (in bytes) */
-#else  /*  __LITTLE_ENDIAN_BITFIELD */
-                       uint32_t bdeSize:24;    /* Size of buffer (in bytes) */
-                       uint32_t bdeFlags:8;    /* BDE Flags 0 IS A SUPPORTED
-                                                  VALUE !! */
-#endif
-#define BUFF_TYPE_BDE_64    0x00       /* BDE (Host_resident) */
-#define BUFF_TYPE_BDE_IMMED 0x01       /* Immediate Data BDE */
-#define BUFF_TYPE_BDE_64P   0x02       /* BDE (Port-resident) */
-#define BUFF_TYPE_BDE_64I   0x08       /* Input BDE (Host-resident) */
-#define BUFF_TYPE_BDE_64IP  0x0A       /* Input BDE (Port-resident) */
-#define BUFF_TYPE_BLP_64    0x40       /* BLP (Host-resident) */
-#define BUFF_TYPE_BLP_64P   0x42       /* BLP (Port-resident) */
-               } f;
-       } tus;
-       uint32_t addrLow;
-       uint32_t addrHigh;
-};
-
 typedef struct ULP_BDL {       /* SLI-2 */
 #ifdef __BIG_ENDIAN_BITFIELD
        uint32_t bdeFlags:8;    /* BDL Flags */
@@ -2287,7 +2310,7 @@ typedef struct {
        uint32_t rsvd3;
        uint32_t rsvd4;
        uint32_t rsvd5;
-       uint16_t rsvd6;
+       uint16_t vfi;
        uint16_t vpi;
 #else  /*  __LITTLE_ENDIAN */
        uint32_t rsvd1;
@@ -2297,7 +2320,7 @@ typedef struct {
        uint32_t rsvd4;
        uint32_t rsvd5;
        uint16_t vpi;
-       uint16_t rsvd6;
+       uint16_t vfi;
 #endif
 } REG_VPI_VAR;
 
@@ -2457,7 +2480,7 @@ typedef struct {
        uint32_t entry_index:16;
 #endif
 
-       uint32_t rsvd1;
+       uint32_t sli4_length;
        uint32_t word_cnt;
        uint32_t resp_offset;
 } DUMP_VAR;
@@ -2470,9 +2493,32 @@ typedef struct {
 #define  DMP_RSP_OFFSET          0x14   /* word 5 contains first word of rsp */
 #define  DMP_RSP_SIZE            0x6C   /* maximum of 27 words of rsp data */
 
+#define  DMP_REGION_VPORT       0x16   /* VPort info region */
+#define  DMP_VPORT_REGION_SIZE  0x200
+#define  DMP_MBOX_OFFSET_WORD   0x5
+
+#define  DMP_REGION_FCOEPARAM   0x17   /* fcoe param region */
+#define  DMP_FCOEPARAM_RGN_SIZE         0x400
+
 #define  WAKE_UP_PARMS_REGION_ID    4
 #define  WAKE_UP_PARMS_WORD_SIZE   15
 
+struct vport_rec {
+       uint8_t wwpn[8];
+       uint8_t wwnn[8];
+};
+
+#define VPORT_INFO_SIG 0x32324752
+#define VPORT_INFO_REV_MASK 0xff
+#define VPORT_INFO_REV 0x1
+#define MAX_STATIC_VPORT_COUNT 16
+struct static_vport_info {
+       uint32_t                signature;
+       uint32_t                rev;
+       struct vport_rec        vport_list[MAX_STATIC_VPORT_COUNT];
+       uint32_t                resvd[66];
+};
+
 /* Option rom version structure */
 struct prog_id {
 #ifdef __BIG_ENDIAN_BITFIELD
@@ -2697,7 +2743,9 @@ typedef struct {
 #endif
 
 #ifdef __BIG_ENDIAN_BITFIELD
-       uint32_t rsvd1     : 23;  /* Reserved                             */
+       uint32_t rsvd1     : 19;  /* Reserved                             */
+       uint32_t cdss      :  1;  /* Configure Data Security SLI          */
+       uint32_t rsvd2     :  3;  /* Reserved                             */
        uint32_t cbg       :  1;  /* Configure BlockGuard                 */
        uint32_t cmv       :  1;  /* Configure Max VPIs                   */
        uint32_t ccrp      :  1;  /* Config Command Ring Polling          */
@@ -2717,10 +2765,14 @@ typedef struct {
        uint32_t ccrp      :  1;  /* Config Command Ring Polling          */
        uint32_t cmv       :  1;  /* Configure Max VPIs                   */
        uint32_t cbg       :  1;  /* Configure BlockGuard                 */
-       uint32_t rsvd1     : 23;  /* Reserved                             */
+       uint32_t rsvd2     :  3;  /* Reserved                             */
+       uint32_t cdss      :  1;  /* Configure Data Security SLI          */
+       uint32_t rsvd1     : 19;  /* Reserved                             */
 #endif
 #ifdef __BIG_ENDIAN_BITFIELD
-       uint32_t rsvd2     : 23;  /* Reserved                             */
+       uint32_t rsvd3     : 19;  /* Reserved                             */
+       uint32_t gdss      :  1;  /* Configure Data Security SLI          */
+       uint32_t rsvd4     :  3;  /* Reserved                             */
        uint32_t gbg       :  1;  /* Grant BlockGuard                     */
        uint32_t gmv       :  1;  /* Grant Max VPIs                       */
        uint32_t gcrp      :  1;  /* Grant Command Ring Polling           */
@@ -2740,7 +2792,9 @@ typedef struct {
        uint32_t gcrp      :  1;  /* Grant Command Ring Polling           */
        uint32_t gmv       :  1;  /* Grant Max VPIs                       */
        uint32_t gbg       :  1;  /* Grant BlockGuard                     */
-       uint32_t rsvd2     : 23;  /* Reserved                             */
+       uint32_t rsvd4     :  3;  /* Reserved                             */
+       uint32_t gdss      :  1;  /* Configure Data Security SLI          */
+       uint32_t rsvd3     : 19;  /* Reserved                             */
 #endif
 
 #ifdef __BIG_ENDIAN_BITFIELD
@@ -2753,20 +2807,20 @@ typedef struct {
 
 #ifdef __BIG_ENDIAN_BITFIELD
        uint32_t max_hbq   : 16;  /* Max HBQs Host expect to configure    */
-       uint32_t rsvd3     : 16;  /* Max HBQs Host expect to configure    */
+       uint32_t rsvd5     : 16;  /* Max HBQs Host expect to configure    */
 #else  /*  __LITTLE_ENDIAN */
-       uint32_t rsvd3     : 16;  /* Max HBQs Host expect to configure    */
+       uint32_t rsvd5     : 16;  /* Max HBQs Host expect to configure    */
        uint32_t max_hbq   : 16;  /* Max HBQs Host expect to configure    */
 #endif
 
-       uint32_t rsvd4;           /* Reserved                             */
+       uint32_t rsvd6;           /* Reserved                             */
 
 #ifdef __BIG_ENDIAN_BITFIELD
-       uint32_t rsvd5      : 16;  /* Reserved                             */
+       uint32_t rsvd7      : 16;  /* Reserved                             */
        uint32_t max_vpi    : 16;  /* Max number of virt N-Ports           */
 #else  /*  __LITTLE_ENDIAN */
        uint32_t max_vpi    : 16;  /* Max number of virt N-Ports           */
-       uint32_t rsvd5      : 16;  /* Reserved                             */
+       uint32_t rsvd7      : 16;  /* Reserved                             */
 #endif
 
 } CONFIG_PORT_VAR;
@@ -3666,3 +3720,5 @@ lpfc_error_lost_link(IOCB_t *iocbp)
 #define MENLO_TIMEOUT 30
 #define SETVAR_MLOMNT 0x103107
 #define SETVAR_MLORST 0x103007
+
+#define BPL_ALIGN_SZ 8 /* 8 byte alignment for bpl and mbufs */
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
new file mode 100644 (file)
index 0000000..39c34b3
--- /dev/null
@@ -0,0 +1,2141 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for         *
+ * Fibre Channel Host Bus Adapters.                                *
+ * Copyright (C) 2009 Emulex.  All rights reserved.                *
+ * EMULEX and SLI are trademarks of Emulex.                        *
+ * www.emulex.com                                                  *
+ *                                                                 *
+ * 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 will be useful. *
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND          *
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,  *
+ * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE      *
+ * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
+ * TO BE LEGALLY INVALID.  See the GNU General Public License for  *
+ * more details, a copy of which can be found in the file COPYING  *
+ * included with this package.                                     *
+ *******************************************************************/
+
+/* Macros to deal with bit fields. Each bit field must have 3 #defines
+ * associated with it (_SHIFT, _MASK, and _WORD).
+ * EG. For a bit field that is in the 7th bit of the "field4" field of a
+ * structure and is 2 bits in size the following #defines must exist:
+ *     struct temp {
+ *             uint32_t        field1;
+ *             uint32_t        field2;
+ *             uint32_t        field3;
+ *             uint32_t        field4;
+ *     #define example_bit_field_SHIFT         7
+ *     #define example_bit_field_MASK          0x03
+ *     #define example_bit_field_WORD          field4
+ *             uint32_t        field5;
+ *     };
+ * Then the macros below may be used to get or set the value of that field.
+ * EG. To get the value of the bit field from the above example:
+ *     struct temp t1;
+ *     value = bf_get(example_bit_field, &t1);
+ * And then to set that bit field:
+ *     bf_set(example_bit_field, &t1, 2);
+ * Or clear that bit field:
+ *     bf_set(example_bit_field, &t1, 0);
+ */
+#define bf_get(name, ptr) \
+       (((ptr)->name##_WORD >> name##_SHIFT) & name##_MASK)
+#define bf_set(name, ptr, value) \
+       ((ptr)->name##_WORD = ((((value) & name##_MASK) << name##_SHIFT) | \
+                ((ptr)->name##_WORD & ~(name##_MASK << name##_SHIFT))))
+
+struct dma_address {
+       uint32_t addr_lo;
+       uint32_t addr_hi;
+};
+
+#define LPFC_SLI4_BAR0         1
+#define LPFC_SLI4_BAR1         2
+#define LPFC_SLI4_BAR2         4
+
+#define LPFC_SLI4_MBX_EMBED    true
+#define LPFC_SLI4_MBX_NEMBED   false
+
+#define LPFC_SLI4_MB_WORD_COUNT                64
+#define LPFC_MAX_MQ_PAGE               8
+#define LPFC_MAX_WQ_PAGE               8
+#define LPFC_MAX_CQ_PAGE               4
+#define LPFC_MAX_EQ_PAGE               8
+
+#define LPFC_VIR_FUNC_MAX       32 /* Maximum number of virtual functions */
+#define LPFC_PCI_FUNC_MAX        5 /* Maximum number of PCI functions */
+#define LPFC_VFR_PAGE_SIZE     0x1000 /* 4KB BAR2 per-VF register page size */
+
+/* Define SLI4 Alignment requirements. */
+#define LPFC_ALIGN_16_BYTE     16
+#define LPFC_ALIGN_64_BYTE     64
+
+/* Define SLI4 specific definitions. */
+#define LPFC_MQ_CQE_BYTE_OFFSET        256
+#define LPFC_MBX_CMD_HDR_LENGTH 16
+#define LPFC_MBX_ERROR_RANGE   0x4000
+#define LPFC_BMBX_BIT1_ADDR_HI 0x2
+#define LPFC_BMBX_BIT1_ADDR_LO 0
+#define LPFC_RPI_HDR_COUNT     64
+#define LPFC_HDR_TEMPLATE_SIZE 4096
+#define LPFC_RPI_ALLOC_ERROR   0xFFFF
+#define LPFC_FCF_RECORD_WD_CNT 132
+#define LPFC_ENTIRE_FCF_DATABASE 0
+#define LPFC_DFLT_FCF_INDEX     0
+
+/* Virtual function numbers */
+#define LPFC_VF0               0
+#define LPFC_VF1               1
+#define LPFC_VF2               2
+#define LPFC_VF3               3
+#define LPFC_VF4               4
+#define LPFC_VF5               5
+#define LPFC_VF6               6
+#define LPFC_VF7               7
+#define LPFC_VF8               8
+#define LPFC_VF9               9
+#define LPFC_VF10              10
+#define LPFC_VF11              11
+#define LPFC_VF12              12
+#define LPFC_VF13              13
+#define LPFC_VF14              14
+#define LPFC_VF15              15
+#define LPFC_VF16              16
+#define LPFC_VF17              17
+#define LPFC_VF18              18
+#define LPFC_VF19              19
+#define LPFC_VF20              20
+#define LPFC_VF21              21
+#define LPFC_VF22              22
+#define LPFC_VF23              23
+#define LPFC_VF24              24
+#define LPFC_VF25              25
+#define LPFC_VF26              26
+#define LPFC_VF27              27
+#define LPFC_VF28              28
+#define LPFC_VF29              29
+#define LPFC_VF30              30
+#define LPFC_VF31              31
+
+/* PCI function numbers */
+#define LPFC_PCI_FUNC0         0
+#define LPFC_PCI_FUNC1         1
+#define LPFC_PCI_FUNC2         2
+#define LPFC_PCI_FUNC3         3
+#define LPFC_PCI_FUNC4         4
+
+/* Active interrupt test count */
+#define LPFC_ACT_INTR_CNT      4
+
+/* Delay Multiplier constant */
+#define LPFC_DMULT_CONST       651042
+#define LPFC_MIM_IMAX          636
+#define LPFC_FP_DEF_IMAX       10000
+#define LPFC_SP_DEF_IMAX       10000
+
+struct ulp_bde64 {
+       union ULP_BDE_TUS {
+               uint32_t w;
+               struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+                       uint32_t bdeFlags:8;    /* BDE Flags 0 IS A SUPPORTED
+                                                  VALUE !! */
+                       uint32_t bdeSize:24;    /* Size of buffer (in bytes) */
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+                       uint32_t bdeSize:24;    /* Size of buffer (in bytes) */
+                       uint32_t bdeFlags:8;    /* BDE Flags 0 IS A SUPPORTED
+                                                  VALUE !! */
+#endif
+#define BUFF_TYPE_BDE_64    0x00       /* BDE (Host_resident) */
+#define BUFF_TYPE_BDE_IMMED 0x01       /* Immediate Data BDE */
+#define BUFF_TYPE_BDE_64P   0x02       /* BDE (Port-resident) */
+#define BUFF_TYPE_BDE_64I   0x08       /* Input BDE (Host-resident) */
+#define BUFF_TYPE_BDE_64IP  0x0A       /* Input BDE (Port-resident) */
+#define BUFF_TYPE_BLP_64    0x40       /* BLP (Host-resident) */
+#define BUFF_TYPE_BLP_64P   0x42       /* BLP (Port-resident) */
+               } f;
+       } tus;
+       uint32_t addrLow;
+       uint32_t addrHigh;
+};
+
+struct lpfc_sli4_flags {
+       uint32_t word0;
+#define lpfc_fip_flag_SHIFT 0
+#define lpfc_fip_flag_MASK 0x00000001
+#define lpfc_fip_flag_WORD word0
+};
+
+/* event queue entry structure */
+struct lpfc_eqe {
+       uint32_t word0;
+#define lpfc_eqe_resource_id_SHIFT     16
+#define lpfc_eqe_resource_id_MASK      0x000000FF
+#define lpfc_eqe_resource_id_WORD      word0
+#define lpfc_eqe_minor_code_SHIFT      4
+#define lpfc_eqe_minor_code_MASK       0x00000FFF
+#define lpfc_eqe_minor_code_WORD       word0
+#define lpfc_eqe_major_code_SHIFT      1
+#define lpfc_eqe_major_code_MASK       0x00000007
+#define lpfc_eqe_major_code_WORD       word0
+#define lpfc_eqe_valid_SHIFT           0
+#define lpfc_eqe_valid_MASK            0x00000001
+#define lpfc_eqe_valid_WORD            word0
+};
+
+/* completion queue entry structure (common fields for all cqe types) */
+struct lpfc_cqe {
+       uint32_t reserved0;
+       uint32_t reserved1;
+       uint32_t reserved2;
+       uint32_t word3;
+#define lpfc_cqe_valid_SHIFT           31
+#define lpfc_cqe_valid_MASK            0x00000001
+#define lpfc_cqe_valid_WORD            word3
+#define lpfc_cqe_code_SHIFT            16
+#define lpfc_cqe_code_MASK             0x000000FF
+#define lpfc_cqe_code_WORD             word3
+};
+
+/* Completion Queue Entry Status Codes */
+#define CQE_STATUS_SUCCESS             0x0
+#define CQE_STATUS_FCP_RSP_FAILURE     0x1
+#define CQE_STATUS_REMOTE_STOP         0x2
+#define CQE_STATUS_LOCAL_REJECT                0x3
+#define CQE_STATUS_NPORT_RJT           0x4
+#define CQE_STATUS_FABRIC_RJT          0x5
+#define CQE_STATUS_NPORT_BSY           0x6
+#define CQE_STATUS_FABRIC_BSY          0x7
+#define CQE_STATUS_INTERMED_RSP                0x8
+#define CQE_STATUS_LS_RJT              0x9
+#define CQE_STATUS_CMD_REJECT          0xb
+#define CQE_STATUS_FCP_TGT_LENCHECK    0xc
+#define CQE_STATUS_NEED_BUFF_ENTRY     0xf
+
+/* Status returned by hardware (valid only if status = CQE_STATUS_SUCCESS). */
+#define CQE_HW_STATUS_NO_ERR           0x0
+#define CQE_HW_STATUS_UNDERRUN         0x1
+#define CQE_HW_STATUS_OVERRUN          0x2
+
+/* Completion Queue Entry Codes */
+#define CQE_CODE_COMPL_WQE             0x1
+#define CQE_CODE_RELEASE_WQE           0x2
+#define CQE_CODE_RECEIVE               0x4
+#define CQE_CODE_XRI_ABORTED           0x5
+
+/* completion queue entry for wqe completions */
+struct lpfc_wcqe_complete {
+       uint32_t word0;
+#define lpfc_wcqe_c_request_tag_SHIFT  16
+#define lpfc_wcqe_c_request_tag_MASK   0x0000FFFF
+#define lpfc_wcqe_c_request_tag_WORD   word0
+#define lpfc_wcqe_c_status_SHIFT       8
+#define lpfc_wcqe_c_status_MASK                0x000000FF
+#define lpfc_wcqe_c_status_WORD                word0
+#define lpfc_wcqe_c_hw_status_SHIFT    0
+#define lpfc_wcqe_c_hw_status_MASK     0x000000FF
+#define lpfc_wcqe_c_hw_status_WORD     word0
+       uint32_t total_data_placed;
+       uint32_t parameter;
+       uint32_t word3;
+#define lpfc_wcqe_c_valid_SHIFT                lpfc_cqe_valid_SHIFT
+#define lpfc_wcqe_c_valid_MASK         lpfc_cqe_valid_MASK
+#define lpfc_wcqe_c_valid_WORD         lpfc_cqe_valid_WORD
+#define lpfc_wcqe_c_xb_SHIFT           28
+#define lpfc_wcqe_c_xb_MASK            0x00000001
+#define lpfc_wcqe_c_xb_WORD            word3
+#define lpfc_wcqe_c_pv_SHIFT           27
+#define lpfc_wcqe_c_pv_MASK            0x00000001
+#define lpfc_wcqe_c_pv_WORD            word3
+#define lpfc_wcqe_c_priority_SHIFT     24
+#define lpfc_wcqe_c_priority_MASK              0x00000007
+#define lpfc_wcqe_c_priority_WORD              word3
+#define lpfc_wcqe_c_code_SHIFT         lpfc_cqe_code_SHIFT
+#define lpfc_wcqe_c_code_MASK          lpfc_cqe_code_MASK
+#define lpfc_wcqe_c_code_WORD          lpfc_cqe_code_WORD
+};
+
+/* completion queue entry for wqe release */
+struct lpfc_wcqe_release {
+       uint32_t reserved0;
+       uint32_t reserved1;
+       uint32_t word2;
+#define lpfc_wcqe_r_wq_id_SHIFT                16
+#define lpfc_wcqe_r_wq_id_MASK         0x0000FFFF
+#define lpfc_wcqe_r_wq_id_WORD         word2
+#define lpfc_wcqe_r_wqe_index_SHIFT    0
+#define lpfc_wcqe_r_wqe_index_MASK     0x0000FFFF
+#define lpfc_wcqe_r_wqe_index_WORD     word2
+       uint32_t word3;
+#define lpfc_wcqe_r_valid_SHIFT                lpfc_cqe_valid_SHIFT
+#define lpfc_wcqe_r_valid_MASK         lpfc_cqe_valid_MASK
+#define lpfc_wcqe_r_valid_WORD         lpfc_cqe_valid_WORD
+#define lpfc_wcqe_r_code_SHIFT         lpfc_cqe_code_SHIFT
+#define lpfc_wcqe_r_code_MASK          lpfc_cqe_code_MASK
+#define lpfc_wcqe_r_code_WORD          lpfc_cqe_code_WORD
+};
+
+struct sli4_wcqe_xri_aborted {
+       uint32_t word0;
+#define lpfc_wcqe_xa_status_SHIFT              8
+#define lpfc_wcqe_xa_status_MASK               0x000000FF
+#define lpfc_wcqe_xa_status_WORD               word0
+       uint32_t parameter;
+       uint32_t word2;
+#define lpfc_wcqe_xa_remote_xid_SHIFT  16
+#define lpfc_wcqe_xa_remote_xid_MASK   0x0000FFFF
+#define lpfc_wcqe_xa_remote_xid_WORD   word2
+#define lpfc_wcqe_xa_xri_SHIFT         0
+#define lpfc_wcqe_xa_xri_MASK          0x0000FFFF
+#define lpfc_wcqe_xa_xri_WORD          word2
+       uint32_t word3;
+#define lpfc_wcqe_xa_valid_SHIFT       lpfc_cqe_valid_SHIFT
+#define lpfc_wcqe_xa_valid_MASK                lpfc_cqe_valid_MASK
+#define lpfc_wcqe_xa_valid_WORD                lpfc_cqe_valid_WORD
+#define lpfc_wcqe_xa_ia_SHIFT          30
+#define lpfc_wcqe_xa_ia_MASK           0x00000001
+#define lpfc_wcqe_xa_ia_WORD           word3
+#define CQE_XRI_ABORTED_IA_REMOTE      0
+#define CQE_XRI_ABORTED_IA_LOCAL       1
+#define lpfc_wcqe_xa_br_SHIFT          29
+#define lpfc_wcqe_xa_br_MASK           0x00000001
+#define lpfc_wcqe_xa_br_WORD           word3
+#define CQE_XRI_ABORTED_BR_BA_ACC      0
+#define CQE_XRI_ABORTED_BR_BA_RJT      1
+#define lpfc_wcqe_xa_eo_SHIFT          28
+#define lpfc_wcqe_xa_eo_MASK           0x00000001
+#define lpfc_wcqe_xa_eo_WORD           word3
+#define CQE_XRI_ABORTED_EO_REMOTE      0
+#define CQE_XRI_ABORTED_EO_LOCAL       1
+#define lpfc_wcqe_xa_code_SHIFT                lpfc_cqe_code_SHIFT
+#define lpfc_wcqe_xa_code_MASK         lpfc_cqe_code_MASK
+#define lpfc_wcqe_xa_code_WORD         lpfc_cqe_code_WORD
+};
+
+/* completion queue entry structure for rqe completion */
+struct lpfc_rcqe {
+       uint32_t word0;
+#define lpfc_rcqe_bindex_SHIFT         16
+#define lpfc_rcqe_bindex_MASK          0x0000FFF
+#define lpfc_rcqe_bindex_WORD          word0
+#define lpfc_rcqe_status_SHIFT         8
+#define lpfc_rcqe_status_MASK          0x000000FF
+#define lpfc_rcqe_status_WORD          word0
+#define FC_STATUS_RQ_SUCCESS           0x10 /* Async receive successful */
+#define FC_STATUS_RQ_BUF_LEN_EXCEEDED  0x11 /* payload truncated */
+#define FC_STATUS_INSUFF_BUF_NEED_BUF  0x12 /* Insufficient buffers */
+#define FC_STATUS_INSUFF_BUF_FRM_DISC  0x13 /* Frame Discard */
+       uint32_t reserved1;
+       uint32_t word2;
+#define lpfc_rcqe_length_SHIFT         16
+#define lpfc_rcqe_length_MASK          0x0000FFFF
+#define lpfc_rcqe_length_WORD          word2
+#define lpfc_rcqe_rq_id_SHIFT          6
+#define lpfc_rcqe_rq_id_MASK           0x000003FF
+#define lpfc_rcqe_rq_id_WORD           word2
+#define lpfc_rcqe_fcf_id_SHIFT         0
+#define lpfc_rcqe_fcf_id_MASK          0x0000003F
+#define lpfc_rcqe_fcf_id_WORD          word2
+       uint32_t word3;
+#define lpfc_rcqe_valid_SHIFT          lpfc_cqe_valid_SHIFT
+#define lpfc_rcqe_valid_MASK           lpfc_cqe_valid_MASK
+#define lpfc_rcqe_valid_WORD           lpfc_cqe_valid_WORD
+#define lpfc_rcqe_port_SHIFT           30
+#define lpfc_rcqe_port_MASK            0x00000001
+#define lpfc_rcqe_port_WORD            word3
+#define lpfc_rcqe_hdr_length_SHIFT     24
+#define lpfc_rcqe_hdr_length_MASK      0x0000001F
+#define lpfc_rcqe_hdr_length_WORD      word3
+#define lpfc_rcqe_code_SHIFT           lpfc_cqe_code_SHIFT
+#define lpfc_rcqe_code_MASK            lpfc_cqe_code_MASK
+#define lpfc_rcqe_code_WORD            lpfc_cqe_code_WORD
+#define lpfc_rcqe_eof_SHIFT            8
+#define lpfc_rcqe_eof_MASK             0x000000FF
+#define lpfc_rcqe_eof_WORD             word3
+#define FCOE_EOFn      0x41
+#define FCOE_EOFt      0x42
+#define FCOE_EOFni     0x49
+#define FCOE_EOFa      0x50
+#define lpfc_rcqe_sof_SHIFT            0
+#define lpfc_rcqe_sof_MASK             0x000000FF
+#define lpfc_rcqe_sof_WORD             word3
+#define FCOE_SOFi2     0x2d
+#define FCOE_SOFi3     0x2e
+#define FCOE_SOFn2     0x35
+#define FCOE_SOFn3     0x36
+};
+
+struct lpfc_wqe_generic{
+       struct ulp_bde64 bde;
+       uint32_t word3;
+       uint32_t word4;
+       uint32_t word5;
+       uint32_t word6;
+#define lpfc_wqe_gen_context_SHIFT     16
+#define lpfc_wqe_gen_context_MASK      0x0000FFFF
+#define lpfc_wqe_gen_context_WORD      word6
+#define lpfc_wqe_gen_xri_SHIFT         0
+#define lpfc_wqe_gen_xri_MASK          0x0000FFFF
+#define lpfc_wqe_gen_xri_WORD          word6
+       uint32_t word7;
+#define lpfc_wqe_gen_lnk_SHIFT         23
+#define lpfc_wqe_gen_lnk_MASK          0x00000001
+#define lpfc_wqe_gen_lnk_WORD          word7
+#define lpfc_wqe_gen_erp_SHIFT         22
+#define lpfc_wqe_gen_erp_MASK          0x00000001
+#define lpfc_wqe_gen_erp_WORD          word7
+#define lpfc_wqe_gen_pu_SHIFT          20
+#define lpfc_wqe_gen_pu_MASK           0x00000003
+#define lpfc_wqe_gen_pu_WORD           word7
+#define lpfc_wqe_gen_class_SHIFT       16
+#define lpfc_wqe_gen_class_MASK                0x00000007
+#define lpfc_wqe_gen_class_WORD                word7
+#define lpfc_wqe_gen_command_SHIFT     8
+#define lpfc_wqe_gen_command_MASK      0x000000FF
+#define lpfc_wqe_gen_command_WORD      word7
+#define lpfc_wqe_gen_status_SHIFT      4
+#define lpfc_wqe_gen_status_MASK       0x0000000F
+#define lpfc_wqe_gen_status_WORD       word7
+#define lpfc_wqe_gen_ct_SHIFT          2
+#define lpfc_wqe_gen_ct_MASK           0x00000007
+#define lpfc_wqe_gen_ct_WORD           word7
+       uint32_t abort_tag;
+       uint32_t word9;
+#define lpfc_wqe_gen_request_tag_SHIFT 0
+#define lpfc_wqe_gen_request_tag_MASK  0x0000FFFF
+#define lpfc_wqe_gen_request_tag_WORD  word9
+       uint32_t word10;
+#define lpfc_wqe_gen_ccp_SHIFT         24
+#define lpfc_wqe_gen_ccp_MASK          0x000000FF
+#define lpfc_wqe_gen_ccp_WORD          word10
+#define lpfc_wqe_gen_ccpe_SHIFT                23
+#define lpfc_wqe_gen_ccpe_MASK         0x00000001
+#define lpfc_wqe_gen_ccpe_WORD         word10
+#define lpfc_wqe_gen_pv_SHIFT          19
+#define lpfc_wqe_gen_pv_MASK           0x00000001
+#define lpfc_wqe_gen_pv_WORD           word10
+#define lpfc_wqe_gen_pri_SHIFT         16
+#define lpfc_wqe_gen_pri_MASK          0x00000007
+#define lpfc_wqe_gen_pri_WORD          word10
+       uint32_t word11;
+#define lpfc_wqe_gen_cq_id_SHIFT       16
+#define lpfc_wqe_gen_cq_id_MASK                0x000003FF
+#define lpfc_wqe_gen_cq_id_WORD                word11
+#define LPFC_WQE_CQ_ID_DEFAULT 0x3ff
+#define lpfc_wqe_gen_wqec_SHIFT                7
+#define lpfc_wqe_gen_wqec_MASK         0x00000001
+#define lpfc_wqe_gen_wqec_WORD         word11
+#define lpfc_wqe_gen_cmd_type_SHIFT    0
+#define lpfc_wqe_gen_cmd_type_MASK     0x0000000F
+#define lpfc_wqe_gen_cmd_type_WORD     word11
+       uint32_t payload[4];
+};
+
+struct lpfc_rqe {
+       uint32_t address_hi;
+       uint32_t address_lo;
+};
+
+/* buffer descriptors */
+struct lpfc_bde4 {
+       uint32_t addr_hi;
+       uint32_t addr_lo;
+       uint32_t word2;
+#define lpfc_bde4_last_SHIFT           31
+#define lpfc_bde4_last_MASK            0x00000001
+#define lpfc_bde4_last_WORD            word2
+#define lpfc_bde4_sge_offset_SHIFT     0
+#define lpfc_bde4_sge_offset_MASK      0x000003FF
+#define lpfc_bde4_sge_offset_WORD      word2
+       uint32_t word3;
+#define lpfc_bde4_length_SHIFT         0
+#define lpfc_bde4_length_MASK          0x000000FF
+#define lpfc_bde4_length_WORD          word3
+};
+
+struct lpfc_register {
+       uint32_t word0;
+};
+
+#define LPFC_UERR_STATUS_HI            0x00A4
+#define LPFC_UERR_STATUS_LO            0x00A0
+#define LPFC_ONLINE0                   0x00B0
+#define LPFC_ONLINE1                   0x00B4
+#define LPFC_SCRATCHPAD                        0x0058
+
+/* BAR0 Registers */
+#define LPFC_HST_STATE                 0x00AC
+#define lpfc_hst_state_perr_SHIFT      31
+#define lpfc_hst_state_perr_MASK       0x1
+#define lpfc_hst_state_perr_WORD       word0
+#define lpfc_hst_state_sfi_SHIFT       30
+#define lpfc_hst_state_sfi_MASK                0x1
+#define lpfc_hst_state_sfi_WORD                word0
+#define lpfc_hst_state_nip_SHIFT       29
+#define lpfc_hst_state_nip_MASK                0x1
+#define lpfc_hst_state_nip_WORD                word0
+#define lpfc_hst_state_ipc_SHIFT       28
+#define lpfc_hst_state_ipc_MASK                0x1
+#define lpfc_hst_state_ipc_WORD                word0
+#define lpfc_hst_state_xrom_SHIFT      27
+#define lpfc_hst_state_xrom_MASK       0x1
+#define lpfc_hst_state_xrom_WORD       word0
+#define lpfc_hst_state_dl_SHIFT                26
+#define lpfc_hst_state_dl_MASK         0x1
+#define lpfc_hst_state_dl_WORD         word0
+#define lpfc_hst_state_port_status_SHIFT       0
+#define lpfc_hst_state_port_status_MASK                0xFFFF
+#define lpfc_hst_state_port_status_WORD                word0
+
+#define LPFC_POST_STAGE_POWER_ON_RESET                 0x0000
+#define LPFC_POST_STAGE_AWAITING_HOST_RDY              0x0001
+#define LPFC_POST_STAGE_HOST_RDY                       0x0002
+#define LPFC_POST_STAGE_BE_RESET                       0x0003
+#define LPFC_POST_STAGE_SEEPROM_CS_START               0x0100
+#define LPFC_POST_STAGE_SEEPROM_CS_DONE                        0x0101
+#define LPFC_POST_STAGE_DDR_CONFIG_START               0x0200
+#define LPFC_POST_STAGE_DDR_CONFIG_DONE                        0x0201
+#define LPFC_POST_STAGE_DDR_CALIBRATE_START            0x0300
+#define LPFC_POST_STAGE_DDR_CALIBRATE_DONE             0x0301
+#define LPFC_POST_STAGE_DDR_TEST_START                 0x0400
+#define LPFC_POST_STAGE_DDR_TEST_DONE                  0x0401
+#define LPFC_POST_STAGE_REDBOOT_INIT_START             0x0600
+#define LPFC_POST_STAGE_REDBOOT_INIT_DONE              0x0601
+#define LPFC_POST_STAGE_FW_IMAGE_LOAD_START            0x0700
+#define LPFC_POST_STAGE_FW_IMAGE_LOAD_DONE             0x0701
+#define LPFC_POST_STAGE_ARMFW_START                    0x0800
+#define LPFC_POST_STAGE_DHCP_QUERY_START               0x0900
+#define LPFC_POST_STAGE_DHCP_QUERY_DONE                        0x0901
+#define LPFC_POST_STAGE_BOOT_TARGET_DISCOVERY_START    0x0A00
+#define LPFC_POST_STAGE_BOOT_TARGET_DISCOVERY_DONE     0x0A01
+#define LPFC_POST_STAGE_RC_OPTION_SET                  0x0B00
+#define LPFC_POST_STAGE_SWITCH_LINK                    0x0B01
+#define LPFC_POST_STAGE_SEND_ICDS_MESSAGE              0x0B02
+#define LPFC_POST_STAGE_PERFROM_TFTP                   0x0B03
+#define LPFC_POST_STAGE_PARSE_XML                      0x0B04
+#define LPFC_POST_STAGE_DOWNLOAD_IMAGE                 0x0B05
+#define LPFC_POST_STAGE_FLASH_IMAGE                    0x0B06
+#define LPFC_POST_STAGE_RC_DONE                                0x0B07
+#define LPFC_POST_STAGE_REBOOT_SYSTEM                  0x0B08
+#define LPFC_POST_STAGE_MAC_ADDRESS                    0x0C00
+#define LPFC_POST_STAGE_ARMFW_READY                    0xC000
+#define LPFC_POST_STAGE_ARMFW_UE                       0xF000
+
+#define lpfc_scratchpad_slirev_SHIFT                   4
+#define lpfc_scratchpad_slirev_MASK                    0xF
+#define lpfc_scratchpad_slirev_WORD                    word0
+#define lpfc_scratchpad_chiptype_SHIFT                 8
+#define lpfc_scratchpad_chiptype_MASK                  0xFF
+#define lpfc_scratchpad_chiptype_WORD                  word0
+#define lpfc_scratchpad_featurelevel1_SHIFT            16
+#define lpfc_scratchpad_featurelevel1_MASK             0xFF
+#define lpfc_scratchpad_featurelevel1_WORD             word0
+#define lpfc_scratchpad_featurelevel2_SHIFT            24
+#define lpfc_scratchpad_featurelevel2_MASK             0xFF
+#define lpfc_scratchpad_featurelevel2_WORD             word0
+
+/* BAR1 Registers */
+#define LPFC_IMR_MASK_ALL      0xFFFFFFFF
+#define LPFC_ISCR_CLEAR_ALL    0xFFFFFFFF
+
+#define LPFC_HST_ISR0          0x0C18
+#define LPFC_HST_ISR1          0x0C1C
+#define LPFC_HST_ISR2          0x0C20
+#define LPFC_HST_ISR3          0x0C24
+#define LPFC_HST_ISR4          0x0C28
+
+#define LPFC_HST_IMR0          0x0C48
+#define LPFC_HST_IMR1          0x0C4C
+#define LPFC_HST_IMR2          0x0C50
+#define LPFC_HST_IMR3          0x0C54
+#define LPFC_HST_IMR4          0x0C58
+
+#define LPFC_HST_ISCR0         0x0C78
+#define LPFC_HST_ISCR1         0x0C7C
+#define LPFC_HST_ISCR2         0x0C80
+#define LPFC_HST_ISCR3         0x0C84
+#define LPFC_HST_ISCR4         0x0C88
+
+#define LPFC_SLI4_INTR0                        BIT0
+#define LPFC_SLI4_INTR1                        BIT1
+#define LPFC_SLI4_INTR2                        BIT2
+#define LPFC_SLI4_INTR3                        BIT3
+#define LPFC_SLI4_INTR4                        BIT4
+#define LPFC_SLI4_INTR5                        BIT5
+#define LPFC_SLI4_INTR6                        BIT6
+#define LPFC_SLI4_INTR7                        BIT7
+#define LPFC_SLI4_INTR8                        BIT8
+#define LPFC_SLI4_INTR9                        BIT9
+#define LPFC_SLI4_INTR10               BIT10
+#define LPFC_SLI4_INTR11               BIT11
+#define LPFC_SLI4_INTR12               BIT12
+#define LPFC_SLI4_INTR13               BIT13
+#define LPFC_SLI4_INTR14               BIT14
+#define LPFC_SLI4_INTR15               BIT15
+#define LPFC_SLI4_INTR16               BIT16
+#define LPFC_SLI4_INTR17               BIT17
+#define LPFC_SLI4_INTR18               BIT18
+#define LPFC_SLI4_INTR19               BIT19
+#define LPFC_SLI4_INTR20               BIT20
+#define LPFC_SLI4_INTR21               BIT21
+#define LPFC_SLI4_INTR22               BIT22
+#define LPFC_SLI4_INTR23               BIT23
+#define LPFC_SLI4_INTR24               BIT24
+#define LPFC_SLI4_INTR25               BIT25
+#define LPFC_SLI4_INTR26               BIT26
+#define LPFC_SLI4_INTR27               BIT27
+#define LPFC_SLI4_INTR28               BIT28
+#define LPFC_SLI4_INTR29               BIT29
+#define LPFC_SLI4_INTR30               BIT30
+#define LPFC_SLI4_INTR31               BIT31
+
+/* BAR2 Registers */
+#define LPFC_RQ_DOORBELL               0x00A0
+#define lpfc_rq_doorbell_num_posted_SHIFT      16
+#define lpfc_rq_doorbell_num_posted_MASK       0x3FFF
+#define lpfc_rq_doorbell_num_posted_WORD       word0
+#define LPFC_RQ_POST_BATCH             8       /* RQEs to post at one time */
+#define lpfc_rq_doorbell_id_SHIFT              0
+#define lpfc_rq_doorbell_id_MASK               0x03FF
+#define lpfc_rq_doorbell_id_WORD               word0
+
+#define LPFC_WQ_DOORBELL               0x0040
+#define lpfc_wq_doorbell_num_posted_SHIFT      24
+#define lpfc_wq_doorbell_num_posted_MASK       0x00FF
+#define lpfc_wq_doorbell_num_posted_WORD       word0
+#define lpfc_wq_doorbell_index_SHIFT           16
+#define lpfc_wq_doorbell_index_MASK            0x00FF
+#define lpfc_wq_doorbell_index_WORD            word0
+#define lpfc_wq_doorbell_id_SHIFT              0
+#define lpfc_wq_doorbell_id_MASK               0xFFFF
+#define lpfc_wq_doorbell_id_WORD               word0
+
+#define LPFC_EQCQ_DOORBELL             0x0120
+#define lpfc_eqcq_doorbell_arm_SHIFT           29
+#define lpfc_eqcq_doorbell_arm_MASK            0x0001
+#define lpfc_eqcq_doorbell_arm_WORD            word0
+#define lpfc_eqcq_doorbell_num_released_SHIFT  16
+#define lpfc_eqcq_doorbell_num_released_MASK   0x1FFF
+#define lpfc_eqcq_doorbell_num_released_WORD   word0
+#define lpfc_eqcq_doorbell_qt_SHIFT            10
+#define lpfc_eqcq_doorbell_qt_MASK             0x0001
+#define lpfc_eqcq_doorbell_qt_WORD             word0
+#define LPFC_QUEUE_TYPE_COMPLETION     0
+#define LPFC_QUEUE_TYPE_EVENT          1
+#define lpfc_eqcq_doorbell_eqci_SHIFT          9
+#define lpfc_eqcq_doorbell_eqci_MASK           0x0001
+#define lpfc_eqcq_doorbell_eqci_WORD           word0
+#define lpfc_eqcq_doorbell_cqid_SHIFT          0
+#define lpfc_eqcq_doorbell_cqid_MASK           0x03FF
+#define lpfc_eqcq_doorbell_cqid_WORD           word0
+#define lpfc_eqcq_doorbell_eqid_SHIFT          0
+#define lpfc_eqcq_doorbell_eqid_MASK           0x01FF
+#define lpfc_eqcq_doorbell_eqid_WORD           word0
+
+#define LPFC_BMBX                      0x0160
+#define lpfc_bmbx_addr_SHIFT           2
+#define lpfc_bmbx_addr_MASK            0x3FFFFFFF
+#define lpfc_bmbx_addr_WORD            word0
+#define lpfc_bmbx_hi_SHIFT             1
+#define lpfc_bmbx_hi_MASK              0x0001
+#define lpfc_bmbx_hi_WORD              word0
+#define lpfc_bmbx_rdy_SHIFT            0
+#define lpfc_bmbx_rdy_MASK             0x0001
+#define lpfc_bmbx_rdy_WORD             word0
+
+#define LPFC_MQ_DOORBELL                       0x0140
+#define lpfc_mq_doorbell_num_posted_SHIFT      16
+#define lpfc_mq_doorbell_num_posted_MASK       0x3FFF
+#define lpfc_mq_doorbell_num_posted_WORD       word0
+#define lpfc_mq_doorbell_id_SHIFT              0
+#define lpfc_mq_doorbell_id_MASK               0x03FF
+#define lpfc_mq_doorbell_id_WORD               word0
+
+struct lpfc_sli4_cfg_mhdr {
+       uint32_t word1;
+#define lpfc_mbox_hdr_emb_SHIFT                0
+#define lpfc_mbox_hdr_emb_MASK         0x00000001
+#define lpfc_mbox_hdr_emb_WORD         word1
+#define lpfc_mbox_hdr_sge_cnt_SHIFT    3
+#define lpfc_mbox_hdr_sge_cnt_MASK     0x0000001F
+#define lpfc_mbox_hdr_sge_cnt_WORD     word1
+       uint32_t payload_length;
+       uint32_t tag_lo;
+       uint32_t tag_hi;
+       uint32_t reserved5;
+};
+
+union lpfc_sli4_cfg_shdr {
+       struct {
+               uint32_t word6;
+#define lpfc_mbox_hdr_opcode_SHIFT             0
+#define lpfc_mbox_hdr_opcode_MASK              0x000000FF
+#define lpfc_mbox_hdr_opcode_WORD              word6
+#define lpfc_mbox_hdr_subsystem_SHIFT          8
+#define lpfc_mbox_hdr_subsystem_MASK           0x000000FF
+#define lpfc_mbox_hdr_subsystem_WORD           word6
+#define lpfc_mbox_hdr_port_number_SHIFT                16
+#define lpfc_mbox_hdr_port_number_MASK         0x000000FF
+#define lpfc_mbox_hdr_port_number_WORD         word6
+#define lpfc_mbox_hdr_domain_SHIFT             24
+#define lpfc_mbox_hdr_domain_MASK              0x000000FF
+#define lpfc_mbox_hdr_domain_WORD              word6
+               uint32_t timeout;
+               uint32_t request_length;
+               uint32_t reserved9;
+       } request;
+       struct {
+               uint32_t word6;
+#define lpfc_mbox_hdr_opcode_SHIFT             0
+#define lpfc_mbox_hdr_opcode_MASK              0x000000FF
+#define lpfc_mbox_hdr_opcode_WORD              word6
+#define lpfc_mbox_hdr_subsystem_SHIFT          8
+#define lpfc_mbox_hdr_subsystem_MASK           0x000000FF
+#define lpfc_mbox_hdr_subsystem_WORD           word6
+#define lpfc_mbox_hdr_domain_SHIFT             24
+#define lpfc_mbox_hdr_domain_MASK              0x000000FF
+#define lpfc_mbox_hdr_domain_WORD              word6
+               uint32_t word7;
+#define lpfc_mbox_hdr_status_SHIFT             0
+#define lpfc_mbox_hdr_status_MASK              0x000000FF
+#define lpfc_mbox_hdr_status_WORD              word7
+#define lpfc_mbox_hdr_add_status_SHIFT         8
+#define lpfc_mbox_hdr_add_status_MASK          0x000000FF
+#define lpfc_mbox_hdr_add_status_WORD          word7
+               uint32_t response_length;
+               uint32_t actual_response_length;
+       } response;
+};
+
+/* Mailbox structures */
+struct mbox_header {
+       struct lpfc_sli4_cfg_mhdr cfg_mhdr;
+       union  lpfc_sli4_cfg_shdr cfg_shdr;
+};
+
+/* Subsystem Definitions */
+#define LPFC_MBOX_SUBSYSTEM_COMMON     0x1
+#define LPFC_MBOX_SUBSYSTEM_FCOE       0xC
+
+/* Device Specific Definitions */
+
+/* The HOST ENDIAN defines are in Big Endian format. */
+#define HOST_ENDIAN_LOW_WORD0   0xFF3412FF
+#define HOST_ENDIAN_HIGH_WORD1 0xFF7856FF
+
+/* Common Opcodes */
+#define LPFC_MBOX_OPCODE_CQ_CREATE             0x0C
+#define LPFC_MBOX_OPCODE_EQ_CREATE             0x0D
+#define LPFC_MBOX_OPCODE_MQ_CREATE             0x15
+#define LPFC_MBOX_OPCODE_GET_CNTL_ATTRIBUTES   0x20
+#define LPFC_MBOX_OPCODE_NOP                   0x21
+#define LPFC_MBOX_OPCODE_MQ_DESTROY            0x35
+#define LPFC_MBOX_OPCODE_CQ_DESTROY            0x36
+#define LPFC_MBOX_OPCODE_EQ_DESTROY            0x37
+#define LPFC_MBOX_OPCODE_FUNCTION_RESET                0x3D
+
+/* FCoE Opcodes */
+#define LPFC_MBOX_OPCODE_FCOE_WQ_CREATE                        0x01
+#define LPFC_MBOX_OPCODE_FCOE_WQ_DESTROY               0x02
+#define LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES           0x03
+#define LPFC_MBOX_OPCODE_FCOE_REMOVE_SGL_PAGES         0x04
+#define LPFC_MBOX_OPCODE_FCOE_RQ_CREATE                        0x05
+#define LPFC_MBOX_OPCODE_FCOE_RQ_DESTROY               0x06
+#define LPFC_MBOX_OPCODE_FCOE_READ_FCF_TABLE           0x08
+#define LPFC_MBOX_OPCODE_FCOE_ADD_FCF                  0x09
+#define LPFC_MBOX_OPCODE_FCOE_DELETE_FCF               0x0A
+#define LPFC_MBOX_OPCODE_FCOE_POST_HDR_TEMPLATE                0x0B
+
+/* Mailbox command structures */
+struct eq_context {
+       uint32_t word0;
+#define lpfc_eq_context_size_SHIFT     31
+#define lpfc_eq_context_size_MASK      0x00000001
+#define lpfc_eq_context_size_WORD      word0
+#define LPFC_EQE_SIZE_4                        0x0
+#define LPFC_EQE_SIZE_16               0x1
+#define lpfc_eq_context_valid_SHIFT    29
+#define lpfc_eq_context_valid_MASK     0x00000001
+#define lpfc_eq_context_valid_WORD     word0
+       uint32_t word1;
+#define lpfc_eq_context_count_SHIFT    26
+#define lpfc_eq_context_count_MASK     0x00000003
+#define lpfc_eq_context_count_WORD     word1
+#define LPFC_EQ_CNT_256                0x0
+#define LPFC_EQ_CNT_512                0x1
+#define LPFC_EQ_CNT_1024       0x2
+#define LPFC_EQ_CNT_2048       0x3
+#define LPFC_EQ_CNT_4096       0x4
+       uint32_t word2;
+#define lpfc_eq_context_delay_multi_SHIFT      13
+#define lpfc_eq_context_delay_multi_MASK       0x000003FF
+#define lpfc_eq_context_delay_multi_WORD       word2
+       uint32_t reserved3;
+};
+
+struct sgl_page_pairs {
+       uint32_t sgl_pg0_addr_lo;
+       uint32_t sgl_pg0_addr_hi;
+       uint32_t sgl_pg1_addr_lo;
+       uint32_t sgl_pg1_addr_hi;
+};
+
+struct lpfc_mbx_post_sgl_pages {
+       struct mbox_header header;
+       uint32_t word0;
+#define lpfc_post_sgl_pages_xri_SHIFT  0
+#define lpfc_post_sgl_pages_xri_MASK   0x0000FFFF
+#define lpfc_post_sgl_pages_xri_WORD   word0
+#define lpfc_post_sgl_pages_xricnt_SHIFT       16
+#define lpfc_post_sgl_pages_xricnt_MASK        0x0000FFFF
+#define lpfc_post_sgl_pages_xricnt_WORD        word0
+       struct sgl_page_pairs  sgl_pg_pairs[1];
+};
+
+/* word0 of page-1 struct shares the same SHIFT/MASK/WORD defines as above */
+struct lpfc_mbx_post_uembed_sgl_page1 {
+       union  lpfc_sli4_cfg_shdr cfg_shdr;
+       uint32_t word0;
+       struct sgl_page_pairs sgl_pg_pairs;
+};
+
+struct lpfc_mbx_sge {
+       uint32_t pa_lo;
+       uint32_t pa_hi;
+       uint32_t length;
+};
+
+struct lpfc_mbx_nembed_cmd {
+       struct lpfc_sli4_cfg_mhdr cfg_mhdr;
+#define LPFC_SLI4_MBX_SGE_MAX_PAGES    19
+       struct lpfc_mbx_sge sge[LPFC_SLI4_MBX_SGE_MAX_PAGES];
+};
+
+struct lpfc_mbx_nembed_sge_virt {
+       void *addr[LPFC_SLI4_MBX_SGE_MAX_PAGES];
+};
+
+struct lpfc_mbx_eq_create {
+       struct mbox_header header;
+       union {
+               struct {
+                       uint32_t word0;
+#define lpfc_mbx_eq_create_num_pages_SHIFT     0
+#define lpfc_mbx_eq_create_num_pages_MASK      0x0000FFFF
+#define lpfc_mbx_eq_create_num_pages_WORD      word0
+                       struct eq_context context;
+                       struct dma_address page[LPFC_MAX_EQ_PAGE];
+               } request;
+               struct {
+                       uint32_t word0;
+#define lpfc_mbx_eq_create_q_id_SHIFT  0
+#define lpfc_mbx_eq_create_q_id_MASK   0x0000FFFF
+#define lpfc_mbx_eq_create_q_id_WORD   word0
+               } response;
+       } u;
+};
+
+struct lpfc_mbx_eq_destroy {
+       struct mbox_header header;
+       union {
+               struct {
+                       uint32_t word0;
+#define lpfc_mbx_eq_destroy_q_id_SHIFT 0
+#define lpfc_mbx_eq_destroy_q_id_MASK  0x0000FFFF
+#define lpfc_mbx_eq_destroy_q_id_WORD  word0
+               } request;
+               struct {
+                       uint32_t word0;
+               } response;
+       } u;
+};
+
+struct lpfc_mbx_nop {
+       struct mbox_header header;
+       uint32_t context[2];
+};
+
+struct cq_context {
+       uint32_t word0;
+#define lpfc_cq_context_event_SHIFT    31
+#define lpfc_cq_context_event_MASK     0x00000001
+#define lpfc_cq_context_event_WORD     word0
+#define lpfc_cq_context_valid_SHIFT    29
+#define lpfc_cq_context_valid_MASK     0x00000001
+#define lpfc_cq_context_valid_WORD     word0
+#define lpfc_cq_context_count_SHIFT    27
+#define lpfc_cq_context_count_MASK     0x00000003
+#define lpfc_cq_context_count_WORD     word0
+#define LPFC_CQ_CNT_256                0x0
+#define LPFC_CQ_CNT_512                0x1
+#define LPFC_CQ_CNT_1024       0x2
+       uint32_t word1;
+#define lpfc_cq_eq_id_SHIFT            22
+#define lpfc_cq_eq_id_MASK             0x000000FF
+#define lpfc_cq_eq_id_WORD             word1
+       uint32_t reserved0;
+       uint32_t reserved1;
+};
+
+struct lpfc_mbx_cq_create {
+       struct mbox_header header;
+       union {
+               struct {
+                       uint32_t word0;
+#define lpfc_mbx_cq_create_num_pages_SHIFT     0
+#define lpfc_mbx_cq_create_num_pages_MASK      0x0000FFFF
+#define lpfc_mbx_cq_create_num_pages_WORD      word0
+                       struct cq_context context;
+                       struct dma_address page[LPFC_MAX_CQ_PAGE];
+               } request;
+               struct {
+                       uint32_t word0;
+#define lpfc_mbx_cq_create_q_id_SHIFT  0
+#define lpfc_mbx_cq_create_q_id_MASK   0x0000FFFF
+#define lpfc_mbx_cq_create_q_id_WORD   word0
+               } response;
+       } u;
+};
+
+struct lpfc_mbx_cq_destroy {
+       struct mbox_header header;
+       union {
+               struct {
+                       uint32_t word0;
+#define lpfc_mbx_cq_destroy_q_id_SHIFT 0
+#define lpfc_mbx_cq_destroy_q_id_MASK  0x0000FFFF
+#define lpfc_mbx_cq_destroy_q_id_WORD  word0
+               } request;
+               struct {
+                       uint32_t word0;
+               } response;
+       } u;
+};
+
+struct wq_context {
+       uint32_t reserved0;
+       uint32_t reserved1;
+       uint32_t reserved2;
+       uint32_t reserved3;
+};
+
+struct lpfc_mbx_wq_create {
+       struct mbox_header header;
+       union {
+               struct {
+                       uint32_t word0;
+#define lpfc_mbx_wq_create_num_pages_SHIFT     0
+#define lpfc_mbx_wq_create_num_pages_MASK      0x0000FFFF
+#define lpfc_mbx_wq_create_num_pages_WORD      word0
+#define lpfc_mbx_wq_create_cq_id_SHIFT         16
+#define lpfc_mbx_wq_create_cq_id_MASK          0x0000FFFF
+#define lpfc_mbx_wq_create_cq_id_WORD          word0
+                       struct dma_address page[LPFC_MAX_WQ_PAGE];
+               } request;
+               struct {
+                       uint32_t word0;
+#define lpfc_mbx_wq_create_q_id_SHIFT  0
+#define lpfc_mbx_wq_create_q_id_MASK   0x0000FFFF
+#define lpfc_mbx_wq_create_q_id_WORD   word0
+               } response;
+       } u;
+};
+
+struct lpfc_mbx_wq_destroy {
+       struct mbox_header header;
+       union {
+               struct {
+                       uint32_t word0;
+#define lpfc_mbx_wq_destroy_q_id_SHIFT 0
+#define lpfc_mbx_wq_destroy_q_id_MASK  0x0000FFFF
+#define lpfc_mbx_wq_destroy_q_id_WORD  word0
+               } request;
+               struct {
+                       uint32_t word0;
+               } response;
+       } u;
+};
+
+#define LPFC_HDR_BUF_SIZE 128
+#define LPFC_DATA_BUF_SIZE 4096
+struct rq_context {
+       uint32_t word0;
+#define lpfc_rq_context_rq_size_SHIFT  16
+#define lpfc_rq_context_rq_size_MASK   0x0000000F
+#define lpfc_rq_context_rq_size_WORD   word0
+#define LPFC_RQ_RING_SIZE_512          9       /* 512 entries */
+#define LPFC_RQ_RING_SIZE_1024         10      /* 1024 entries */
+#define LPFC_RQ_RING_SIZE_2048         11      /* 2048 entries */
+#define LPFC_RQ_RING_SIZE_4096         12      /* 4096 entries */
+       uint32_t reserved1;
+       uint32_t word2;
+#define lpfc_rq_context_cq_id_SHIFT    16
+#define lpfc_rq_context_cq_id_MASK     0x000003FF
+#define lpfc_rq_context_cq_id_WORD     word2
+#define lpfc_rq_context_buf_size_SHIFT 0
+#define lpfc_rq_context_buf_size_MASK  0x0000FFFF
+#define lpfc_rq_context_buf_size_WORD  word2
+       uint32_t reserved3;
+};
+
+struct lpfc_mbx_rq_create {
+       struct mbox_header header;
+       union {
+               struct {
+                       uint32_t word0;
+#define lpfc_mbx_rq_create_num_pages_SHIFT     0
+#define lpfc_mbx_rq_create_num_pages_MASK      0x0000FFFF
+#define lpfc_mbx_rq_create_num_pages_WORD      word0
+                       struct rq_context context;
+                       struct dma_address page[LPFC_MAX_WQ_PAGE];
+               } request;
+               struct {
+                       uint32_t word0;
+#define lpfc_mbx_rq_create_q_id_SHIFT  0
+#define lpfc_mbx_rq_create_q_id_MASK   0x0000FFFF
+#define lpfc_mbx_rq_create_q_id_WORD   word0
+               } response;
+       } u;
+};
+
+struct lpfc_mbx_rq_destroy {
+       struct mbox_header header;
+       union {
+               struct {
+                       uint32_t word0;
+#define lpfc_mbx_rq_destroy_q_id_SHIFT 0
+#define lpfc_mbx_rq_destroy_q_id_MASK  0x0000FFFF
+#define lpfc_mbx_rq_destroy_q_id_WORD  word0
+               } request;
+               struct {
+                       uint32_t word0;
+               } response;
+       } u;
+};
+
+struct mq_context {
+       uint32_t word0;
+#define lpfc_mq_context_cq_id_SHIFT    22
+#define lpfc_mq_context_cq_id_MASK     0x000003FF
+#define lpfc_mq_context_cq_id_WORD     word0
+#define lpfc_mq_context_count_SHIFT    16
+#define lpfc_mq_context_count_MASK     0x0000000F
+#define lpfc_mq_context_count_WORD     word0
+#define LPFC_MQ_CNT_16         0x5
+#define LPFC_MQ_CNT_32         0x6
+#define LPFC_MQ_CNT_64         0x7
+#define LPFC_MQ_CNT_128                0x8
+       uint32_t word1;
+#define lpfc_mq_context_valid_SHIFT    31
+#define lpfc_mq_context_valid_MASK     0x00000001
+#define lpfc_mq_context_valid_WORD     word1
+       uint32_t reserved2;
+       uint32_t reserved3;
+};
+
+struct lpfc_mbx_mq_create {
+       struct mbox_header header;
+       union {
+               struct {
+                       uint32_t word0;
+#define lpfc_mbx_mq_create_num_pages_SHIFT     0
+#define lpfc_mbx_mq_create_num_pages_MASK      0x0000FFFF
+#define lpfc_mbx_mq_create_num_pages_WORD      word0
+                       struct mq_context context;
+                       struct dma_address page[LPFC_MAX_MQ_PAGE];
+               } request;
+               struct {
+                       uint32_t word0;
+#define lpfc_mbx_mq_create_q_id_SHIFT  0
+#define lpfc_mbx_mq_create_q_id_MASK   0x0000FFFF
+#define lpfc_mbx_mq_create_q_id_WORD   word0
+               } response;
+       } u;
+};
+
+struct lpfc_mbx_mq_destroy {
+       struct mbox_header header;
+       union {
+               struct {
+                       uint32_t word0;
+#define lpfc_mbx_mq_destroy_q_id_SHIFT 0
+#define lpfc_mbx_mq_destroy_q_id_MASK  0x0000FFFF
+#define lpfc_mbx_mq_destroy_q_id_WORD  word0
+               } request;
+               struct {
+                       uint32_t word0;
+               } response;
+       } u;
+};
+
+struct lpfc_mbx_post_hdr_tmpl {
+       struct mbox_header header;
+       uint32_t word10;
+#define lpfc_mbx_post_hdr_tmpl_rpi_offset_SHIFT  0
+#define lpfc_mbx_post_hdr_tmpl_rpi_offset_MASK   0x0000FFFF
+#define lpfc_mbx_post_hdr_tmpl_rpi_offset_WORD   word10
+#define lpfc_mbx_post_hdr_tmpl_page_cnt_SHIFT   16
+#define lpfc_mbx_post_hdr_tmpl_page_cnt_MASK    0x0000FFFF
+#define lpfc_mbx_post_hdr_tmpl_page_cnt_WORD    word10
+       uint32_t rpi_paddr_lo;
+       uint32_t rpi_paddr_hi;
+};
+
+struct sli4_sge {      /* SLI-4 */
+       uint32_t addr_hi;
+       uint32_t addr_lo;
+
+       uint32_t word2;
+#define lpfc_sli4_sge_offset_SHIFT     0 /* Offset of buffer - Not used*/
+#define lpfc_sli4_sge_offset_MASK      0x00FFFFFF
+#define lpfc_sli4_sge_offset_WORD      word2
+#define lpfc_sli4_sge_last_SHIFT       31 /* Last SEG in the SGL sets
+                                               this  flag !! */
+#define lpfc_sli4_sge_last_MASK                0x00000001
+#define lpfc_sli4_sge_last_WORD                word2
+       uint32_t word3;
+#define lpfc_sli4_sge_len_SHIFT                0
+#define lpfc_sli4_sge_len_MASK         0x0001FFFF
+#define lpfc_sli4_sge_len_WORD         word3
+};
+
+struct fcf_record {
+       uint32_t max_rcv_size;
+       uint32_t fka_adv_period;
+       uint32_t fip_priority;
+       uint32_t word3;
+#define lpfc_fcf_record_mac_0_SHIFT            0
+#define lpfc_fcf_record_mac_0_MASK             0x000000FF
+#define lpfc_fcf_record_mac_0_WORD             word3
+#define lpfc_fcf_record_mac_1_SHIFT            8
+#define lpfc_fcf_record_mac_1_MASK             0x000000FF
+#define lpfc_fcf_record_mac_1_WORD             word3
+#define lpfc_fcf_record_mac_2_SHIFT            16
+#define lpfc_fcf_record_mac_2_MASK             0x000000FF
+#define lpfc_fcf_record_mac_2_WORD             word3
+#define lpfc_fcf_record_mac_3_SHIFT            24
+#define lpfc_fcf_record_mac_3_MASK             0x000000FF
+#define lpfc_fcf_record_mac_3_WORD             word3
+       uint32_t word4;
+#define lpfc_fcf_record_mac_4_SHIFT            0
+#define lpfc_fcf_record_mac_4_MASK             0x000000FF
+#define lpfc_fcf_record_mac_4_WORD             word4
+#define lpfc_fcf_record_mac_5_SHIFT            8
+#define lpfc_fcf_record_mac_5_MASK             0x000000FF
+#define lpfc_fcf_record_mac_5_WORD             word4
+#define lpfc_fcf_record_fcf_avail_SHIFT                16
+#define lpfc_fcf_record_fcf_avail_MASK         0x000000FF
+#define lpfc_fcf_record_fc_avail_WORD          word4
+#define lpfc_fcf_record_mac_addr_prov_SHIFT    24
+#define lpfc_fcf_record_mac_addr_prov_MASK     0x000000FF
+#define lpfc_fcf_record_mac_addr_prov_WORD     word4
+#define LPFC_FCF_FPMA           1      /* Fabric Provided MAC Address */
+#define LPFC_FCF_SPMA           2       /* Server Provided MAC Address */
+       uint32_t word5;
+#define lpfc_fcf_record_fab_name_0_SHIFT       0
+#define lpfc_fcf_record_fab_name_0_MASK                0x000000FF
+#define lpfc_fcf_record_fab_name_0_WORD                word5
+#define lpfc_fcf_record_fab_name_1_SHIFT       8
+#define lpfc_fcf_record_fab_name_1_MASK                0x000000FF
+#define lpfc_fcf_record_fab_name_1_WORD                word5
+#define lpfc_fcf_record_fab_name_2_SHIFT       16
+#define lpfc_fcf_record_fab_name_2_MASK                0x000000FF
+#define lpfc_fcf_record_fab_name_2_WORD                word5
+#define lpfc_fcf_record_fab_name_3_SHIFT       24
+#define lpfc_fcf_record_fab_name_3_MASK                0x000000FF
+#define lpfc_fcf_record_fab_name_3_WORD                word5
+       uint32_t word6;
+#define lpfc_fcf_record_fab_name_4_SHIFT       0
+#define lpfc_fcf_record_fab_name_4_MASK                0x000000FF
+#define lpfc_fcf_record_fab_name_4_WORD                word6
+#define lpfc_fcf_record_fab_name_5_SHIFT       8
+#define lpfc_fcf_record_fab_name_5_MASK                0x000000FF
+#define lpfc_fcf_record_fab_name_5_WORD                word6
+#define lpfc_fcf_record_fab_name_6_SHIFT       16
+#define lpfc_fcf_record_fab_name_6_MASK                0x000000FF
+#define lpfc_fcf_record_fab_name_6_WORD                word6
+#define lpfc_fcf_record_fab_name_7_SHIFT       24
+#define lpfc_fcf_record_fab_name_7_MASK                0x000000FF
+#define lpfc_fcf_record_fab_name_7_WORD                word6
+       uint32_t word7;
+#define lpfc_fcf_record_fc_map_0_SHIFT         0
+#define lpfc_fcf_record_fc_map_0_MASK          0x000000FF
+#define lpfc_fcf_record_fc_map_0_WORD          word7
+#define lpfc_fcf_record_fc_map_1_SHIFT         8
+#define lpfc_fcf_record_fc_map_1_MASK          0x000000FF
+#define lpfc_fcf_record_fc_map_1_WORD          word7
+#define lpfc_fcf_record_fc_map_2_SHIFT         16
+#define lpfc_fcf_record_fc_map_2_MASK          0x000000FF
+#define lpfc_fcf_record_fc_map_2_WORD          word7
+#define lpfc_fcf_record_fcf_valid_SHIFT                24
+#define lpfc_fcf_record_fcf_valid_MASK         0x000000FF
+#define lpfc_fcf_record_fcf_valid_WORD         word7
+       uint32_t word8;
+#define lpfc_fcf_record_fcf_index_SHIFT                0
+#define lpfc_fcf_record_fcf_index_MASK         0x0000FFFF
+#define lpfc_fcf_record_fcf_index_WORD         word8
+#define lpfc_fcf_record_fcf_state_SHIFT                16
+#define lpfc_fcf_record_fcf_state_MASK         0x0000FFFF
+#define lpfc_fcf_record_fcf_state_WORD         word8
+       uint8_t vlan_bitmap[512];
+};
+
+struct lpfc_mbx_read_fcf_tbl {
+       union lpfc_sli4_cfg_shdr cfg_shdr;
+       union {
+               struct {
+                       uint32_t word10;
+#define lpfc_mbx_read_fcf_tbl_indx_SHIFT       0
+#define lpfc_mbx_read_fcf_tbl_indx_MASK                0x0000FFFF
+#define lpfc_mbx_read_fcf_tbl_indx_WORD                word10
+               } request;
+               struct {
+                       uint32_t eventag;
+               } response;
+       } u;
+       uint32_t word11;
+#define lpfc_mbx_read_fcf_tbl_nxt_vindx_SHIFT  0
+#define lpfc_mbx_read_fcf_tbl_nxt_vindx_MASK   0x0000FFFF
+#define lpfc_mbx_read_fcf_tbl_nxt_vindx_WORD   word11
+};
+
+struct lpfc_mbx_add_fcf_tbl_entry {
+       union lpfc_sli4_cfg_shdr cfg_shdr;
+       uint32_t word10;
+#define lpfc_mbx_add_fcf_tbl_fcfi_SHIFT        0
+#define lpfc_mbx_add_fcf_tbl_fcfi_MASK         0x0000FFFF
+#define lpfc_mbx_add_fcf_tbl_fcfi_WORD         word10
+       struct lpfc_mbx_sge fcf_sge;
+};
+
+struct lpfc_mbx_del_fcf_tbl_entry {
+       struct mbox_header header;
+       uint32_t word10;
+#define lpfc_mbx_del_fcf_tbl_count_SHIFT       0
+#define lpfc_mbx_del_fcf_tbl_count_MASK                0x0000FFFF
+#define lpfc_mbx_del_fcf_tbl_count_WORD                word10
+#define lpfc_mbx_del_fcf_tbl_index_SHIFT       16
+#define lpfc_mbx_del_fcf_tbl_index_MASK                0x0000FFFF
+#define lpfc_mbx_del_fcf_tbl_index_WORD                word10
+};
+
+/* Status field for embedded SLI_CONFIG mailbox command */
+#define STATUS_SUCCESS                                 0x0
+#define STATUS_FAILED                                  0x1
+#define STATUS_ILLEGAL_REQUEST                         0x2
+#define STATUS_ILLEGAL_FIELD                           0x3
+#define STATUS_INSUFFICIENT_BUFFER                     0x4
+#define STATUS_UNAUTHORIZED_REQUEST                    0x5
+#define STATUS_FLASHROM_SAVE_FAILED                    0x17
+#define STATUS_FLASHROM_RESTORE_FAILED                 0x18
+#define STATUS_ICCBINDEX_ALLOC_FAILED                  0x1a
+#define STATUS_IOCTLHANDLE_ALLOC_FAILED                0x1b
+#define STATUS_INVALID_PHY_ADDR_FROM_OSM               0x1c
+#define STATUS_INVALID_PHY_ADDR_LEN_FROM_OSM           0x1d
+#define STATUS_ASSERT_FAILED                           0x1e
+#define STATUS_INVALID_SESSION                         0x1f
+#define STATUS_INVALID_CONNECTION                      0x20
+#define STATUS_BTL_PATH_EXCEEDS_OSM_LIMIT              0x21
+#define STATUS_BTL_NO_FREE_SLOT_PATH                   0x24
+#define STATUS_BTL_NO_FREE_SLOT_TGTID                  0x25
+#define STATUS_OSM_DEVSLOT_NOT_FOUND                   0x26
+#define STATUS_FLASHROM_READ_FAILED                    0x27
+#define STATUS_POLL_IOCTL_TIMEOUT                      0x28
+#define STATUS_ERROR_ACITMAIN                          0x2a
+#define STATUS_REBOOT_REQUIRED                         0x2c
+#define STATUS_FCF_IN_USE                              0x3a
+
+struct lpfc_mbx_sli4_config {
+       struct mbox_header header;
+};
+
+struct lpfc_mbx_init_vfi {
+       uint32_t word1;
+#define lpfc_init_vfi_vr_SHIFT         31
+#define lpfc_init_vfi_vr_MASK          0x00000001
+#define lpfc_init_vfi_vr_WORD          word1
+#define lpfc_init_vfi_vt_SHIFT         30
+#define lpfc_init_vfi_vt_MASK          0x00000001
+#define lpfc_init_vfi_vt_WORD          word1
+#define lpfc_init_vfi_vf_SHIFT         29
+#define lpfc_init_vfi_vf_MASK          0x00000001
+#define lpfc_init_vfi_vf_WORD          word1
+#define lpfc_init_vfi_vfi_SHIFT                0
+#define lpfc_init_vfi_vfi_MASK         0x0000FFFF
+#define lpfc_init_vfi_vfi_WORD         word1
+       uint32_t word2;
+#define lpfc_init_vfi_fcfi_SHIFT       0
+#define lpfc_init_vfi_fcfi_MASK                0x0000FFFF
+#define lpfc_init_vfi_fcfi_WORD                word2
+       uint32_t word3;
+#define lpfc_init_vfi_pri_SHIFT                13
+#define lpfc_init_vfi_pri_MASK         0x00000007
+#define lpfc_init_vfi_pri_WORD         word3
+#define lpfc_init_vfi_vf_id_SHIFT      1
+#define lpfc_init_vfi_vf_id_MASK       0x00000FFF
+#define lpfc_init_vfi_vf_id_WORD       word3
+       uint32_t word4;
+#define lpfc_init_vfi_hop_count_SHIFT  24
+#define lpfc_init_vfi_hop_count_MASK   0x000000FF
+#define lpfc_init_vfi_hop_count_WORD   word4
+};
+
+struct lpfc_mbx_reg_vfi {
+       uint32_t word1;
+#define lpfc_reg_vfi_vp_SHIFT          28
+#define lpfc_reg_vfi_vp_MASK           0x00000001
+#define lpfc_reg_vfi_vp_WORD           word1
+#define lpfc_reg_vfi_vfi_SHIFT         0
+#define lpfc_reg_vfi_vfi_MASK          0x0000FFFF
+#define lpfc_reg_vfi_vfi_WORD          word1
+       uint32_t word2;
+#define lpfc_reg_vfi_vpi_SHIFT         16
+#define lpfc_reg_vfi_vpi_MASK          0x0000FFFF
+#define lpfc_reg_vfi_vpi_WORD          word2
+#define lpfc_reg_vfi_fcfi_SHIFT                0
+#define lpfc_reg_vfi_fcfi_MASK         0x0000FFFF
+#define lpfc_reg_vfi_fcfi_WORD         word2
+       uint32_t word3_rsvd;
+       uint32_t word4_rsvd;
+       struct ulp_bde64 bde;
+       uint32_t word8_rsvd;
+       uint32_t word9_rsvd;
+       uint32_t word10;
+#define lpfc_reg_vfi_nport_id_SHIFT            0
+#define lpfc_reg_vfi_nport_id_MASK             0x00FFFFFF
+#define lpfc_reg_vfi_nport_id_WORD             word10
+};
+
+struct lpfc_mbx_init_vpi {
+       uint32_t word1;
+#define lpfc_init_vpi_vfi_SHIFT                16
+#define lpfc_init_vpi_vfi_MASK         0x0000FFFF
+#define lpfc_init_vpi_vfi_WORD         word1
+#define lpfc_init_vpi_vpi_SHIFT                0
+#define lpfc_init_vpi_vpi_MASK         0x0000FFFF
+#define lpfc_init_vpi_vpi_WORD         word1
+};
+
+struct lpfc_mbx_read_vpi {
+       uint32_t word1_rsvd;
+       uint32_t word2;
+#define lpfc_mbx_read_vpi_vnportid_SHIFT       0
+#define lpfc_mbx_read_vpi_vnportid_MASK                0x00FFFFFF
+#define lpfc_mbx_read_vpi_vnportid_WORD                word2
+       uint32_t word3_rsvd;
+       uint32_t word4;
+#define lpfc_mbx_read_vpi_acq_alpa_SHIFT       0
+#define lpfc_mbx_read_vpi_acq_alpa_MASK                0x000000FF
+#define lpfc_mbx_read_vpi_acq_alpa_WORD                word4
+#define lpfc_mbx_read_vpi_pb_SHIFT             15
+#define lpfc_mbx_read_vpi_pb_MASK              0x00000001
+#define lpfc_mbx_read_vpi_pb_WORD              word4
+#define lpfc_mbx_read_vpi_spec_alpa_SHIFT      16
+#define lpfc_mbx_read_vpi_spec_alpa_MASK       0x000000FF
+#define lpfc_mbx_read_vpi_spec_alpa_WORD       word4
+#define lpfc_mbx_read_vpi_ns_SHIFT             30
+#define lpfc_mbx_read_vpi_ns_MASK              0x00000001
+#define lpfc_mbx_read_vpi_ns_WORD              word4
+#define lpfc_mbx_read_vpi_hl_SHIFT             31
+#define lpfc_mbx_read_vpi_hl_MASK              0x00000001
+#define lpfc_mbx_read_vpi_hl_WORD              word4
+       uint32_t word5_rsvd;
+       uint32_t word6;
+#define lpfc_mbx_read_vpi_vpi_SHIFT            0
+#define lpfc_mbx_read_vpi_vpi_MASK             0x0000FFFF
+#define lpfc_mbx_read_vpi_vpi_WORD             word6
+       uint32_t word7;
+#define lpfc_mbx_read_vpi_mac_0_SHIFT          0
+#define lpfc_mbx_read_vpi_mac_0_MASK           0x000000FF
+#define lpfc_mbx_read_vpi_mac_0_WORD           word7
+#define lpfc_mbx_read_vpi_mac_1_SHIFT          8
+#define lpfc_mbx_read_vpi_mac_1_MASK           0x000000FF
+#define lpfc_mbx_read_vpi_mac_1_WORD           word7
+#define lpfc_mbx_read_vpi_mac_2_SHIFT          16
+#define lpfc_mbx_read_vpi_mac_2_MASK           0x000000FF
+#define lpfc_mbx_read_vpi_mac_2_WORD           word7
+#define lpfc_mbx_read_vpi_mac_3_SHIFT          24
+#define lpfc_mbx_read_vpi_mac_3_MASK           0x000000FF
+#define lpfc_mbx_read_vpi_mac_3_WORD           word7
+       uint32_t word8;
+#define lpfc_mbx_read_vpi_mac_4_SHIFT          0
+#define lpfc_mbx_read_vpi_mac_4_MASK           0x000000FF
+#define lpfc_mbx_read_vpi_mac_4_WORD           word8
+#define lpfc_mbx_read_vpi_mac_5_SHIFT          8
+#define lpfc_mbx_read_vpi_mac_5_MASK           0x000000FF
+#define lpfc_mbx_read_vpi_mac_5_WORD           word8
+#define lpfc_mbx_read_vpi_vlan_tag_SHIFT       16
+#define lpfc_mbx_read_vpi_vlan_tag_MASK                0x00000FFF
+#define lpfc_mbx_read_vpi_vlan_tag_WORD                word8
+#define lpfc_mbx_read_vpi_vv_SHIFT             28
+#define lpfc_mbx_read_vpi_vv_MASK              0x0000001
+#define lpfc_mbx_read_vpi_vv_WORD              word8
+};
+
+struct lpfc_mbx_unreg_vfi {
+       uint32_t word1_rsvd;
+       uint32_t word2;
+#define lpfc_unreg_vfi_vfi_SHIFT       0
+#define lpfc_unreg_vfi_vfi_MASK                0x0000FFFF
+#define lpfc_unreg_vfi_vfi_WORD                word2
+};
+
+struct lpfc_mbx_resume_rpi {
+       uint32_t word1;
+#define lpfc_resume_rpi_rpi_SHIFT      0
+#define lpfc_resume_rpi_rpi_MASK       0x0000FFFF
+#define lpfc_resume_rpi_rpi_WORD       word1
+       uint32_t event_tag;
+       uint32_t word3_rsvd;
+       uint32_t word4_rsvd;
+       uint32_t word5_rsvd;
+       uint32_t word6;
+#define lpfc_resume_rpi_vpi_SHIFT      0
+#define lpfc_resume_rpi_vpi_MASK       0x0000FFFF
+#define lpfc_resume_rpi_vpi_WORD       word6
+#define lpfc_resume_rpi_vfi_SHIFT      16
+#define lpfc_resume_rpi_vfi_MASK       0x0000FFFF
+#define lpfc_resume_rpi_vfi_WORD       word6
+};
+
+#define REG_FCF_INVALID_QID    0xFFFF
+struct lpfc_mbx_reg_fcfi {
+       uint32_t word1;
+#define lpfc_reg_fcfi_info_index_SHIFT 0
+#define lpfc_reg_fcfi_info_index_MASK  0x0000FFFF
+#define lpfc_reg_fcfi_info_index_WORD  word1
+#define lpfc_reg_fcfi_fcfi_SHIFT       16
+#define lpfc_reg_fcfi_fcfi_MASK                0x0000FFFF
+#define lpfc_reg_fcfi_fcfi_WORD                word1
+       uint32_t word2;
+#define lpfc_reg_fcfi_rq_id1_SHIFT     0
+#define lpfc_reg_fcfi_rq_id1_MASK      0x0000FFFF
+#define lpfc_reg_fcfi_rq_id1_WORD      word2
+#define lpfc_reg_fcfi_rq_id0_SHIFT     16
+#define lpfc_reg_fcfi_rq_id0_MASK      0x0000FFFF
+#define lpfc_reg_fcfi_rq_id0_WORD      word2
+       uint32_t word3;
+#define lpfc_reg_fcfi_rq_id3_SHIFT     0
+#define lpfc_reg_fcfi_rq_id3_MASK      0x0000FFFF
+#define lpfc_reg_fcfi_rq_id3_WORD      word3
+#define lpfc_reg_fcfi_rq_id2_SHIFT     16
+#define lpfc_reg_fcfi_rq_id2_MASK      0x0000FFFF
+#define lpfc_reg_fcfi_rq_id2_WORD      word3
+       uint32_t word4;
+#define lpfc_reg_fcfi_type_match0_SHIFT        24
+#define lpfc_reg_fcfi_type_match0_MASK 0x000000FF
+#define lpfc_reg_fcfi_type_match0_WORD word4
+#define lpfc_reg_fcfi_type_mask0_SHIFT 16
+#define lpfc_reg_fcfi_type_mask0_MASK  0x000000FF
+#define lpfc_reg_fcfi_type_mask0_WORD  word4
+#define lpfc_reg_fcfi_rctl_match0_SHIFT        8
+#define lpfc_reg_fcfi_rctl_match0_MASK 0x000000FF
+#define lpfc_reg_fcfi_rctl_match0_WORD word4
+#define lpfc_reg_fcfi_rctl_mask0_SHIFT 0
+#define lpfc_reg_fcfi_rctl_mask0_MASK  0x000000FF
+#define lpfc_reg_fcfi_rctl_mask0_WORD  word4
+       uint32_t word5;
+#define lpfc_reg_fcfi_type_match1_SHIFT        24
+#define lpfc_reg_fcfi_type_match1_MASK 0x000000FF
+#define lpfc_reg_fcfi_type_match1_WORD word5
+#define lpfc_reg_fcfi_type_mask1_SHIFT 16
+#define lpfc_reg_fcfi_type_mask1_MASK  0x000000FF
+#define lpfc_reg_fcfi_type_mask1_WORD  word5
+#define lpfc_reg_fcfi_rctl_match1_SHIFT        8
+#define lpfc_reg_fcfi_rctl_match1_MASK 0x000000FF
+#define lpfc_reg_fcfi_rctl_match1_WORD word5
+#define lpfc_reg_fcfi_rctl_mask1_SHIFT 0
+#define lpfc_reg_fcfi_rctl_mask1_MASK  0x000000FF
+#define lpfc_reg_fcfi_rctl_mask1_WORD  word5
+       uint32_t word6;
+#define lpfc_reg_fcfi_type_match2_SHIFT        24
+#define lpfc_reg_fcfi_type_match2_MASK 0x000000FF
+#define lpfc_reg_fcfi_type_match2_WORD word6
+#define lpfc_reg_fcfi_type_mask2_SHIFT 16
+#define lpfc_reg_fcfi_type_mask2_MASK  0x000000FF
+#define lpfc_reg_fcfi_type_mask2_WORD  word6
+#define lpfc_reg_fcfi_rctl_match2_SHIFT        8
+#define lpfc_reg_fcfi_rctl_match2_MASK 0x000000FF
+#define lpfc_reg_fcfi_rctl_match2_WORD word6
+#define lpfc_reg_fcfi_rctl_mask2_SHIFT 0
+#define lpfc_reg_fcfi_rctl_mask2_MASK  0x000000FF
+#define lpfc_reg_fcfi_rctl_mask2_WORD  word6
+       uint32_t word7;
+#define lpfc_reg_fcfi_type_match3_SHIFT        24
+#define lpfc_reg_fcfi_type_match3_MASK 0x000000FF
+#define lpfc_reg_fcfi_type_match3_WORD word7
+#define lpfc_reg_fcfi_type_mask3_SHIFT 16
+#define lpfc_reg_fcfi_type_mask3_MASK  0x000000FF
+#define lpfc_reg_fcfi_type_mask3_WORD  word7
+#define lpfc_reg_fcfi_rctl_match3_SHIFT        8
+#define lpfc_reg_fcfi_rctl_match3_MASK 0x000000FF
+#define lpfc_reg_fcfi_rctl_match3_WORD word7
+#define lpfc_reg_fcfi_rctl_mask3_SHIFT 0
+#define lpfc_reg_fcfi_rctl_mask3_MASK  0x000000FF
+#define lpfc_reg_fcfi_rctl_mask3_WORD  word7
+       uint32_t word8;
+#define lpfc_reg_fcfi_mam_SHIFT                13
+#define lpfc_reg_fcfi_mam_MASK         0x00000003
+#define lpfc_reg_fcfi_mam_WORD         word8
+#define LPFC_MAM_BOTH          0       /* Both SPMA and FPMA */
+#define LPFC_MAM_SPMA          1       /* Server Provided MAC Address */
+#define LPFC_MAM_FPMA          2       /* Fabric Provided MAC Address */
+#define lpfc_reg_fcfi_vv_SHIFT         12
+#define lpfc_reg_fcfi_vv_MASK          0x00000001
+#define lpfc_reg_fcfi_vv_WORD          word8
+#define lpfc_reg_fcfi_vlan_tag_SHIFT   0
+#define lpfc_reg_fcfi_vlan_tag_MASK    0x00000FFF
+#define lpfc_reg_fcfi_vlan_tag_WORD    word8
+};
+
+struct lpfc_mbx_unreg_fcfi {
+       uint32_t word1_rsv;
+       uint32_t word2;
+#define lpfc_unreg_fcfi_SHIFT          0
+#define lpfc_unreg_fcfi_MASK           0x0000FFFF
+#define lpfc_unreg_fcfi_WORD           word2
+};
+
+struct lpfc_mbx_read_rev {
+       uint32_t word1;
+#define lpfc_mbx_rd_rev_sli_lvl_SHIFT                  16
+#define lpfc_mbx_rd_rev_sli_lvl_MASK                   0x0000000F
+#define lpfc_mbx_rd_rev_sli_lvl_WORD                   word1
+#define lpfc_mbx_rd_rev_fcoe_SHIFT             20
+#define lpfc_mbx_rd_rev_fcoe_MASK              0x00000001
+#define lpfc_mbx_rd_rev_fcoe_WORD              word1
+#define lpfc_mbx_rd_rev_vpd_SHIFT              29
+#define lpfc_mbx_rd_rev_vpd_MASK               0x00000001
+#define lpfc_mbx_rd_rev_vpd_WORD               word1
+       uint32_t first_hw_rev;
+       uint32_t second_hw_rev;
+       uint32_t word4_rsvd;
+       uint32_t third_hw_rev;
+       uint32_t word6;
+#define lpfc_mbx_rd_rev_fcph_low_SHIFT         0
+#define lpfc_mbx_rd_rev_fcph_low_MASK          0x000000FF
+#define lpfc_mbx_rd_rev_fcph_low_WORD          word6
+#define lpfc_mbx_rd_rev_fcph_high_SHIFT                8
+#define lpfc_mbx_rd_rev_fcph_high_MASK         0x000000FF
+#define lpfc_mbx_rd_rev_fcph_high_WORD         word6
+#define lpfc_mbx_rd_rev_ftr_lvl_low_SHIFT      16
+#define lpfc_mbx_rd_rev_ftr_lvl_low_MASK       0x000000FF
+#define lpfc_mbx_rd_rev_ftr_lvl_low_WORD       word6
+#define lpfc_mbx_rd_rev_ftr_lvl_high_SHIFT     24
+#define lpfc_mbx_rd_rev_ftr_lvl_high_MASK      0x000000FF
+#define lpfc_mbx_rd_rev_ftr_lvl_high_WORD      word6
+       uint32_t word7_rsvd;
+       uint32_t fw_id_rev;
+       uint8_t  fw_name[16];
+       uint32_t ulp_fw_id_rev;
+       uint8_t  ulp_fw_name[16];
+       uint32_t word18_47_rsvd[30];
+       uint32_t word48;
+#define lpfc_mbx_rd_rev_avail_len_SHIFT                0
+#define lpfc_mbx_rd_rev_avail_len_MASK         0x00FFFFFF
+#define lpfc_mbx_rd_rev_avail_len_WORD         word48
+       uint32_t vpd_paddr_low;
+       uint32_t vpd_paddr_high;
+       uint32_t avail_vpd_len;
+       uint32_t rsvd_52_63[12];
+};
+
+struct lpfc_mbx_read_config {
+       uint32_t word1;
+#define lpfc_mbx_rd_conf_max_bbc_SHIFT         0
+#define lpfc_mbx_rd_conf_max_bbc_MASK          0x000000FF
+#define lpfc_mbx_rd_conf_max_bbc_WORD          word1
+#define lpfc_mbx_rd_conf_init_bbc_SHIFT                8
+#define lpfc_mbx_rd_conf_init_bbc_MASK         0x000000FF
+#define lpfc_mbx_rd_conf_init_bbc_WORD         word1
+       uint32_t word2;
+#define lpfc_mbx_rd_conf_nport_did_SHIFT       0
+#define lpfc_mbx_rd_conf_nport_did_MASK                0x00FFFFFF
+#define lpfc_mbx_rd_conf_nport_did_WORD                word2
+#define lpfc_mbx_rd_conf_topology_SHIFT                24
+#define lpfc_mbx_rd_conf_topology_MASK         0x000000FF
+#define lpfc_mbx_rd_conf_topology_WORD         word2
+       uint32_t word3;
+#define lpfc_mbx_rd_conf_ao_SHIFT              0
+#define lpfc_mbx_rd_conf_ao_MASK               0x00000001
+#define lpfc_mbx_rd_conf_ao_WORD               word3
+#define lpfc_mbx_rd_conf_bb_scn_SHIFT          8
+#define lpfc_mbx_rd_conf_bb_scn_MASK           0x0000000F
+#define lpfc_mbx_rd_conf_bb_scn_WORD           word3
+#define lpfc_mbx_rd_conf_cbb_scn_SHIFT         12
+#define lpfc_mbx_rd_conf_cbb_scn_MASK          0x0000000F
+#define lpfc_mbx_rd_conf_cbb_scn_WORD          word3
+#define lpfc_mbx_rd_conf_mc_SHIFT              29
+#define lpfc_mbx_rd_conf_mc_MASK               0x00000001
+#define lpfc_mbx_rd_conf_mc_WORD               word3
+       uint32_t word4;
+#define lpfc_mbx_rd_conf_e_d_tov_SHIFT         0
+#define lpfc_mbx_rd_conf_e_d_tov_MASK          0x0000FFFF
+#define lpfc_mbx_rd_conf_e_d_tov_WORD          word4
+       uint32_t word5;
+#define lpfc_mbx_rd_conf_lp_tov_SHIFT          0
+#define lpfc_mbx_rd_conf_lp_tov_MASK           0x0000FFFF
+#define lpfc_mbx_rd_conf_lp_tov_WORD           word5
+       uint32_t word6;
+#define lpfc_mbx_rd_conf_r_a_tov_SHIFT         0
+#define lpfc_mbx_rd_conf_r_a_tov_MASK          0x0000FFFF
+#define lpfc_mbx_rd_conf_r_a_tov_WORD          word6
+       uint32_t word7;
+#define lpfc_mbx_rd_conf_r_t_tov_SHIFT         0
+#define lpfc_mbx_rd_conf_r_t_tov_MASK          0x000000FF
+#define lpfc_mbx_rd_conf_r_t_tov_WORD          word7
+       uint32_t word8;
+#define lpfc_mbx_rd_conf_al_tov_SHIFT          0
+#define lpfc_mbx_rd_conf_al_tov_MASK           0x0000000F
+#define lpfc_mbx_rd_conf_al_tov_WORD           word8
+       uint32_t word9;
+#define lpfc_mbx_rd_conf_lmt_SHIFT             0
+#define lpfc_mbx_rd_conf_lmt_MASK              0x0000FFFF
+#define lpfc_mbx_rd_conf_lmt_WORD              word9
+       uint32_t word10;
+#define lpfc_mbx_rd_conf_max_alpa_SHIFT                0
+#define lpfc_mbx_rd_conf_max_alpa_MASK         0x000000FF
+#define lpfc_mbx_rd_conf_max_alpa_WORD         word10
+       uint32_t word11_rsvd;
+       uint32_t word12;
+#define lpfc_mbx_rd_conf_xri_base_SHIFT                0
+#define lpfc_mbx_rd_conf_xri_base_MASK         0x0000FFFF
+#define lpfc_mbx_rd_conf_xri_base_WORD         word12
+#define lpfc_mbx_rd_conf_xri_count_SHIFT       16
+#define lpfc_mbx_rd_conf_xri_count_MASK                0x0000FFFF
+#define lpfc_mbx_rd_conf_xri_count_WORD                word12
+       uint32_t word13;
+#define lpfc_mbx_rd_conf_rpi_base_SHIFT                0
+#define lpfc_mbx_rd_conf_rpi_base_MASK         0x0000FFFF
+#define lpfc_mbx_rd_conf_rpi_base_WORD         word13
+#define lpfc_mbx_rd_conf_rpi_count_SHIFT       16
+#define lpfc_mbx_rd_conf_rpi_count_MASK                0x0000FFFF
+#define lpfc_mbx_rd_conf_rpi_count_WORD                word13
+       uint32_t word14;
+#define lpfc_mbx_rd_conf_vpi_base_SHIFT                0
+#define lpfc_mbx_rd_conf_vpi_base_MASK         0x0000FFFF
+#define lpfc_mbx_rd_conf_vpi_base_WORD         word14
+#define lpfc_mbx_rd_conf_vpi_count_SHIFT       16
+#define lpfc_mbx_rd_conf_vpi_count_MASK                0x0000FFFF
+#define lpfc_mbx_rd_conf_vpi_count_WORD                word14
+       uint32_t word15;
+#define lpfc_mbx_rd_conf_vfi_base_SHIFT         0
+#define lpfc_mbx_rd_conf_vfi_base_MASK          0x0000FFFF
+#define lpfc_mbx_rd_conf_vfi_base_WORD          word15
+#define lpfc_mbx_rd_conf_vfi_count_SHIFT        16
+#define lpfc_mbx_rd_conf_vfi_count_MASK         0x0000FFFF
+#define lpfc_mbx_rd_conf_vfi_count_WORD         word15
+       uint32_t word16;
+#define lpfc_mbx_rd_conf_fcfi_base_SHIFT       0
+#define lpfc_mbx_rd_conf_fcfi_base_MASK                0x0000FFFF
+#define lpfc_mbx_rd_conf_fcfi_base_WORD                word16
+#define lpfc_mbx_rd_conf_fcfi_count_SHIFT      16
+#define lpfc_mbx_rd_conf_fcfi_count_MASK       0x0000FFFF
+#define lpfc_mbx_rd_conf_fcfi_count_WORD       word16
+       uint32_t word17;
+#define lpfc_mbx_rd_conf_rq_count_SHIFT                0
+#define lpfc_mbx_rd_conf_rq_count_MASK         0x0000FFFF
+#define lpfc_mbx_rd_conf_rq_count_WORD         word17
+#define lpfc_mbx_rd_conf_eq_count_SHIFT                16
+#define lpfc_mbx_rd_conf_eq_count_MASK         0x0000FFFF
+#define lpfc_mbx_rd_conf_eq_count_WORD         word17
+       uint32_t word18;
+#define lpfc_mbx_rd_conf_wq_count_SHIFT                0
+#define lpfc_mbx_rd_conf_wq_count_MASK         0x0000FFFF
+#define lpfc_mbx_rd_conf_wq_count_WORD         word18
+#define lpfc_mbx_rd_conf_cq_count_SHIFT                16
+#define lpfc_mbx_rd_conf_cq_count_MASK         0x0000FFFF
+#define lpfc_mbx_rd_conf_cq_count_WORD         word18
+};
+
+struct lpfc_mbx_request_features {
+       uint32_t word1;
+#define lpfc_mbx_rq_ftr_qry_SHIFT              0
+#define lpfc_mbx_rq_ftr_qry_MASK               0x00000001
+#define lpfc_mbx_rq_ftr_qry_WORD               word1
+       uint32_t word2;
+#define lpfc_mbx_rq_ftr_rq_iaab_SHIFT          0
+#define lpfc_mbx_rq_ftr_rq_iaab_MASK           0x00000001
+#define lpfc_mbx_rq_ftr_rq_iaab_WORD           word2
+#define lpfc_mbx_rq_ftr_rq_npiv_SHIFT          1
+#define lpfc_mbx_rq_ftr_rq_npiv_MASK           0x00000001
+#define lpfc_mbx_rq_ftr_rq_npiv_WORD           word2
+#define lpfc_mbx_rq_ftr_rq_dif_SHIFT           2
+#define lpfc_mbx_rq_ftr_rq_dif_MASK            0x00000001
+#define lpfc_mbx_rq_ftr_rq_dif_WORD            word2
+#define lpfc_mbx_rq_ftr_rq_vf_SHIFT            3
+#define lpfc_mbx_rq_ftr_rq_vf_MASK             0x00000001
+#define lpfc_mbx_rq_ftr_rq_vf_WORD             word2
+#define lpfc_mbx_rq_ftr_rq_fcpi_SHIFT          4
+#define lpfc_mbx_rq_ftr_rq_fcpi_MASK           0x00000001
+#define lpfc_mbx_rq_ftr_rq_fcpi_WORD           word2
+#define lpfc_mbx_rq_ftr_rq_fcpt_SHIFT          5
+#define lpfc_mbx_rq_ftr_rq_fcpt_MASK           0x00000001
+#define lpfc_mbx_rq_ftr_rq_fcpt_WORD           word2
+#define lpfc_mbx_rq_ftr_rq_fcpc_SHIFT          6
+#define lpfc_mbx_rq_ftr_rq_fcpc_MASK           0x00000001
+#define lpfc_mbx_rq_ftr_rq_fcpc_WORD           word2
+#define lpfc_mbx_rq_ftr_rq_ifip_SHIFT          7
+#define lpfc_mbx_rq_ftr_rq_ifip_MASK           0x00000001
+#define lpfc_mbx_rq_ftr_rq_ifip_WORD           word2
+       uint32_t word3;
+#define lpfc_mbx_rq_ftr_rsp_iaab_SHIFT         0
+#define lpfc_mbx_rq_ftr_rsp_iaab_MASK          0x00000001
+#define lpfc_mbx_rq_ftr_rsp_iaab_WORD          word3
+#define lpfc_mbx_rq_ftr_rsp_npiv_SHIFT         1
+#define lpfc_mbx_rq_ftr_rsp_npiv_MASK          0x00000001
+#define lpfc_mbx_rq_ftr_rsp_npiv_WORD          word3
+#define lpfc_mbx_rq_ftr_rsp_dif_SHIFT          2
+#define lpfc_mbx_rq_ftr_rsp_dif_MASK           0x00000001
+#define lpfc_mbx_rq_ftr_rsp_dif_WORD           word3
+#define lpfc_mbx_rq_ftr_rsp_vf_SHIFT           3
+#define lpfc_mbx_rq_ftr_rsp_vf__MASK           0x00000001
+#define lpfc_mbx_rq_ftr_rsp_vf_WORD            word3
+#define lpfc_mbx_rq_ftr_rsp_fcpi_SHIFT         4
+#define lpfc_mbx_rq_ftr_rsp_fcpi_MASK          0x00000001
+#define lpfc_mbx_rq_ftr_rsp_fcpi_WORD          word3
+#define lpfc_mbx_rq_ftr_rsp_fcpt_SHIFT         5
+#define lpfc_mbx_rq_ftr_rsp_fcpt_MASK          0x00000001
+#define lpfc_mbx_rq_ftr_rsp_fcpt_WORD          word3
+#define lpfc_mbx_rq_ftr_rsp_fcpc_SHIFT         6
+#define lpfc_mbx_rq_ftr_rsp_fcpc_MASK          0x00000001
+#define lpfc_mbx_rq_ftr_rsp_fcpc_WORD          word3
+#define lpfc_mbx_rq_ftr_rsp_ifip_SHIFT         7
+#define lpfc_mbx_rq_ftr_rsp_ifip_MASK          0x00000001
+#define lpfc_mbx_rq_ftr_rsp_ifip_WORD          word3
+};
+
+/* Mailbox Completion Queue Error Messages */
+#define MB_CQE_STATUS_SUCCESS                  0x0
+#define MB_CQE_STATUS_INSUFFICIENT_PRIVILEGES  0x1
+#define MB_CQE_STATUS_INVALID_PARAMETER                0x2
+#define MB_CQE_STATUS_INSUFFICIENT_RESOURCES   0x3
+#define MB_CEQ_STATUS_QUEUE_FLUSHING           0x4
+#define MB_CQE_STATUS_DMA_FAILED               0x5
+
+/* mailbox queue entry structure */
+struct lpfc_mqe {
+       uint32_t word0;
+#define lpfc_mqe_status_SHIFT          16
+#define lpfc_mqe_status_MASK           0x0000FFFF
+#define lpfc_mqe_status_WORD           word0
+#define lpfc_mqe_command_SHIFT         8
+#define lpfc_mqe_command_MASK          0x000000FF
+#define lpfc_mqe_command_WORD          word0
+       union {
+               uint32_t mb_words[LPFC_SLI4_MB_WORD_COUNT - 1];
+               /* sli4 mailbox commands */
+               struct lpfc_mbx_sli4_config sli4_config;
+               struct lpfc_mbx_init_vfi init_vfi;
+               struct lpfc_mbx_reg_vfi reg_vfi;
+               struct lpfc_mbx_reg_vfi unreg_vfi;
+               struct lpfc_mbx_init_vpi init_vpi;
+               struct lpfc_mbx_resume_rpi resume_rpi;
+               struct lpfc_mbx_read_fcf_tbl read_fcf_tbl;
+               struct lpfc_mbx_add_fcf_tbl_entry add_fcf_entry;
+               struct lpfc_mbx_del_fcf_tbl_entry del_fcf_entry;
+               struct lpfc_mbx_reg_fcfi reg_fcfi;
+               struct lpfc_mbx_unreg_fcfi unreg_fcfi;
+               struct lpfc_mbx_mq_create mq_create;
+               struct lpfc_mbx_eq_create eq_create;
+               struct lpfc_mbx_cq_create cq_create;
+               struct lpfc_mbx_wq_create wq_create;
+               struct lpfc_mbx_rq_create rq_create;
+               struct lpfc_mbx_mq_destroy mq_destroy;
+               struct lpfc_mbx_eq_destroy eq_destroy;
+               struct lpfc_mbx_cq_destroy cq_destroy;
+               struct lpfc_mbx_wq_destroy wq_destroy;
+               struct lpfc_mbx_rq_destroy rq_destroy;
+               struct lpfc_mbx_post_sgl_pages post_sgl_pages;
+               struct lpfc_mbx_nembed_cmd nembed_cmd;
+               struct lpfc_mbx_read_rev read_rev;
+               struct lpfc_mbx_read_vpi read_vpi;
+               struct lpfc_mbx_read_config rd_config;
+               struct lpfc_mbx_request_features req_ftrs;
+               struct lpfc_mbx_post_hdr_tmpl hdr_tmpl;
+               struct lpfc_mbx_nop nop;
+       } un;
+};
+
+struct lpfc_mcqe {
+       uint32_t word0;
+#define lpfc_mcqe_status_SHIFT         0
+#define lpfc_mcqe_status_MASK          0x0000FFFF
+#define lpfc_mcqe_status_WORD          word0
+#define lpfc_mcqe_ext_status_SHIFT     16
+#define lpfc_mcqe_ext_status_MASK      0x0000FFFF
+#define lpfc_mcqe_ext_status_WORD      word0
+       uint32_t mcqe_tag0;
+       uint32_t mcqe_tag1;
+       uint32_t trailer;
+#define lpfc_trailer_valid_SHIFT       31
+#define lpfc_trailer_valid_MASK                0x00000001
+#define lpfc_trailer_valid_WORD                trailer
+#define lpfc_trailer_async_SHIFT       30
+#define lpfc_trailer_async_MASK                0x00000001
+#define lpfc_trailer_async_WORD                trailer
+#define lpfc_trailer_hpi_SHIFT         29
+#define lpfc_trailer_hpi_MASK          0x00000001
+#define lpfc_trailer_hpi_WORD          trailer
+#define lpfc_trailer_completed_SHIFT   28
+#define lpfc_trailer_completed_MASK    0x00000001
+#define lpfc_trailer_completed_WORD    trailer
+#define lpfc_trailer_consumed_SHIFT    27
+#define lpfc_trailer_consumed_MASK     0x00000001
+#define lpfc_trailer_consumed_WORD     trailer
+#define lpfc_trailer_type_SHIFT                16
+#define lpfc_trailer_type_MASK         0x000000FF
+#define lpfc_trailer_type_WORD         trailer
+#define lpfc_trailer_code_SHIFT                8
+#define lpfc_trailer_code_MASK         0x000000FF
+#define lpfc_trailer_code_WORD         trailer
+#define LPFC_TRAILER_CODE_LINK 0x1
+#define LPFC_TRAILER_CODE_FCOE 0x2
+#define LPFC_TRAILER_CODE_DCBX 0x3
+};
+
+struct lpfc_acqe_link {
+       uint32_t word0;
+#define lpfc_acqe_link_speed_SHIFT             24
+#define lpfc_acqe_link_speed_MASK              0x000000FF
+#define lpfc_acqe_link_speed_WORD              word0
+#define LPFC_ASYNC_LINK_SPEED_ZERO             0x0
+#define LPFC_ASYNC_LINK_SPEED_10MBPS           0x1
+#define LPFC_ASYNC_LINK_SPEED_100MBPS          0x2
+#define LPFC_ASYNC_LINK_SPEED_1GBPS            0x3
+#define LPFC_ASYNC_LINK_SPEED_10GBPS           0x4
+#define lpfc_acqe_link_duplex_SHIFT            16
+#define lpfc_acqe_link_duplex_MASK             0x000000FF
+#define lpfc_acqe_link_duplex_WORD             word0
+#define LPFC_ASYNC_LINK_DUPLEX_NONE            0x0
+#define LPFC_ASYNC_LINK_DUPLEX_HALF            0x1
+#define LPFC_ASYNC_LINK_DUPLEX_FULL            0x2
+#define lpfc_acqe_link_status_SHIFT            8
+#define lpfc_acqe_link_status_MASK             0x000000FF
+#define lpfc_acqe_link_status_WORD             word0
+#define LPFC_ASYNC_LINK_STATUS_DOWN            0x0
+#define LPFC_ASYNC_LINK_STATUS_UP              0x1
+#define LPFC_ASYNC_LINK_STATUS_LOGICAL_DOWN    0x2
+#define LPFC_ASYNC_LINK_STATUS_LOGICAL_UP      0x3
+#define lpfc_acqe_link_physical_SHIFT          0
+#define lpfc_acqe_link_physical_MASK           0x000000FF
+#define lpfc_acqe_link_physical_WORD           word0
+#define LPFC_ASYNC_LINK_PORT_A                 0x0
+#define LPFC_ASYNC_LINK_PORT_B                 0x1
+       uint32_t word1;
+#define lpfc_acqe_link_fault_SHIFT     0
+#define lpfc_acqe_link_fault_MASK      0x000000FF
+#define lpfc_acqe_link_fault_WORD      word1
+#define LPFC_ASYNC_LINK_FAULT_NONE     0x0
+#define LPFC_ASYNC_LINK_FAULT_LOCAL    0x1
+#define LPFC_ASYNC_LINK_FAULT_REMOTE   0x2
+       uint32_t event_tag;
+       uint32_t trailer;
+};
+
+struct lpfc_acqe_fcoe {
+       uint32_t fcf_index;
+       uint32_t word1;
+#define lpfc_acqe_fcoe_fcf_count_SHIFT         0
+#define lpfc_acqe_fcoe_fcf_count_MASK          0x0000FFFF
+#define lpfc_acqe_fcoe_fcf_count_WORD          word1
+#define lpfc_acqe_fcoe_event_type_SHIFT                16
+#define lpfc_acqe_fcoe_event_type_MASK         0x0000FFFF
+#define lpfc_acqe_fcoe_event_type_WORD         word1
+#define LPFC_FCOE_EVENT_TYPE_NEW_FCF           0x1
+#define LPFC_FCOE_EVENT_TYPE_FCF_TABLE_FULL    0x2
+#define LPFC_FCOE_EVENT_TYPE_FCF_DEAD          0x3
+       uint32_t event_tag;
+       uint32_t trailer;
+};
+
+struct lpfc_acqe_dcbx {
+       uint32_t tlv_ttl;
+       uint32_t reserved;
+       uint32_t event_tag;
+       uint32_t trailer;
+};
+
+/*
+ * Define the bootstrap mailbox (bmbx) region used to communicate
+ * mailbox command between the host and port. The mailbox consists
+ * of a payload area of 256 bytes and a completion queue of length
+ * 16 bytes.
+ */
+struct lpfc_bmbx_create {
+       struct lpfc_mqe mqe;
+       struct lpfc_mcqe mcqe;
+};
+
+#define SGL_ALIGN_SZ 64
+#define SGL_PAGE_SIZE 4096
+/* align SGL addr on a size boundary - adjust address up */
+#define NO_XRI ((uint16_t)-1)
+struct wqe_common {
+       uint32_t word6;
+#define wqe_xri_SHIFT         0
+#define wqe_xri_MASK          0x0000FFFF
+#define wqe_xri_WORD          word6
+#define wqe_ctxt_tag_SHIFT    16
+#define wqe_ctxt_tag_MASK     0x0000FFFF
+#define wqe_ctxt_tag_WORD     word6
+       uint32_t word7;
+#define wqe_ct_SHIFT          2
+#define wqe_ct_MASK           0x00000003
+#define wqe_ct_WORD           word7
+#define wqe_status_SHIFT      4
+#define wqe_status_MASK       0x0000000f
+#define wqe_status_WORD       word7
+#define wqe_cmnd_SHIFT        8
+#define wqe_cmnd_MASK         0x000000ff
+#define wqe_cmnd_WORD         word7
+#define wqe_class_SHIFT       16
+#define wqe_class_MASK        0x00000007
+#define wqe_class_WORD        word7
+#define wqe_pu_SHIFT          20
+#define wqe_pu_MASK           0x00000003
+#define wqe_pu_WORD           word7
+#define wqe_erp_SHIFT         22
+#define wqe_erp_MASK          0x00000001
+#define wqe_erp_WORD          word7
+#define wqe_lnk_SHIFT         23
+#define wqe_lnk_MASK          0x00000001
+#define wqe_lnk_WORD          word7
+#define wqe_tmo_SHIFT         24
+#define wqe_tmo_MASK          0x000000ff
+#define wqe_tmo_WORD          word7
+       uint32_t abort_tag; /* word 8 in WQE */
+       uint32_t word9;
+#define wqe_reqtag_SHIFT      0
+#define wqe_reqtag_MASK       0x0000FFFF
+#define wqe_reqtag_WORD       word9
+#define wqe_rcvoxid_SHIFT     16
+#define wqe_rcvoxid_MASK       0x0000FFFF
+#define wqe_rcvoxid_WORD       word9
+       uint32_t word10;
+#define wqe_pri_SHIFT         16
+#define wqe_pri_MASK          0x00000007
+#define wqe_pri_WORD          word10
+#define wqe_pv_SHIFT          19
+#define wqe_pv_MASK           0x00000001
+#define wqe_pv_WORD           word10
+#define wqe_xc_SHIFT          21
+#define wqe_xc_MASK           0x00000001
+#define wqe_xc_WORD           word10
+#define wqe_ccpe_SHIFT        23
+#define wqe_ccpe_MASK         0x00000001
+#define wqe_ccpe_WORD         word10
+#define wqe_ccp_SHIFT         24
+#define wqe_ccp_MASK         0x000000ff
+#define wqe_ccp_WORD         word10
+       uint32_t word11;
+#define wqe_cmd_type_SHIFT  0
+#define wqe_cmd_type_MASK   0x0000000f
+#define wqe_cmd_type_WORD   word11
+#define wqe_wqec_SHIFT      7
+#define wqe_wqec_MASK       0x00000001
+#define wqe_wqec_WORD       word11
+#define wqe_cqid_SHIFT      16
+#define wqe_cqid_MASK       0x000003ff
+#define wqe_cqid_WORD       word11
+};
+
+struct wqe_did {
+       uint32_t word5;
+#define wqe_els_did_SHIFT         0
+#define wqe_els_did_MASK          0x00FFFFFF
+#define wqe_els_did_WORD          word5
+#define wqe_xmit_bls_ar_SHIFT         30
+#define wqe_xmit_bls_ar_MASK          0x00000001
+#define wqe_xmit_bls_ar_WORD          word5
+#define wqe_xmit_bls_xo_SHIFT         31
+#define wqe_xmit_bls_xo_MASK          0x00000001
+#define wqe_xmit_bls_xo_WORD          word5
+};
+
+struct els_request64_wqe {
+       struct ulp_bde64 bde;
+       uint32_t payload_len;
+       uint32_t word4;
+#define els_req64_sid_SHIFT         0
+#define els_req64_sid_MASK          0x00FFFFFF
+#define els_req64_sid_WORD          word4
+#define els_req64_sp_SHIFT          24
+#define els_req64_sp_MASK           0x00000001
+#define els_req64_sp_WORD           word4
+#define els_req64_vf_SHIFT          25
+#define els_req64_vf_MASK           0x00000001
+#define els_req64_vf_WORD           word4
+       struct wqe_did  wqe_dest;
+       struct wqe_common wqe_com; /* words 6-11 */
+       uint32_t word12;
+#define els_req64_vfid_SHIFT        1
+#define els_req64_vfid_MASK         0x00000FFF
+#define els_req64_vfid_WORD         word12
+#define els_req64_pri_SHIFT         13
+#define els_req64_pri_MASK          0x00000007
+#define els_req64_pri_WORD          word12
+       uint32_t word13;
+#define els_req64_hopcnt_SHIFT      24
+#define els_req64_hopcnt_MASK       0x000000ff
+#define els_req64_hopcnt_WORD       word13
+       uint32_t reserved[2];
+};
+
+struct xmit_els_rsp64_wqe {
+       struct ulp_bde64 bde;
+       uint32_t rsvd3;
+       uint32_t rsvd4;
+       struct wqe_did  wqe_dest;
+       struct wqe_common wqe_com; /* words 6-11 */
+       uint32_t rsvd_12_15[4];
+};
+
+struct xmit_bls_rsp64_wqe {
+       uint32_t payload0;
+       uint32_t word1;
+#define xmit_bls_rsp64_rxid_SHIFT  0
+#define xmit_bls_rsp64_rxid_MASK   0x0000ffff
+#define xmit_bls_rsp64_rxid_WORD   word1
+#define xmit_bls_rsp64_oxid_SHIFT  16
+#define xmit_bls_rsp64_oxid_MASK   0x0000ffff
+#define xmit_bls_rsp64_oxid_WORD   word1
+       uint32_t word2;
+#define xmit_bls_rsp64_seqcntlo_SHIFT  0
+#define xmit_bls_rsp64_seqcntlo_MASK   0x0000ffff
+#define xmit_bls_rsp64_seqcntlo_WORD   word2
+#define xmit_bls_rsp64_seqcnthi_SHIFT  16
+#define xmit_bls_rsp64_seqcnthi_MASK   0x0000ffff
+#define xmit_bls_rsp64_seqcnthi_WORD   word2
+       uint32_t rsrvd3;
+       uint32_t rsrvd4;
+       struct wqe_did  wqe_dest;
+       struct wqe_common wqe_com; /* words 6-11 */
+       uint32_t rsvd_12_15[4];
+};
+struct wqe_rctl_dfctl {
+       uint32_t word5;
+#define wqe_si_SHIFT 2
+#define wqe_si_MASK  0x000000001
+#define wqe_si_WORD  word5
+#define wqe_la_SHIFT 3
+#define wqe_la_MASK  0x000000001
+#define wqe_la_WORD  word5
+#define wqe_ls_SHIFT 7
+#define wqe_ls_MASK  0x000000001
+#define wqe_ls_WORD  word5
+#define wqe_dfctl_SHIFT 8
+#define wqe_dfctl_MASK  0x0000000ff
+#define wqe_dfctl_WORD  word5
+#define wqe_type_SHIFT 16
+#define wqe_type_MASK  0x0000000ff
+#define wqe_type_WORD  word5
+#define wqe_rctl_SHIFT 24
+#define wqe_rctl_MASK  0x0000000ff
+#define wqe_rctl_WORD  word5
+};
+
+struct xmit_seq64_wqe {
+       struct ulp_bde64 bde;
+       uint32_t paylaod_offset;
+       uint32_t relative_offset;
+       struct wqe_rctl_dfctl wge_ctl;
+       struct wqe_common wqe_com; /* words 6-11 */
+       /* Note: word10 different REVISIT */
+       uint32_t xmit_len;
+       uint32_t rsvd_12_15[3];
+};
+struct xmit_bcast64_wqe {
+       struct ulp_bde64 bde;
+       uint32_t paylaod_len;
+       uint32_t rsvd4;
+       struct wqe_rctl_dfctl wge_ctl; /* word 5 */
+       struct wqe_common wqe_com;     /* words 6-11 */
+       uint32_t rsvd_12_15[4];
+};
+
+struct gen_req64_wqe {
+       struct ulp_bde64 bde;
+       uint32_t command_len;
+       uint32_t payload_len;
+       struct wqe_rctl_dfctl wge_ctl; /* word 5 */
+       struct wqe_common wqe_com;     /* words 6-11 */
+       uint32_t rsvd_12_15[4];
+};
+
+struct create_xri_wqe {
+       uint32_t rsrvd[5];           /* words 0-4 */
+       struct wqe_did  wqe_dest;  /* word 5 */
+       struct wqe_common wqe_com; /* words 6-11 */
+       uint32_t rsvd_12_15[4];         /* word 12-15 */
+};
+
+#define T_REQUEST_TAG 3
+#define T_XRI_TAG 1
+
+struct abort_cmd_wqe {
+       uint32_t rsrvd[3];
+       uint32_t word3;
+#define        abort_cmd_ia_SHIFT  0
+#define        abort_cmd_ia_MASK  0x000000001
+#define        abort_cmd_ia_WORD  word3
+#define        abort_cmd_criteria_SHIFT  8
+#define        abort_cmd_criteria_MASK  0x0000000ff
+#define        abort_cmd_criteria_WORD  word3
+       uint32_t rsrvd4;
+       uint32_t rsrvd5;
+       struct wqe_common wqe_com;     /* words 6-11 */
+       uint32_t rsvd_12_15[4];         /* word 12-15 */
+};
+
+struct fcp_iwrite64_wqe {
+       struct ulp_bde64 bde;
+       uint32_t payload_len;
+       uint32_t total_xfer_len;
+       uint32_t initial_xfer_len;
+       struct wqe_common wqe_com;     /* words 6-11 */
+       uint32_t rsvd_12_15[4];         /* word 12-15 */
+};
+
+struct fcp_iread64_wqe {
+       struct ulp_bde64 bde;
+       uint32_t payload_len;          /* word 3 */
+       uint32_t total_xfer_len;       /* word 4 */
+       uint32_t rsrvd5;               /* word 5 */
+       struct wqe_common wqe_com;     /* words 6-11 */
+       uint32_t rsvd_12_15[4];         /* word 12-15 */
+};
+
+struct fcp_icmnd64_wqe {
+       struct ulp_bde64 bde;    /* words 0-2 */
+       uint32_t rsrvd[3];             /* words 3-5 */
+       struct wqe_common wqe_com;     /* words 6-11 */
+       uint32_t rsvd_12_15[4];         /* word 12-15 */
+};
+
+
+union lpfc_wqe {
+       uint32_t words[16];
+       struct lpfc_wqe_generic generic;
+       struct fcp_icmnd64_wqe fcp_icmd;
+       struct fcp_iread64_wqe fcp_iread;
+       struct fcp_iwrite64_wqe fcp_iwrite;
+       struct abort_cmd_wqe abort_cmd;
+       struct create_xri_wqe create_xri;
+       struct xmit_bcast64_wqe xmit_bcast64;
+       struct xmit_seq64_wqe xmit_sequence;
+       struct xmit_bls_rsp64_wqe xmit_bls_rsp;
+       struct xmit_els_rsp64_wqe xmit_els_rsp;
+       struct els_request64_wqe els_req;
+       struct gen_req64_wqe gen_req;
+};
+
+#define FCP_COMMAND 0x0
+#define FCP_COMMAND_DATA_OUT 0x1
+#define ELS_COMMAND_NON_FIP 0xC
+#define ELS_COMMAND_FIP 0xD
+#define OTHER_COMMAND 0x8
+
index 86d1bdcbf2d819003355d81c06318c451575d23d..2f5907f92eeaee7cac3079615ac035d67c27e1a5 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2008 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2009 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport_fc.h>
 
+#include "lpfc_hw4.h"
 #include "lpfc_hw.h"
 #include "lpfc_sli.h"
+#include "lpfc_sli4.h"
 #include "lpfc_nl.h"
 #include "lpfc_disc.h"
 #include "lpfc_scsi.h"
@@ -51,9 +53,23 @@ char *_dump_buf_dif;
 unsigned long _dump_buf_dif_order;
 spinlock_t _dump_buf_lock;
 
-static int lpfc_parse_vpd(struct lpfc_hba *, uint8_t *, int);
 static void lpfc_get_hba_model_desc(struct lpfc_hba *, uint8_t *, uint8_t *);
 static int lpfc_post_rcv_buf(struct lpfc_hba *);
+static int lpfc_sli4_queue_create(struct lpfc_hba *);
+static void lpfc_sli4_queue_destroy(struct lpfc_hba *);
+static int lpfc_create_bootstrap_mbox(struct lpfc_hba *);
+static int lpfc_setup_endian_order(struct lpfc_hba *);
+static int lpfc_sli4_read_config(struct lpfc_hba *);
+static void lpfc_destroy_bootstrap_mbox(struct lpfc_hba *);
+static void lpfc_free_sgl_list(struct lpfc_hba *);
+static int lpfc_init_sgl_list(struct lpfc_hba *);
+static int lpfc_init_active_sgl_array(struct lpfc_hba *);
+static void lpfc_free_active_sgl(struct lpfc_hba *);
+static int lpfc_hba_down_post_s3(struct lpfc_hba *phba);
+static int lpfc_hba_down_post_s4(struct lpfc_hba *phba);
+static int lpfc_sli4_cq_event_pool_create(struct lpfc_hba *);
+static void lpfc_sli4_cq_event_pool_destroy(struct lpfc_hba *);
+static void lpfc_sli4_cq_event_release_all(struct lpfc_hba *);
 
 static struct scsi_transport_template *lpfc_transport_template = NULL;
 static struct scsi_transport_template *lpfc_vport_transport_template = NULL;
@@ -92,7 +108,7 @@ lpfc_config_port_prep(struct lpfc_hba *phba)
                return -ENOMEM;
        }
 
-       mb = &pmb->mb;
+       mb = &pmb->u.mb;
        phba->link_state = LPFC_INIT_MBX_CMDS;
 
        if (lpfc_is_LC_HBA(phba->pcidev->device)) {
@@ -205,6 +221,11 @@ lpfc_config_port_prep(struct lpfc_hba *phba)
                                        mb->mbxCommand, mb->mbxStatus);
                        mb->un.varDmp.word_cnt = 0;
                }
+               /* dump mem may return a zero when finished or we got a
+                * mailbox error, either way we are done.
+                */
+               if (mb->un.varDmp.word_cnt == 0)
+                       break;
                if (mb->un.varDmp.word_cnt > DMP_VPD_SIZE - offset)
                        mb->un.varDmp.word_cnt = DMP_VPD_SIZE - offset;
                lpfc_sli_pcimem_bcopy(((uint8_t *)mb) + DMP_RSP_OFFSET,
@@ -233,7 +254,7 @@ out_free_mbox:
 static void
 lpfc_config_async_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
 {
-       if (pmboxq->mb.mbxStatus == MBX_SUCCESS)
+       if (pmboxq->u.mb.mbxStatus == MBX_SUCCESS)
                phba->temp_sensor_support = 1;
        else
                phba->temp_sensor_support = 0;
@@ -260,7 +281,7 @@ lpfc_dump_wakeup_param_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
        /* character array used for decoding dist type. */
        char dist_char[] = "nabx";
 
-       if (pmboxq->mb.mbxStatus != MBX_SUCCESS) {
+       if (pmboxq->u.mb.mbxStatus != MBX_SUCCESS) {
                mempool_free(pmboxq, phba->mbox_mem_pool);
                return;
        }
@@ -268,7 +289,7 @@ lpfc_dump_wakeup_param_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
        prg = (struct prog_id *) &prog_id_word;
 
        /* word 7 contain option rom version */
-       prog_id_word = pmboxq->mb.un.varWords[7];
+       prog_id_word = pmboxq->u.mb.un.varWords[7];
 
        /* Decode the Option rom version word to a readable string */
        if (prg->dist < 4)
@@ -325,7 +346,7 @@ lpfc_config_port_post(struct lpfc_hba *phba)
                phba->link_state = LPFC_HBA_ERROR;
                return -ENOMEM;
        }
-       mb = &pmb->mb;
+       mb = &pmb->u.mb;
 
        /* Get login parameters for NID.  */
        lpfc_read_sparam(phba, pmb, 0);
@@ -364,6 +385,7 @@ lpfc_config_port_post(struct lpfc_hba *phba)
        /* Update the fc_host data structures with new wwn. */
        fc_host_node_name(shost) = wwn_to_u64(vport->fc_nodename.u.wwn);
        fc_host_port_name(shost) = wwn_to_u64(vport->fc_portname.u.wwn);
+       fc_host_max_npiv_vports(shost) = phba->max_vpi;
 
        /* If no serial number in VPD data, use low 6 bytes of WWNN */
        /* This should be consolidated into parse_vpd ? - mr */
@@ -460,17 +482,18 @@ lpfc_config_port_post(struct lpfc_hba *phba)
                        lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
                                        "0352 Config MSI mailbox command "
                                        "failed, mbxCmd x%x, mbxStatus x%x\n",
-                                       pmb->mb.mbxCommand, pmb->mb.mbxStatus);
+                                       pmb->u.mb.mbxCommand,
+                                       pmb->u.mb.mbxStatus);
                        mempool_free(pmb, phba->mbox_mem_pool);
                        return -EIO;
                }
        }
 
+       spin_lock_irq(&phba->hbalock);
        /* Initialize ERATT handling flag */
        phba->hba_flag &= ~HBA_ERATT_HANDLED;
 
        /* Enable appropriate host interrupts */
-       spin_lock_irq(&phba->hbalock);
        status = readl(phba->HCregaddr);
        status |= HC_MBINT_ENA | HC_ERINT_ENA | HC_LAINT_ENA;
        if (psli->num_rings > 0)
@@ -571,16 +594,20 @@ lpfc_hba_down_prep(struct lpfc_hba *phba)
 {
        struct lpfc_vport **vports;
        int i;
-       /* Disable interrupts */
-       writel(0, phba->HCregaddr);
-       readl(phba->HCregaddr); /* flush */
+
+       if (phba->sli_rev <= LPFC_SLI_REV3) {
+               /* Disable interrupts */
+               writel(0, phba->HCregaddr);
+               readl(phba->HCregaddr); /* flush */
+       }
 
        if (phba->pport->load_flag & FC_UNLOADING)
                lpfc_cleanup_discovery_resources(phba->pport);
        else {
                vports = lpfc_create_vport_work_array(phba);
                if (vports != NULL)
-                       for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++)
+                       for (i = 0; i <= phba->max_vports &&
+                               vports[i] != NULL; i++)
                                lpfc_cleanup_discovery_resources(vports[i]);
                lpfc_destroy_vport_work_array(phba, vports);
        }
@@ -588,7 +615,7 @@ lpfc_hba_down_prep(struct lpfc_hba *phba)
 }
 
 /**
- * lpfc_hba_down_post - Perform lpfc uninitialization after HBA reset
+ * lpfc_hba_down_post_s3 - Perform lpfc uninitialization after HBA reset
  * @phba: pointer to lpfc HBA data structure.
  *
  * This routine will do uninitialization after the HBA is reset when bring
@@ -598,8 +625,8 @@ lpfc_hba_down_prep(struct lpfc_hba *phba)
  *   0 - sucess.
  *   Any other value - error.
  **/
-int
-lpfc_hba_down_post(struct lpfc_hba *phba)
+static int
+lpfc_hba_down_post_s3(struct lpfc_hba *phba)
 {
        struct lpfc_sli *psli = &phba->sli;
        struct lpfc_sli_ring *pring;
@@ -642,6 +669,77 @@ lpfc_hba_down_post(struct lpfc_hba *phba)
 
        return 0;
 }
+/**
+ * lpfc_hba_down_post_s4 - Perform lpfc uninitialization after HBA reset
+ * @phba: pointer to lpfc HBA data structure.
+ *
+ * This routine will do uninitialization after the HBA is reset when bring
+ * down the SLI Layer.
+ *
+ * Return codes
+ *   0 - sucess.
+ *   Any other value - error.
+ **/
+static int
+lpfc_hba_down_post_s4(struct lpfc_hba *phba)
+{
+       struct lpfc_scsi_buf *psb, *psb_next;
+       LIST_HEAD(aborts);
+       int ret;
+       unsigned long iflag = 0;
+       ret = lpfc_hba_down_post_s3(phba);
+       if (ret)
+               return ret;
+       /* At this point in time the HBA is either reset or DOA. Either
+        * way, nothing should be on lpfc_abts_els_sgl_list, it needs to be
+        * on the lpfc_sgl_list so that it can either be freed if the
+        * driver is unloading or reposted if the driver is restarting
+        * the port.
+        */
+       spin_lock_irq(&phba->hbalock);  /* required for lpfc_sgl_list and */
+                                       /* scsl_buf_list */
+       /* abts_sgl_list_lock required because worker thread uses this
+        * list.
+        */
+       spin_lock(&phba->sli4_hba.abts_sgl_list_lock);
+       list_splice_init(&phba->sli4_hba.lpfc_abts_els_sgl_list,
+                       &phba->sli4_hba.lpfc_sgl_list);
+       spin_unlock(&phba->sli4_hba.abts_sgl_list_lock);
+       /* abts_scsi_buf_list_lock required because worker thread uses this
+        * list.
+        */
+       spin_lock(&phba->sli4_hba.abts_scsi_buf_list_lock);
+       list_splice_init(&phba->sli4_hba.lpfc_abts_scsi_buf_list,
+                       &aborts);
+       spin_unlock(&phba->sli4_hba.abts_scsi_buf_list_lock);
+       spin_unlock_irq(&phba->hbalock);
+
+       list_for_each_entry_safe(psb, psb_next, &aborts, list) {
+               psb->pCmd = NULL;
+               psb->status = IOSTAT_SUCCESS;
+       }
+       spin_lock_irqsave(&phba->scsi_buf_list_lock, iflag);
+       list_splice(&aborts, &phba->lpfc_scsi_buf_list);
+       spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag);
+       return 0;
+}
+
+/**
+ * lpfc_hba_down_post - Wrapper func for hba down post routine
+ * @phba: pointer to lpfc HBA data structure.
+ *
+ * This routine wraps the actual SLI3 or SLI4 routine for performing
+ * uninitialization after the HBA is reset when bring down the SLI Layer.
+ *
+ * Return codes
+ *   0 - sucess.
+ *   Any other value - error.
+ **/
+int
+lpfc_hba_down_post(struct lpfc_hba *phba)
+{
+       return (*phba->lpfc_hba_down_post)(phba);
+}
 
 /**
  * lpfc_hb_timeout - The HBA-timer timeout handler
@@ -809,7 +907,7 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
                                        "taking this port offline.\n");
 
                        spin_lock_irq(&phba->hbalock);
-                       psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
+                       psli->sli_flag &= ~LPFC_SLI_ACTIVE;
                        spin_unlock_irq(&phba->hbalock);
 
                        lpfc_offline_prep(phba);
@@ -834,13 +932,15 @@ lpfc_offline_eratt(struct lpfc_hba *phba)
        struct lpfc_sli   *psli = &phba->sli;
 
        spin_lock_irq(&phba->hbalock);
-       psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
+       psli->sli_flag &= ~LPFC_SLI_ACTIVE;
        spin_unlock_irq(&phba->hbalock);
        lpfc_offline_prep(phba);
 
        lpfc_offline(phba);
        lpfc_reset_barrier(phba);
+       spin_lock_irq(&phba->hbalock);
        lpfc_sli_brdreset(phba);
+       spin_unlock_irq(&phba->hbalock);
        lpfc_hba_down_post(phba);
        lpfc_sli_brdready(phba, HS_MBRDY);
        lpfc_unblock_mgmt_io(phba);
@@ -848,6 +948,25 @@ lpfc_offline_eratt(struct lpfc_hba *phba)
        return;
 }
 
+/**
+ * lpfc_sli4_offline_eratt - Bring lpfc offline on SLI4 hardware error attention
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is called to bring a SLI4 HBA offline when HBA hardware error
+ * other than Port Error 6 has been detected.
+ **/
+static void
+lpfc_sli4_offline_eratt(struct lpfc_hba *phba)
+{
+       lpfc_offline_prep(phba);
+       lpfc_offline(phba);
+       lpfc_sli4_brdreset(phba);
+       lpfc_hba_down_post(phba);
+       lpfc_sli4_post_status_check(phba);
+       lpfc_unblock_mgmt_io(phba);
+       phba->link_state = LPFC_HBA_ERROR;
+}
+
 /**
  * lpfc_handle_deferred_eratt - The HBA hardware deferred error handler
  * @phba: pointer to lpfc hba data structure.
@@ -864,6 +983,16 @@ lpfc_handle_deferred_eratt(struct lpfc_hba *phba)
        struct lpfc_sli_ring  *pring;
        struct lpfc_sli *psli = &phba->sli;
 
+       /* If the pci channel is offline, ignore possible errors,
+        * since we cannot communicate with the pci card anyway.
+        */
+       if (pci_channel_offline(phba->pcidev)) {
+               spin_lock_irq(&phba->hbalock);
+               phba->hba_flag &= ~DEFER_ERATT;
+               spin_unlock_irq(&phba->hbalock);
+               return;
+       }
+
        lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
                "0479 Deferred Adapter Hardware Error "
                "Data: x%x x%x x%x\n",
@@ -871,7 +1000,7 @@ lpfc_handle_deferred_eratt(struct lpfc_hba *phba)
                phba->work_status[0], phba->work_status[1]);
 
        spin_lock_irq(&phba->hbalock);
-       psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
+       psli->sli_flag &= ~LPFC_SLI_ACTIVE;
        spin_unlock_irq(&phba->hbalock);
 
 
@@ -909,13 +1038,30 @@ lpfc_handle_deferred_eratt(struct lpfc_hba *phba)
        if ((!phba->work_hs) && (!(phba->pport->load_flag & FC_UNLOADING)))
                phba->work_hs = old_host_status & ~HS_FFER1;
 
+       spin_lock_irq(&phba->hbalock);
        phba->hba_flag &= ~DEFER_ERATT;
+       spin_unlock_irq(&phba->hbalock);
        phba->work_status[0] = readl(phba->MBslimaddr + 0xa8);
        phba->work_status[1] = readl(phba->MBslimaddr + 0xac);
 }
 
+static void
+lpfc_board_errevt_to_mgmt(struct lpfc_hba *phba)
+{
+       struct lpfc_board_event_header board_event;
+       struct Scsi_Host *shost;
+
+       board_event.event_type = FC_REG_BOARD_EVENT;
+       board_event.subcategory = LPFC_EVENT_PORTINTERR;
+       shost = lpfc_shost_from_vport(phba->pport);
+       fc_host_post_vendor_event(shost, fc_get_event_number(),
+                                 sizeof(board_event),
+                                 (char *) &board_event,
+                                 LPFC_NL_VENDOR_ID);
+}
+
 /**
- * lpfc_handle_eratt - The HBA hardware error handler
+ * lpfc_handle_eratt_s3 - The SLI3 HBA hardware error handler
  * @phba: pointer to lpfc hba data structure.
  *
  * This routine is invoked to handle the following HBA hardware error
@@ -924,8 +1070,8 @@ lpfc_handle_deferred_eratt(struct lpfc_hba *phba)
  * 2 - DMA ring index out of range
  * 3 - Mailbox command came back as unknown
  **/
-void
-lpfc_handle_eratt(struct lpfc_hba *phba)
+static void
+lpfc_handle_eratt_s3(struct lpfc_hba *phba)
 {
        struct lpfc_vport *vport = phba->pport;
        struct lpfc_sli   *psli = &phba->sli;
@@ -934,24 +1080,23 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
        unsigned long temperature;
        struct temp_event temp_event_data;
        struct Scsi_Host  *shost;
-       struct lpfc_board_event_header board_event;
 
        /* If the pci channel is offline, ignore possible errors,
-        * since we cannot communicate with the pci card anyway. */
-       if (pci_channel_offline(phba->pcidev))
+        * since we cannot communicate with the pci card anyway.
+        */
+       if (pci_channel_offline(phba->pcidev)) {
+               spin_lock_irq(&phba->hbalock);
+               phba->hba_flag &= ~DEFER_ERATT;
+               spin_unlock_irq(&phba->hbalock);
                return;
+       }
+
        /* If resets are disabled then leave the HBA alone and return */
        if (!phba->cfg_enable_hba_reset)
                return;
 
        /* Send an internal error event to mgmt application */
-       board_event.event_type = FC_REG_BOARD_EVENT;
-       board_event.subcategory = LPFC_EVENT_PORTINTERR;
-       shost = lpfc_shost_from_vport(phba->pport);
-       fc_host_post_vendor_event(shost, fc_get_event_number(),
-                                 sizeof(board_event),
-                                 (char *) &board_event,
-                                 LPFC_NL_VENDOR_ID);
+       lpfc_board_errevt_to_mgmt(phba);
 
        if (phba->hba_flag & DEFER_ERATT)
                lpfc_handle_deferred_eratt(phba);
@@ -965,7 +1110,7 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
                                phba->work_status[0], phba->work_status[1]);
 
                spin_lock_irq(&phba->hbalock);
-               psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
+               psli->sli_flag &= ~LPFC_SLI_ACTIVE;
                spin_unlock_irq(&phba->hbalock);
 
                /*
@@ -1036,6 +1181,65 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
        return;
 }
 
+/**
+ * lpfc_handle_eratt_s4 - The SLI4 HBA hardware error handler
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to handle the SLI4 HBA hardware error attention
+ * conditions.
+ **/
+static void
+lpfc_handle_eratt_s4(struct lpfc_hba *phba)
+{
+       struct lpfc_vport *vport = phba->pport;
+       uint32_t event_data;
+       struct Scsi_Host *shost;
+
+       /* If the pci channel is offline, ignore possible errors, since
+        * we cannot communicate with the pci card anyway.
+        */
+       if (pci_channel_offline(phba->pcidev))
+               return;
+       /* If resets are disabled then leave the HBA alone and return */
+       if (!phba->cfg_enable_hba_reset)
+               return;
+
+       /* Send an internal error event to mgmt application */
+       lpfc_board_errevt_to_mgmt(phba);
+
+       /* For now, the actual action for SLI4 device handling is not
+        * specified yet, just treated it as adaptor hardware failure
+        */
+       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                       "0143 SLI4 Adapter Hardware Error Data: x%x x%x\n",
+                       phba->work_status[0], phba->work_status[1]);
+
+       event_data = FC_REG_DUMP_EVENT;
+       shost = lpfc_shost_from_vport(vport);
+       fc_host_post_vendor_event(shost, fc_get_event_number(),
+                                 sizeof(event_data), (char *) &event_data,
+                                 SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+
+       lpfc_sli4_offline_eratt(phba);
+}
+
+/**
+ * lpfc_handle_eratt - Wrapper func for handling hba error attention
+ * @phba: pointer to lpfc HBA data structure.
+ *
+ * This routine wraps the actual SLI3 or SLI4 hba error attention handling
+ * routine from the API jump table function pointer from the lpfc_hba struct.
+ *
+ * Return codes
+ *   0 - sucess.
+ *   Any other value - error.
+ **/
+void
+lpfc_handle_eratt(struct lpfc_hba *phba)
+{
+       (*phba->lpfc_handle_eratt)(phba);
+}
+
 /**
  * lpfc_handle_latt - The HBA link event handler
  * @phba: pointer to lpfc hba data structure.
@@ -1137,7 +1341,7 @@ lpfc_handle_latt_err_exit:
  *   0 - pointer to the VPD passed in is NULL
  *   1 - success
  **/
-static int
+int
 lpfc_parse_vpd(struct lpfc_hba *phba, uint8_t *vpd, int len)
 {
        uint8_t lenlo, lenhi;
@@ -1292,6 +1496,7 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
        uint16_t dev_id = phba->pcidev->device;
        int max_speed;
        int GE = 0;
+       int oneConnect = 0; /* default is not a oneConnect */
        struct {
                char * name;
                int    max_speed;
@@ -1437,6 +1642,14 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
        case PCI_DEVICE_ID_PROTEUS_S:
                m = (typeof(m)) {"LPemv12002-S", max_speed, "PCIe IOV"};
                break;
+       case PCI_DEVICE_ID_TIGERSHARK:
+               oneConnect = 1;
+               m = (typeof(m)) {"OCe10100-F", max_speed, "PCIe"};
+               break;
+       case PCI_DEVICE_ID_TIGERSHARK_S:
+               oneConnect = 1;
+               m = (typeof(m)) {"OCe10100-F-S", max_speed, "PCIe"};
+               break;
        default:
                m = (typeof(m)){ NULL };
                break;
@@ -1444,13 +1657,24 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
 
        if (mdp && mdp[0] == '\0')
                snprintf(mdp, 79,"%s", m.name);
-       if (descp && descp[0] == '\0')
-               snprintf(descp, 255,
-                       "Emulex %s %d%s %s %s",
-                       m.name, m.max_speed,
-                       (GE) ? "GE" : "Gb",
-                       m.bus,
-                       (GE) ? "FCoE Adapter" : "Fibre Channel Adapter");
+       /* oneConnect hba requires special processing, they are all initiators
+        * and we put the port number on the end
+        */
+       if (descp && descp[0] == '\0') {
+               if (oneConnect)
+                       snprintf(descp, 255,
+                               "Emulex OneConnect %s, FCoE Initiator, Port %s",
+                               m.name,
+                               phba->Port);
+               else
+                       snprintf(descp, 255,
+                               "Emulex %s %d%s %s %s",
+                               m.name, m.max_speed,
+                               (GE) ? "GE" : "Gb",
+                               m.bus,
+                               (GE) ? "FCoE Adapter" :
+                                       "Fibre Channel Adapter");
+       }
 }
 
 /**
@@ -1533,7 +1757,8 @@ lpfc_post_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, int cnt)
                icmd->ulpCommand = CMD_QUE_RING_BUF64_CN;
                icmd->ulpLe = 1;
 
-               if (lpfc_sli_issue_iocb(phba, pring, iocb, 0) == IOCB_ERROR) {
+               if (lpfc_sli_issue_iocb(phba, pring->ringno, iocb, 0) ==
+                   IOCB_ERROR) {
                        lpfc_mbuf_free(phba, mp1->virt, mp1->phys);
                        kfree(mp1);
                        cnt++;
@@ -1761,7 +1986,6 @@ lpfc_cleanup(struct lpfc_vport *vport)
         * Lets wait for this to happen, if needed.
         */
        while (!list_empty(&vport->fc_nodes)) {
-
                if (i++ > 3000) {
                        lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
                                "0233 Nodelist not empty\n");
@@ -1782,7 +2006,6 @@ lpfc_cleanup(struct lpfc_vport *vport)
                /* Wait for any activity on ndlps to settle */
                msleep(10);
        }
-       return;
 }
 
 /**
@@ -1803,22 +2026,36 @@ lpfc_stop_vport_timers(struct lpfc_vport *vport)
 }
 
 /**
- * lpfc_stop_phba_timers - Stop all the timers associated with an HBA
+ * lpfc_stop_hba_timers - Stop all the timers associated with an HBA
  * @phba: pointer to lpfc hba data structure.
  *
  * This routine stops all the timers associated with a HBA. This function is
  * invoked before either putting a HBA offline or unloading the driver.
  **/
-static void
-lpfc_stop_phba_timers(struct lpfc_hba *phba)
+void
+lpfc_stop_hba_timers(struct lpfc_hba *phba)
 {
-       del_timer_sync(&phba->fcp_poll_timer);
        lpfc_stop_vport_timers(phba->pport);
        del_timer_sync(&phba->sli.mbox_tmo);
        del_timer_sync(&phba->fabric_block_timer);
-       phba->hb_outstanding = 0;
-       del_timer_sync(&phba->hb_tmofunc);
        del_timer_sync(&phba->eratt_poll);
+       del_timer_sync(&phba->hb_tmofunc);
+       phba->hb_outstanding = 0;
+
+       switch (phba->pci_dev_grp) {
+       case LPFC_PCI_DEV_LP:
+               /* Stop any LightPulse device specific driver timers */
+               del_timer_sync(&phba->fcp_poll_timer);
+               break;
+       case LPFC_PCI_DEV_OC:
+               /* Stop any OneConnect device sepcific driver timers */
+               break;
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0297 Invalid device group (x%x)\n",
+                               phba->pci_dev_grp);
+               break;
+       }
        return;
 }
 
@@ -1878,14 +2115,21 @@ lpfc_online(struct lpfc_hba *phba)
                return 1;
        }
 
-       if (lpfc_sli_hba_setup(phba)) { /* Initialize the HBA */
-               lpfc_unblock_mgmt_io(phba);
-               return 1;
+       if (phba->sli_rev == LPFC_SLI_REV4) {
+               if (lpfc_sli4_hba_setup(phba)) { /* Initialize SLI4 HBA */
+                       lpfc_unblock_mgmt_io(phba);
+                       return 1;
+               }
+       } else {
+               if (lpfc_sli_hba_setup(phba)) { /* Initialize SLI2/SLI3 HBA */
+                       lpfc_unblock_mgmt_io(phba);
+                       return 1;
+               }
        }
 
        vports = lpfc_create_vport_work_array(phba);
        if (vports != NULL)
-               for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+               for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
                        struct Scsi_Host *shost;
                        shost = lpfc_shost_from_vport(vports[i]);
                        spin_lock_irq(shost->host_lock);
@@ -1947,11 +2191,12 @@ lpfc_offline_prep(struct lpfc_hba * phba)
        /* Issue an unreg_login to all nodes on all vports */
        vports = lpfc_create_vport_work_array(phba);
        if (vports != NULL) {
-               for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+               for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
                        struct Scsi_Host *shost;
 
                        if (vports[i]->load_flag & FC_UNLOADING)
                                continue;
+                       vports[i]->vfi_state &= ~LPFC_VFI_REGISTERED;
                        shost = lpfc_shost_from_vport(vports[i]);
                        list_for_each_entry_safe(ndlp, next_ndlp,
                                                 &vports[i]->fc_nodes,
@@ -1975,7 +2220,7 @@ lpfc_offline_prep(struct lpfc_hba * phba)
        }
        lpfc_destroy_vport_work_array(phba, vports);
 
-       lpfc_sli_flush_mbox_queue(phba);
+       lpfc_sli_mbox_sys_shutdown(phba);
 }
 
 /**
@@ -1996,11 +2241,11 @@ lpfc_offline(struct lpfc_hba *phba)
        if (phba->pport->fc_flag & FC_OFFLINE_MODE)
                return;
 
-       /* stop all timers associated with this hba */
-       lpfc_stop_phba_timers(phba);
+       /* stop port and all timers associated with this hba */
+       lpfc_stop_port(phba);
        vports = lpfc_create_vport_work_array(phba);
        if (vports != NULL)
-               for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++)
+               for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++)
                        lpfc_stop_vport_timers(vports[i]);
        lpfc_destroy_vport_work_array(phba, vports);
        lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
@@ -2013,7 +2258,7 @@ lpfc_offline(struct lpfc_hba *phba)
        spin_unlock_irq(&phba->hbalock);
        vports = lpfc_create_vport_work_array(phba);
        if (vports != NULL)
-               for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+               for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
                        shost = lpfc_shost_from_vport(vports[i]);
                        spin_lock_irq(shost->host_lock);
                        vports[i]->work_port_events = 0;
@@ -2106,6 +2351,10 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
        shost->max_lun = vport->cfg_max_luns;
        shost->this_id = -1;
        shost->max_cmd_len = 16;
+       if (phba->sli_rev == LPFC_SLI_REV4) {
+               shost->dma_boundary = LPFC_SLI4_MAX_SEGMENT_SIZE;
+               shost->sg_tablesize = phba->cfg_sg_seg_cnt;
+       }
 
        /*
         * Set initial can_queue value since 0 is no longer supported and
@@ -2123,6 +2372,7 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
 
        /* Initialize all internally managed lists. */
        INIT_LIST_HEAD(&vport->fc_nodes);
+       INIT_LIST_HEAD(&vport->rcv_buffer_list);
        spin_lock_init(&vport->work_port_lock);
 
        init_timer(&vport->fc_disctmo);
@@ -2314,192 +2564,501 @@ void lpfc_host_attrib_init(struct Scsi_Host *shost)
 }
 
 /**
- * lpfc_enable_msix - Enable MSI-X interrupt mode
+ * lpfc_stop_port_s3 - Stop SLI3 device port
  * @phba: pointer to lpfc hba data structure.
  *
- * This routine is invoked to enable the MSI-X interrupt vectors. The kernel
- * function pci_enable_msix() is called to enable the MSI-X vectors. Note that
- * pci_enable_msix(), once invoked, enables either all or nothing, depending
- * on the current availability of PCI vector resources. The device driver is
- * responsible for calling the individual request_irq() to register each MSI-X
- * vector with a interrupt handler, which is done in this function. Note that
- * later when device is unloading, the driver should always call free_irq()
- * on all MSI-X vectors it has done request_irq() on before calling
- * pci_disable_msix(). Failure to do so results in a BUG_ON() and a device
- * will be left with MSI-X enabled and leaks its vectors.
- *
- * Return codes
- *   0 - sucessful
- *   other values - error
+ * This routine is invoked to stop an SLI3 device port, it stops the device
+ * from generating interrupts and stops the device driver's timers for the
+ * device.
  **/
-static int
-lpfc_enable_msix(struct lpfc_hba *phba)
+static void
+lpfc_stop_port_s3(struct lpfc_hba *phba)
 {
-       int rc, i;
-       LPFC_MBOXQ_t *pmb;
+       /* Clear all interrupt enable conditions */
+       writel(0, phba->HCregaddr);
+       readl(phba->HCregaddr); /* flush */
+       /* Clear all pending interrupts */
+       writel(0xffffffff, phba->HAregaddr);
+       readl(phba->HAregaddr); /* flush */
 
-       /* Set up MSI-X multi-message vectors */
-       for (i = 0; i < LPFC_MSIX_VECTORS; i++)
-               phba->msix_entries[i].entry = i;
+       /* Reset some HBA SLI setup states */
+       lpfc_stop_hba_timers(phba);
+       phba->pport->work_port_events = 0;
+}
 
-       /* Configure MSI-X capability structure */
-       rc = pci_enable_msix(phba->pcidev, phba->msix_entries,
-                               ARRAY_SIZE(phba->msix_entries));
-       if (rc) {
-               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
-                               "0420 PCI enable MSI-X failed (%d)\n", rc);
-               goto msi_fail_out;
-       } else
-               for (i = 0; i < LPFC_MSIX_VECTORS; i++)
-                       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
-                                       "0477 MSI-X entry[%d]: vector=x%x "
-                                       "message=%d\n", i,
-                                       phba->msix_entries[i].vector,
-                                       phba->msix_entries[i].entry);
-       /*
-        * Assign MSI-X vectors to interrupt handlers
-        */
+/**
+ * lpfc_stop_port_s4 - Stop SLI4 device port
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to stop an SLI4 device port, it stops the device
+ * from generating interrupts and stops the device driver's timers for the
+ * device.
+ **/
+static void
+lpfc_stop_port_s4(struct lpfc_hba *phba)
+{
+       /* Reset some HBA SLI4 setup states */
+       lpfc_stop_hba_timers(phba);
+       phba->pport->work_port_events = 0;
+       phba->sli4_hba.intr_enable = 0;
+       /* Hard clear it for now, shall have more graceful way to wait later */
+       phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+}
 
-       /* vector-0 is associated to slow-path handler */
-       rc = request_irq(phba->msix_entries[0].vector, &lpfc_sp_intr_handler,
-                        IRQF_SHARED, LPFC_SP_DRIVER_HANDLER_NAME, phba);
-       if (rc) {
-               lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
-                               "0421 MSI-X slow-path request_irq failed "
-                               "(%d)\n", rc);
-               goto msi_fail_out;
-       }
+/**
+ * lpfc_stop_port - Wrapper function for stopping hba port
+ * @phba: Pointer to HBA context object.
+ *
+ * This routine wraps the actual SLI3 or SLI4 hba stop port routine from
+ * the API jump table function pointer from the lpfc_hba struct.
+ **/
+void
+lpfc_stop_port(struct lpfc_hba *phba)
+{
+       phba->lpfc_stop_port(phba);
+}
 
-       /* vector-1 is associated to fast-path handler */
-       rc = request_irq(phba->msix_entries[1].vector, &lpfc_fp_intr_handler,
-                        IRQF_SHARED, LPFC_FP_DRIVER_HANDLER_NAME, phba);
+/**
+ * lpfc_sli4_remove_dflt_fcf - Remove the driver default fcf record from the port.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to remove the driver default fcf record from
+ * the port.  This routine currently acts on FCF Index 0.
+ *
+ **/
+void
+lpfc_sli_remove_dflt_fcf(struct lpfc_hba *phba)
+{
+       int rc = 0;
+       LPFC_MBOXQ_t *mboxq;
+       struct lpfc_mbx_del_fcf_tbl_entry *del_fcf_record;
+       uint32_t mbox_tmo, req_len;
+       uint32_t shdr_status, shdr_add_status;
 
-       if (rc) {
-               lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
-                               "0429 MSI-X fast-path request_irq failed "
-                               "(%d)\n", rc);
-               goto irq_fail_out;
+       mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mboxq) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                       "2020 Failed to allocate mbox for ADD_FCF cmd\n");
+               return;
        }
 
+       req_len = sizeof(struct lpfc_mbx_del_fcf_tbl_entry) -
+                 sizeof(struct lpfc_sli4_cfg_mhdr);
+       rc = lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_FCOE,
+                             LPFC_MBOX_OPCODE_FCOE_DELETE_FCF,
+                             req_len, LPFC_SLI4_MBX_EMBED);
        /*
-        * Configure HBA MSI-X attention conditions to messages
+        * In phase 1, there is a single FCF index, 0.  In phase2, the driver
+        * supports multiple FCF indices.
         */
-       pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       del_fcf_record = &mboxq->u.mqe.un.del_fcf_entry;
+       bf_set(lpfc_mbx_del_fcf_tbl_count, del_fcf_record, 1);
+       bf_set(lpfc_mbx_del_fcf_tbl_index, del_fcf_record,
+              phba->fcf.fcf_indx);
 
-       if (!pmb) {
-               rc = -ENOMEM;
-               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
-                               "0474 Unable to allocate memory for issuing "
-                               "MBOX_CONFIG_MSI command\n");
-               goto mem_fail_out;
+       if (!phba->sli4_hba.intr_enable)
+               rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+       else {
+               mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
+               rc = lpfc_sli_issue_mbox_wait(phba, mboxq, mbox_tmo);
        }
-       rc = lpfc_config_msi(phba, pmb);
-       if (rc)
-               goto mbx_fail_out;
-       rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
-       if (rc != MBX_SUCCESS) {
-               lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
-                               "0351 Config MSI mailbox command failed, "
-                               "mbxCmd x%x, mbxStatus x%x\n",
-                               pmb->mb.mbxCommand, pmb->mb.mbxStatus);
-               goto mbx_fail_out;
+       /* The IOCTL status is embedded in the mailbox subheader. */
+       shdr_status = bf_get(lpfc_mbox_hdr_status,
+                            &del_fcf_record->header.cfg_shdr.response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,
+                                &del_fcf_record->header.cfg_shdr.response);
+       if (shdr_status || shdr_add_status || rc != MBX_SUCCESS) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "2516 DEL FCF of default FCF Index failed "
+                               "mbx status x%x, status x%x add_status x%x\n",
+                               rc, shdr_status, shdr_add_status);
        }
+       if (rc != MBX_TIMEOUT)
+               mempool_free(mboxq, phba->mbox_mem_pool);
+}
 
-       /* Free memory allocated for mailbox command */
-       mempool_free(pmb, phba->mbox_mem_pool);
-       return rc;
+/**
+ * lpfc_sli4_parse_latt_fault - Parse sli4 link-attention link fault code
+ * @phba: pointer to lpfc hba data structure.
+ * @acqe_link: pointer to the async link completion queue entry.
+ *
+ * This routine is to parse the SLI4 link-attention link fault code and
+ * translate it into the base driver's read link attention mailbox command
+ * status.
+ *
+ * Return: Link-attention status in terms of base driver's coding.
+ **/
+static uint16_t
+lpfc_sli4_parse_latt_fault(struct lpfc_hba *phba,
+                          struct lpfc_acqe_link *acqe_link)
+{
+       uint16_t latt_fault;
 
-mbx_fail_out:
-       /* Free memory allocated for mailbox command */
-       mempool_free(pmb, phba->mbox_mem_pool);
+       switch (bf_get(lpfc_acqe_link_fault, acqe_link)) {
+       case LPFC_ASYNC_LINK_FAULT_NONE:
+       case LPFC_ASYNC_LINK_FAULT_LOCAL:
+       case LPFC_ASYNC_LINK_FAULT_REMOTE:
+               latt_fault = 0;
+               break;
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0398 Invalid link fault code: x%x\n",
+                               bf_get(lpfc_acqe_link_fault, acqe_link));
+               latt_fault = MBXERR_ERROR;
+               break;
+       }
+       return latt_fault;
+}
 
-mem_fail_out:
-       /* free the irq already requested */
-       free_irq(phba->msix_entries[1].vector, phba);
+/**
+ * lpfc_sli4_parse_latt_type - Parse sli4 link attention type
+ * @phba: pointer to lpfc hba data structure.
+ * @acqe_link: pointer to the async link completion queue entry.
+ *
+ * This routine is to parse the SLI4 link attention type and translate it
+ * into the base driver's link attention type coding.
+ *
+ * Return: Link attention type in terms of base driver's coding.
+ **/
+static uint8_t
+lpfc_sli4_parse_latt_type(struct lpfc_hba *phba,
+                         struct lpfc_acqe_link *acqe_link)
+{
+       uint8_t att_type;
 
-irq_fail_out:
-       /* free the irq already requested */
-       free_irq(phba->msix_entries[0].vector, phba);
+       switch (bf_get(lpfc_acqe_link_status, acqe_link)) {
+       case LPFC_ASYNC_LINK_STATUS_DOWN:
+       case LPFC_ASYNC_LINK_STATUS_LOGICAL_DOWN:
+               att_type = AT_LINK_DOWN;
+               break;
+       case LPFC_ASYNC_LINK_STATUS_UP:
+               /* Ignore physical link up events - wait for logical link up */
+               att_type = AT_RESERVED;
+               break;
+       case LPFC_ASYNC_LINK_STATUS_LOGICAL_UP:
+               att_type = AT_LINK_UP;
+               break;
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0399 Invalid link attention type: x%x\n",
+                               bf_get(lpfc_acqe_link_status, acqe_link));
+               att_type = AT_RESERVED;
+               break;
+       }
+       return att_type;
+}
 
-msi_fail_out:
-       /* Unconfigure MSI-X capability structure */
-       pci_disable_msix(phba->pcidev);
-       return rc;
+/**
+ * lpfc_sli4_parse_latt_link_speed - Parse sli4 link-attention link speed
+ * @phba: pointer to lpfc hba data structure.
+ * @acqe_link: pointer to the async link completion queue entry.
+ *
+ * This routine is to parse the SLI4 link-attention link speed and translate
+ * it into the base driver's link-attention link speed coding.
+ *
+ * Return: Link-attention link speed in terms of base driver's coding.
+ **/
+static uint8_t
+lpfc_sli4_parse_latt_link_speed(struct lpfc_hba *phba,
+                               struct lpfc_acqe_link *acqe_link)
+{
+       uint8_t link_speed;
+
+       switch (bf_get(lpfc_acqe_link_speed, acqe_link)) {
+       case LPFC_ASYNC_LINK_SPEED_ZERO:
+               link_speed = LA_UNKNW_LINK;
+               break;
+       case LPFC_ASYNC_LINK_SPEED_10MBPS:
+               link_speed = LA_UNKNW_LINK;
+               break;
+       case LPFC_ASYNC_LINK_SPEED_100MBPS:
+               link_speed = LA_UNKNW_LINK;
+               break;
+       case LPFC_ASYNC_LINK_SPEED_1GBPS:
+               link_speed = LA_1GHZ_LINK;
+               break;
+       case LPFC_ASYNC_LINK_SPEED_10GBPS:
+               link_speed = LA_10GHZ_LINK;
+               break;
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0483 Invalid link-attention link speed: x%x\n",
+                               bf_get(lpfc_acqe_link_speed, acqe_link));
+               link_speed = LA_UNKNW_LINK;
+               break;
+       }
+       return link_speed;
 }
 
 /**
- * lpfc_disable_msix - Disable MSI-X interrupt mode
+ * lpfc_sli4_async_link_evt - Process the asynchronous link event
  * @phba: pointer to lpfc hba data structure.
+ * @acqe_link: pointer to the async link completion queue entry.
  *
- * This routine is invoked to release the MSI-X vectors and then disable the
- * MSI-X interrupt mode.
+ * This routine is to handle the SLI4 asynchronous link event.
  **/
 static void
-lpfc_disable_msix(struct lpfc_hba *phba)
+lpfc_sli4_async_link_evt(struct lpfc_hba *phba,
+                        struct lpfc_acqe_link *acqe_link)
 {
-       int i;
+       struct lpfc_dmabuf *mp;
+       LPFC_MBOXQ_t *pmb;
+       MAILBOX_t *mb;
+       READ_LA_VAR *la;
+       uint8_t att_type;
 
-       /* Free up MSI-X multi-message vectors */
-       for (i = 0; i < LPFC_MSIX_VECTORS; i++)
-               free_irq(phba->msix_entries[i].vector, phba);
-       /* Disable MSI-X */
-       pci_disable_msix(phba->pcidev);
+       att_type = lpfc_sli4_parse_latt_type(phba, acqe_link);
+       if (att_type != AT_LINK_DOWN && att_type != AT_LINK_UP)
+               return;
+       pmb = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!pmb) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0395 The mboxq allocation failed\n");
+               return;
+       }
+       mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+       if (!mp) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0396 The lpfc_dmabuf allocation failed\n");
+               goto out_free_pmb;
+       }
+       mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys);
+       if (!mp->virt) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0397 The mbuf allocation failed\n");
+               goto out_free_dmabuf;
+       }
+
+       /* Cleanup any outstanding ELS commands */
+       lpfc_els_flush_all_cmd(phba);
+
+       /* Block ELS IOCBs until we have done process link event */
+       phba->sli.ring[LPFC_ELS_RING].flag |= LPFC_STOP_IOCB_EVENT;
+
+       /* Update link event statistics */
+       phba->sli.slistat.link_event++;
+
+       /* Create pseudo lpfc_handle_latt mailbox command from link ACQE */
+       lpfc_read_la(phba, pmb, mp);
+       pmb->vport = phba->pport;
+
+       /* Parse and translate status field */
+       mb = &pmb->u.mb;
+       mb->mbxStatus = lpfc_sli4_parse_latt_fault(phba, acqe_link);
+
+       /* Parse and translate link attention fields */
+       la = (READ_LA_VAR *) &pmb->u.mb.un.varReadLA;
+       la->eventTag = acqe_link->event_tag;
+       la->attType = att_type;
+       la->UlnkSpeed = lpfc_sli4_parse_latt_link_speed(phba, acqe_link);
+
+       /* Fake the the following irrelvant fields */
+       la->topology = TOPOLOGY_PT_PT;
+       la->granted_AL_PA = 0;
+       la->il = 0;
+       la->pb = 0;
+       la->fa = 0;
+       la->mm = 0;
+
+       /* Keep the link status for extra SLI4 state machine reference */
+       phba->sli4_hba.link_state.speed =
+                               bf_get(lpfc_acqe_link_speed, acqe_link);
+       phba->sli4_hba.link_state.duplex =
+                               bf_get(lpfc_acqe_link_duplex, acqe_link);
+       phba->sli4_hba.link_state.status =
+                               bf_get(lpfc_acqe_link_status, acqe_link);
+       phba->sli4_hba.link_state.physical =
+                               bf_get(lpfc_acqe_link_physical, acqe_link);
+       phba->sli4_hba.link_state.fault =
+                               bf_get(lpfc_acqe_link_fault, acqe_link);
+
+       /* Invoke the lpfc_handle_latt mailbox command callback function */
+       lpfc_mbx_cmpl_read_la(phba, pmb);
+
+       return;
+
+out_free_dmabuf:
+       kfree(mp);
+out_free_pmb:
+       mempool_free(pmb, phba->mbox_mem_pool);
 }
 
 /**
- * lpfc_enable_msi - Enable MSI interrupt mode
+ * lpfc_sli4_async_fcoe_evt - Process the asynchronous fcoe event
  * @phba: pointer to lpfc hba data structure.
+ * @acqe_link: pointer to the async fcoe completion queue entry.
  *
- * This routine is invoked to enable the MSI interrupt mode. The kernel
- * function pci_enable_msi() is called to enable the MSI vector. The
- * device driver is responsible for calling the request_irq() to register
- * MSI vector with a interrupt the handler, which is done in this function.
- *
- * Return codes
- *     0 - sucessful
- *     other values - error
- */
-static int
-lpfc_enable_msi(struct lpfc_hba *phba)
+ * This routine is to handle the SLI4 asynchronous fcoe event.
+ **/
+static void
+lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
+                        struct lpfc_acqe_fcoe *acqe_fcoe)
 {
+       uint8_t event_type = bf_get(lpfc_acqe_fcoe_event_type, acqe_fcoe);
        int rc;
 
-       rc = pci_enable_msi(phba->pcidev);
-       if (!rc)
-               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
-                               "0462 PCI enable MSI mode success.\n");
-       else {
-               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
-                               "0471 PCI enable MSI mode failed (%d)\n", rc);
-               return rc;
-       }
+       switch (event_type) {
+       case LPFC_FCOE_EVENT_TYPE_NEW_FCF:
+               lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+                       "2546 New FCF found index 0x%x tag 0x%x \n",
+                       acqe_fcoe->fcf_index,
+                       acqe_fcoe->event_tag);
+               /*
+                * If the current FCF is in discovered state,
+                * do nothing.
+                */
+               spin_lock_irq(&phba->hbalock);
+               if (phba->fcf.fcf_flag & FCF_DISCOVERED) {
+                       spin_unlock_irq(&phba->hbalock);
+                       break;
+               }
+               spin_unlock_irq(&phba->hbalock);
 
-       rc = request_irq(phba->pcidev->irq, lpfc_intr_handler,
-                        IRQF_SHARED, LPFC_DRIVER_NAME, phba);
-       if (rc) {
-               pci_disable_msi(phba->pcidev);
-               lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
-                               "0478 MSI request_irq failed (%d)\n", rc);
+               /* Read the FCF table and re-discover SAN. */
+               rc = lpfc_sli4_read_fcf_record(phba,
+                       LPFC_FCOE_FCF_GET_FIRST);
+               if (rc)
+                       lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+                               "2547 Read FCF record failed 0x%x\n",
+                               rc);
+               break;
+
+       case LPFC_FCOE_EVENT_TYPE_FCF_TABLE_FULL:
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                       "2548 FCF Table full count 0x%x tag 0x%x \n",
+                       bf_get(lpfc_acqe_fcoe_fcf_count, acqe_fcoe),
+                       acqe_fcoe->event_tag);
+               break;
+
+       case LPFC_FCOE_EVENT_TYPE_FCF_DEAD:
+               lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+                       "2549 FCF disconnected fron network index 0x%x"
+                       " tag 0x%x \n", acqe_fcoe->fcf_index,
+                       acqe_fcoe->event_tag);
+               /* If the event is not for currently used fcf do nothing */
+               if (phba->fcf.fcf_indx != acqe_fcoe->fcf_index)
+                       break;
+               /*
+                * Currently, driver support only one FCF - so treat this as
+                * a link down.
+                */
+               lpfc_linkdown(phba);
+               /* Unregister FCF if no devices connected to it */
+               lpfc_unregister_unused_fcf(phba);
+               break;
+
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                       "0288 Unknown FCoE event type 0x%x event tag "
+                       "0x%x\n", event_type, acqe_fcoe->event_tag);
+               break;
        }
-       return rc;
 }
 
 /**
- * lpfc_disable_msi - Disable MSI interrupt mode
+ * lpfc_sli4_async_dcbx_evt - Process the asynchronous dcbx event
  * @phba: pointer to lpfc hba data structure.
+ * @acqe_link: pointer to the async dcbx completion queue entry.
  *
- * This routine is invoked to disable the MSI interrupt mode. The driver
- * calls free_irq() on MSI vector it has done request_irq() on before
- * calling pci_disable_msi(). Failure to do so results in a BUG_ON() and
- * a device will be left with MSI enabled and leaks its vector.
- */
-
+ * This routine is to handle the SLI4 asynchronous dcbx event.
+ **/
 static void
-lpfc_disable_msi(struct lpfc_hba *phba)
+lpfc_sli4_async_dcbx_evt(struct lpfc_hba *phba,
+                        struct lpfc_acqe_dcbx *acqe_dcbx)
 {
-       free_irq(phba->pcidev->irq, phba);
-       pci_disable_msi(phba->pcidev);
-       return;
+       lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                       "0290 The SLI4 DCBX asynchronous event is not "
+                       "handled yet\n");
+}
+
+/**
+ * lpfc_sli4_async_event_proc - Process all the pending asynchronous event
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked by the worker thread to process all the pending
+ * SLI4 asynchronous events.
+ **/
+void lpfc_sli4_async_event_proc(struct lpfc_hba *phba)
+{
+       struct lpfc_cq_event *cq_event;
+
+       /* First, declare the async event has been handled */
+       spin_lock_irq(&phba->hbalock);
+       phba->hba_flag &= ~ASYNC_EVENT;
+       spin_unlock_irq(&phba->hbalock);
+       /* Now, handle all the async events */
+       while (!list_empty(&phba->sli4_hba.sp_asynce_work_queue)) {
+               /* Get the first event from the head of the event queue */
+               spin_lock_irq(&phba->hbalock);
+               list_remove_head(&phba->sli4_hba.sp_asynce_work_queue,
+                                cq_event, struct lpfc_cq_event, list);
+               spin_unlock_irq(&phba->hbalock);
+               /* Process the asynchronous event */
+               switch (bf_get(lpfc_trailer_code, &cq_event->cqe.mcqe_cmpl)) {
+               case LPFC_TRAILER_CODE_LINK:
+                       lpfc_sli4_async_link_evt(phba,
+                                                &cq_event->cqe.acqe_link);
+                       break;
+               case LPFC_TRAILER_CODE_FCOE:
+                       lpfc_sli4_async_fcoe_evt(phba,
+                                                &cq_event->cqe.acqe_fcoe);
+                       break;
+               case LPFC_TRAILER_CODE_DCBX:
+                       lpfc_sli4_async_dcbx_evt(phba,
+                                                &cq_event->cqe.acqe_dcbx);
+                       break;
+               default:
+                       lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                                       "1804 Invalid asynchrous event code: "
+                                       "x%x\n", bf_get(lpfc_trailer_code,
+                                       &cq_event->cqe.mcqe_cmpl));
+                       break;
+               }
+               /* Free the completion event processed to the free pool */
+               lpfc_sli4_cq_event_release(phba, cq_event);
+       }
+}
+
+/**
+ * lpfc_api_table_setup - Set up per hba pci-device group func api jump table
+ * @phba: pointer to lpfc hba data structure.
+ * @dev_grp: The HBA PCI-Device group number.
+ *
+ * This routine is invoked to set up the per HBA PCI-Device group function
+ * API jump table entries.
+ *
+ * Return: 0 if success, otherwise -ENODEV
+ **/
+int
+lpfc_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
+{
+       int rc;
+
+       /* Set up lpfc PCI-device group */
+       phba->pci_dev_grp = dev_grp;
+
+       /* The LPFC_PCI_DEV_OC uses SLI4 */
+       if (dev_grp == LPFC_PCI_DEV_OC)
+               phba->sli_rev = LPFC_SLI_REV4;
+
+       /* Set up device INIT API function jump table */
+       rc = lpfc_init_api_table_setup(phba, dev_grp);
+       if (rc)
+               return -ENODEV;
+       /* Set up SCSI API function jump table */
+       rc = lpfc_scsi_api_table_setup(phba, dev_grp);
+       if (rc)
+               return -ENODEV;
+       /* Set up SLI API function jump table */
+       rc = lpfc_sli_api_table_setup(phba, dev_grp);
+       if (rc)
+               return -ENODEV;
+       /* Set up MBOX API function jump table */
+       rc = lpfc_mbox_api_table_setup(phba, dev_grp);
+       if (rc)
+               return -ENODEV;
+
+       return 0;
 }
 
 /**
@@ -2509,9 +3068,8 @@ lpfc_disable_msi(struct lpfc_hba *phba)
  *
  * This routine it invoked to log the currently used active interrupt mode
  * to the device.
- */
-static void
-lpfc_log_intr_mode(struct lpfc_hba *phba, uint32_t intr_mode)
+ **/
+static void lpfc_log_intr_mode(struct lpfc_hba *phba, uint32_t intr_mode)
 {
        switch (intr_mode) {
        case 0:
@@ -2534,659 +3092,4383 @@ lpfc_log_intr_mode(struct lpfc_hba *phba, uint32_t intr_mode)
        return;
 }
 
-static void
-lpfc_stop_port(struct lpfc_hba *phba)
-{
-       /* Clear all interrupt enable conditions */
-       writel(0, phba->HCregaddr);
-       readl(phba->HCregaddr); /* flush */
-       /* Clear all pending interrupts */
-       writel(0xffffffff, phba->HAregaddr);
-       readl(phba->HAregaddr); /* flush */
-
-       /* Reset some HBA SLI setup states */
-       lpfc_stop_phba_timers(phba);
-       phba->pport->work_port_events = 0;
-
-       return;
-}
-
 /**
- * lpfc_enable_intr - Enable device interrupt
+ * lpfc_enable_pci_dev - Enable a generic PCI device.
  * @phba: pointer to lpfc hba data structure.
  *
- * This routine is invoked to enable device interrupt and associate driver's
- * interrupt handler(s) to interrupt vector(s). Depends on the interrupt
- * mode configured to the driver, the driver will try to fallback from the
- * configured interrupt mode to an interrupt mode which is supported by the
- * platform, kernel, and device in the order of: MSI-X -> MSI -> IRQ.
+ * This routine is invoked to enable the PCI device that is common to all
+ * PCI devices.
  *
  * Return codes
- *   0 - sucessful
- *   other values - error
+ *     0 - sucessful
+ *     other values - error
  **/
-static uint32_t
-lpfc_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode)
+static int
+lpfc_enable_pci_dev(struct lpfc_hba *phba)
 {
-       uint32_t intr_mode = LPFC_INTR_ERROR;
-       int retval;
+       struct pci_dev *pdev;
+       int bars;
 
-       if (cfg_mode == 2) {
-               /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */
-               retval = lpfc_sli_config_port(phba, 3);
-               if (!retval) {
-                       /* Now, try to enable MSI-X interrupt mode */
-                       retval = lpfc_enable_msix(phba);
-                       if (!retval) {
-                               /* Indicate initialization to MSI-X mode */
-                               phba->intr_type = MSIX;
-                               intr_mode = 2;
-                       }
-               }
-       }
+       /* Obtain PCI device reference */
+       if (!phba->pcidev)
+               goto out_error;
+       else
+               pdev = phba->pcidev;
+       /* Select PCI BARs */
+       bars = pci_select_bars(pdev, IORESOURCE_MEM);
+       /* Enable PCI device */
+       if (pci_enable_device_mem(pdev))
+               goto out_error;
+       /* Request PCI resource for the device */
+       if (pci_request_selected_regions(pdev, bars, LPFC_DRIVER_NAME))
+               goto out_disable_device;
+       /* Set up device as PCI master and save state for EEH */
+       pci_set_master(pdev);
+       pci_try_set_mwi(pdev);
+       pci_save_state(pdev);
 
-       /* Fallback to MSI if MSI-X initialization failed */
-       if (cfg_mode >= 1 && phba->intr_type == NONE) {
-               retval = lpfc_enable_msi(phba);
-               if (!retval) {
-                       /* Indicate initialization to MSI mode */
-                       phba->intr_type = MSI;
-                       intr_mode = 1;
-               }
-       }
+       return 0;
 
-       /* Fallback to INTx if both MSI-X/MSI initalization failed */
-       if (phba->intr_type == NONE) {
-               retval = request_irq(phba->pcidev->irq, lpfc_intr_handler,
-                                    IRQF_SHARED, LPFC_DRIVER_NAME, phba);
-               if (!retval) {
-                       /* Indicate initialization to INTx mode */
-                       phba->intr_type = INTx;
-                       intr_mode = 0;
-               }
-       }
-       return intr_mode;
+out_disable_device:
+       pci_disable_device(pdev);
+out_error:
+       return -ENODEV;
 }
 
 /**
- * lpfc_disable_intr - Disable device interrupt
+ * lpfc_disable_pci_dev - Disable a generic PCI device.
  * @phba: pointer to lpfc hba data structure.
  *
- * This routine is invoked to disable device interrupt and disassociate the
- * driver's interrupt handler(s) from interrupt vector(s). Depending on the
- * interrupt mode, the driver will release the interrupt vector(s) for the
- * message signaled interrupt.
+ * This routine is invoked to disable the PCI device that is common to all
+ * PCI devices.
  **/
 static void
-lpfc_disable_intr(struct lpfc_hba *phba)
+lpfc_disable_pci_dev(struct lpfc_hba *phba)
 {
-       /* Disable the currently initialized interrupt mode */
-       if (phba->intr_type == MSIX)
-               lpfc_disable_msix(phba);
-       else if (phba->intr_type == MSI)
-               lpfc_disable_msi(phba);
-       else if (phba->intr_type == INTx)
-               free_irq(phba->pcidev->irq, phba);
+       struct pci_dev *pdev;
+       int bars;
 
-       /* Reset interrupt management states */
-       phba->intr_type = NONE;
-       phba->sli.slistat.sli_intr = 0;
+       /* Obtain PCI device reference */
+       if (!phba->pcidev)
+               return;
+       else
+               pdev = phba->pcidev;
+       /* Select PCI BARs */
+       bars = pci_select_bars(pdev, IORESOURCE_MEM);
+       /* Release PCI resource and disable PCI device */
+       pci_release_selected_regions(pdev, bars);
+       pci_disable_device(pdev);
+       /* Null out PCI private reference to driver */
+       pci_set_drvdata(pdev, NULL);
 
        return;
 }
 
 /**
- * lpfc_pci_probe_one - lpfc PCI probe func to register device to PCI subsystem
- * @pdev: pointer to PCI device
- * @pid: pointer to PCI device identifier
- *
- * This routine is to be registered to the kernel's PCI subsystem. When an
- * Emulex HBA is presented in PCI bus, the kernel PCI subsystem looks at
- * PCI device-specific information of the device and driver to see if the
- * driver state that it can support this kind of device. If the match is
- * successful, the driver core invokes this routine. If this routine
- * determines it can claim the HBA, it does all the initialization that it
- * needs to do to handle the HBA properly.
+ * lpfc_reset_hba - Reset a hba
+ * @phba: pointer to lpfc hba data structure.
  *
- * Return code
- *   0 - driver can claim the device
- *   negative value - driver can not claim the device
+ * This routine is invoked to reset a hba device. It brings the HBA
+ * offline, performs a board restart, and then brings the board back
+ * online. The lpfc_offline calls lpfc_sli_hba_down which will clean up
+ * on outstanding mailbox commands.
  **/
-static int __devinit
-lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
+void
+lpfc_reset_hba(struct lpfc_hba *phba)
 {
-       struct lpfc_vport *vport = NULL;
-       struct lpfc_hba   *phba;
-       struct lpfc_sli   *psli;
-       struct lpfc_iocbq *iocbq_entry = NULL, *iocbq_next = NULL;
-       struct Scsi_Host  *shost = NULL;
-       void *ptr;
-       unsigned long bar0map_len, bar2map_len;
-       int error = -ENODEV, retval;
-       int  i, hbq_count;
-       uint16_t iotag;
-       uint32_t cfg_mode, intr_mode;
-       int bars = pci_select_bars(pdev, IORESOURCE_MEM);
-       struct lpfc_adapter_event_header adapter_event;
-
-       if (pci_enable_device_mem(pdev))
-               goto out;
-       if (pci_request_selected_regions(pdev, bars, LPFC_DRIVER_NAME))
-               goto out_disable_device;
-
-       phba = kzalloc(sizeof (struct lpfc_hba), GFP_KERNEL);
-       if (!phba)
-               goto out_release_regions;
-
-       atomic_set(&phba->fast_event_count, 0);
-       spin_lock_init(&phba->hbalock);
-
-       /* Initialize ndlp management spinlock */
-       spin_lock_init(&phba->ndlp_lock);
-
-       phba->pcidev = pdev;
+       /* If resets are disabled then set error state and return. */
+       if (!phba->cfg_enable_hba_reset) {
+               phba->link_state = LPFC_HBA_ERROR;
+               return;
+       }
+       lpfc_offline_prep(phba);
+       lpfc_offline(phba);
+       lpfc_sli_brdrestart(phba);
+       lpfc_online(phba);
+       lpfc_unblock_mgmt_io(phba);
+}
 
-       /* Assign an unused board number */
-       if ((phba->brd_no = lpfc_get_instance()) < 0)
-               goto out_free_phba;
+/**
+ * lpfc_sli_driver_resource_setup - Setup driver internal resources for SLI3 dev.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to set up the driver internal resources specific to
+ * support the SLI-3 HBA device it attached to.
+ *
+ * Return codes
+ *     0 - sucessful
+ *     other values - error
+ **/
+static int
+lpfc_sli_driver_resource_setup(struct lpfc_hba *phba)
+{
+       struct lpfc_sli *psli;
 
-       INIT_LIST_HEAD(&phba->port_list);
-       init_waitqueue_head(&phba->wait_4_mlo_m_q);
        /*
-        * Get all the module params for configuring this host and then
-        * establish the host.
+        * Initialize timers used by driver
         */
-       lpfc_get_cfgparam(phba);
-       phba->max_vpi = LPFC_MAX_VPI;
 
-       /* Initialize timers used by driver */
+       /* Heartbeat timer */
        init_timer(&phba->hb_tmofunc);
        phba->hb_tmofunc.function = lpfc_hb_timeout;
        phba->hb_tmofunc.data = (unsigned long)phba;
 
        psli = &phba->sli;
+       /* MBOX heartbeat timer */
        init_timer(&psli->mbox_tmo);
        psli->mbox_tmo.function = lpfc_mbox_timeout;
        psli->mbox_tmo.data = (unsigned long) phba;
+       /* FCP polling mode timer */
        init_timer(&phba->fcp_poll_timer);
        phba->fcp_poll_timer.function = lpfc_poll_timeout;
        phba->fcp_poll_timer.data = (unsigned long) phba;
+       /* Fabric block timer */
        init_timer(&phba->fabric_block_timer);
        phba->fabric_block_timer.function = lpfc_fabric_block_timeout;
        phba->fabric_block_timer.data = (unsigned long) phba;
+       /* EA polling mode timer */
        init_timer(&phba->eratt_poll);
        phba->eratt_poll.function = lpfc_poll_eratt;
        phba->eratt_poll.data = (unsigned long) phba;
 
-       pci_set_master(pdev);
-       pci_save_state(pdev);
-       pci_try_set_mwi(pdev);
+       /* Host attention work mask setup */
+       phba->work_ha_mask = (HA_ERATT | HA_MBATT | HA_LATT);
+       phba->work_ha_mask |= (HA_RXMASK << (LPFC_ELS_RING * 4));
+
+       /* Get all the module params for configuring this host */
+       lpfc_get_cfgparam(phba);
+       /*
+        * Since the sg_tablesize is module parameter, the sg_dma_buf_size
+        * used to create the sg_dma_buf_pool must be dynamically calculated.
+        * 2 segments are added since the IOCB needs a command and response bde.
+        */
+       phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd) +
+               sizeof(struct fcp_rsp) +
+                       ((phba->cfg_sg_seg_cnt + 2) * sizeof(struct ulp_bde64));
+
+       if (phba->cfg_enable_bg) {
+               phba->cfg_sg_seg_cnt = LPFC_MAX_SG_SEG_CNT;
+               phba->cfg_sg_dma_buf_size +=
+                       phba->cfg_prot_sg_seg_cnt * sizeof(struct ulp_bde64);
+       }
 
-       if (pci_set_dma_mask(phba->pcidev, DMA_BIT_MASK(64)) != 0)
-               if (pci_set_dma_mask(phba->pcidev, DMA_BIT_MASK(32)) != 0)
-                       goto out_idr_remove;
+       /* Also reinitialize the host templates with new values. */
+       lpfc_vport_template.sg_tablesize = phba->cfg_sg_seg_cnt;
+       lpfc_template.sg_tablesize = phba->cfg_sg_seg_cnt;
+
+       phba->max_vpi = LPFC_MAX_VPI;
+       /* This will be set to correct value after config_port mbox */
+       phba->max_vports = 0;
 
        /*
-        * Get the bus address of Bar0 and Bar2 and the number of bytes
-        * required by each mapping.
+        * Initialize the SLI Layer to run with lpfc HBAs.
         */
-       phba->pci_bar0_map = pci_resource_start(phba->pcidev, 0);
-       bar0map_len        = pci_resource_len(phba->pcidev, 0);
+       lpfc_sli_setup(phba);
+       lpfc_sli_queue_setup(phba);
 
-       phba->pci_bar2_map = pci_resource_start(phba->pcidev, 2);
-       bar2map_len        = pci_resource_len(phba->pcidev, 2);
+       /* Allocate device driver memory */
+       if (lpfc_mem_alloc(phba, BPL_ALIGN_SZ))
+               return -ENOMEM;
 
-       /* Map HBA SLIM to a kernel virtual address. */
-       phba->slim_memmap_p = ioremap(phba->pci_bar0_map, bar0map_len);
-       if (!phba->slim_memmap_p) {
-               error = -ENODEV;
-               dev_printk(KERN_ERR, &pdev->dev,
-                          "ioremap failed for SLIM memory.\n");
-               goto out_idr_remove;
-       }
+       return 0;
+}
 
-       /* Map HBA Control Registers to a kernel virtual address. */
-       phba->ctrl_regs_memmap_p = ioremap(phba->pci_bar2_map, bar2map_len);
-       if (!phba->ctrl_regs_memmap_p) {
-               error = -ENODEV;
-               dev_printk(KERN_ERR, &pdev->dev,
-                          "ioremap failed for HBA control registers.\n");
-               goto out_iounmap_slim;
-       }
+/**
+ * lpfc_sli_driver_resource_unset - Unset drvr internal resources for SLI3 dev
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to unset the driver internal resources set up
+ * specific for supporting the SLI-3 HBA device it attached to.
+ **/
+static void
+lpfc_sli_driver_resource_unset(struct lpfc_hba *phba)
+{
+       /* Free device driver memory allocated */
+       lpfc_mem_free_all(phba);
 
-       /* Allocate memory for SLI-2 structures */
-       phba->slim2p.virt = dma_alloc_coherent(&phba->pcidev->dev,
-                                              SLI2_SLIM_SIZE,
-                                              &phba->slim2p.phys,
-                                              GFP_KERNEL);
-       if (!phba->slim2p.virt)
-               goto out_iounmap;
+       return;
+}
 
-       memset(phba->slim2p.virt, 0, SLI2_SLIM_SIZE);
-       phba->mbox = phba->slim2p.virt + offsetof(struct lpfc_sli2_slim, mbx);
-       phba->pcb = (phba->slim2p.virt + offsetof(struct lpfc_sli2_slim, pcb));
-       phba->IOCBs = (phba->slim2p.virt +
-                      offsetof(struct lpfc_sli2_slim, IOCBs));
+/**
+ * lpfc_sli4_driver_resource_setup - Setup drvr internal resources for SLI4 dev
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to set up the driver internal resources specific to
+ * support the SLI-4 HBA device it attached to.
+ *
+ * Return codes
+ *     0 - sucessful
+ *     other values - error
+ **/
+static int
+lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
+{
+       struct lpfc_sli *psli;
+       int rc;
+       int i, hbq_count;
 
-       phba->hbqslimp.virt = dma_alloc_coherent(&phba->pcidev->dev,
-                                                lpfc_sli_hbq_size(),
-                                                &phba->hbqslimp.phys,
-                                                GFP_KERNEL);
-       if (!phba->hbqslimp.virt)
-               goto out_free_slim;
+       /* Before proceed, wait for POST done and device ready */
+       rc = lpfc_sli4_post_status_check(phba);
+       if (rc)
+               return -ENODEV;
+
+       /*
+        * Initialize timers used by driver
+        */
+
+       /* Heartbeat timer */
+       init_timer(&phba->hb_tmofunc);
+       phba->hb_tmofunc.function = lpfc_hb_timeout;
+       phba->hb_tmofunc.data = (unsigned long)phba;
+
+       psli = &phba->sli;
+       /* MBOX heartbeat timer */
+       init_timer(&psli->mbox_tmo);
+       psli->mbox_tmo.function = lpfc_mbox_timeout;
+       psli->mbox_tmo.data = (unsigned long) phba;
+       /* Fabric block timer */
+       init_timer(&phba->fabric_block_timer);
+       phba->fabric_block_timer.function = lpfc_fabric_block_timeout;
+       phba->fabric_block_timer.data = (unsigned long) phba;
+       /* EA polling mode timer */
+       init_timer(&phba->eratt_poll);
+       phba->eratt_poll.function = lpfc_poll_eratt;
+       phba->eratt_poll.data = (unsigned long) phba;
+       /*
+        * We need to do a READ_CONFIG mailbox command here before
+        * calling lpfc_get_cfgparam. For VFs this will report the
+        * MAX_XRI, MAX_VPI, MAX_RPI, MAX_IOCB, and MAX_VFI settings.
+        * All of the resources allocated
+        * for this Port are tied to these values.
+        */
+       /* Get all the module params for configuring this host */
+       lpfc_get_cfgparam(phba);
+       phba->max_vpi = LPFC_MAX_VPI;
+       /* This will be set to correct value after the read_config mbox */
+       phba->max_vports = 0;
+
+       /* Program the default value of vlan_id and fc_map */
+       phba->valid_vlan = 0;
+       phba->fc_map[0] = LPFC_FCOE_FCF_MAP0;
+       phba->fc_map[1] = LPFC_FCOE_FCF_MAP1;
+       phba->fc_map[2] = LPFC_FCOE_FCF_MAP2;
+
+       /*
+        * Since the sg_tablesize is module parameter, the sg_dma_buf_size
+        * used to create the sg_dma_buf_pool must be dynamically calculated.
+        * 2 segments are added since the IOCB needs a command and response bde.
+        * To insure that the scsi sgl does not cross a 4k page boundary only
+        * sgl sizes of 1k, 2k, 4k, and 8k are supported.
+        * Table of sgl sizes and seg_cnt:
+        * sgl size,    sg_seg_cnt      total seg
+        * 1k           50              52
+        * 2k           114             116
+        * 4k           242             244
+        * 8k           498             500
+        * cmd(32) + rsp(160) + (52 * sizeof(sli4_sge)) = 1024
+        * cmd(32) + rsp(160) + (116 * sizeof(sli4_sge)) = 2048
+        * cmd(32) + rsp(160) + (244 * sizeof(sli4_sge)) = 4096
+        * cmd(32) + rsp(160) + (500 * sizeof(sli4_sge)) = 8192
+        */
+       if (phba->cfg_sg_seg_cnt <= LPFC_DEFAULT_SG_SEG_CNT)
+               phba->cfg_sg_seg_cnt = 50;
+       else if (phba->cfg_sg_seg_cnt <= 114)
+               phba->cfg_sg_seg_cnt = 114;
+       else if (phba->cfg_sg_seg_cnt <= 242)
+               phba->cfg_sg_seg_cnt = 242;
+       else
+               phba->cfg_sg_seg_cnt = 498;
 
+       phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd)
+                                       + sizeof(struct fcp_rsp);
+       phba->cfg_sg_dma_buf_size +=
+               ((phba->cfg_sg_seg_cnt + 2) * sizeof(struct sli4_sge));
+
+       /* Initialize buffer queue management fields */
        hbq_count = lpfc_sli_hbq_count();
-       ptr = phba->hbqslimp.virt;
-       for (i = 0; i < hbq_count; ++i) {
-               phba->hbqs[i].hbq_virt = ptr;
+       for (i = 0; i < hbq_count; ++i)
                INIT_LIST_HEAD(&phba->hbqs[i].hbq_buffer_list);
-               ptr += (lpfc_hbq_defs[i]->entry_count *
-                       sizeof(struct lpfc_hbq_entry));
-       }
-       phba->hbqs[LPFC_ELS_HBQ].hbq_alloc_buffer = lpfc_els_hbq_alloc;
-       phba->hbqs[LPFC_ELS_HBQ].hbq_free_buffer  = lpfc_els_hbq_free;
+       INIT_LIST_HEAD(&phba->rb_pend_list);
+       phba->hbqs[LPFC_ELS_HBQ].hbq_alloc_buffer = lpfc_sli4_rb_alloc;
+       phba->hbqs[LPFC_ELS_HBQ].hbq_free_buffer = lpfc_sli4_rb_free;
 
-       memset(phba->hbqslimp.virt, 0, lpfc_sli_hbq_size());
+       /*
+        * Initialize the SLI Layer to run with lpfc SLI4 HBAs.
+        */
+       /* Initialize the Abort scsi buffer list used by driver */
+       spin_lock_init(&phba->sli4_hba.abts_scsi_buf_list_lock);
+       INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_scsi_buf_list);
+       /* This abort list used by worker thread */
+       spin_lock_init(&phba->sli4_hba.abts_sgl_list_lock);
 
-       INIT_LIST_HEAD(&phba->hbqbuf_in_list);
+       /*
+        * Initialize dirver internal slow-path work queues
+        */
 
-       /* Initialize the SLI Layer to run with lpfc HBAs. */
+       /* Driver internel slow-path CQ Event pool */
+       INIT_LIST_HEAD(&phba->sli4_hba.sp_cqe_event_pool);
+       /* Response IOCB work queue list */
+       INIT_LIST_HEAD(&phba->sli4_hba.sp_rspiocb_work_queue);
+       /* Asynchronous event CQ Event work queue list */
+       INIT_LIST_HEAD(&phba->sli4_hba.sp_asynce_work_queue);
+       /* Fast-path XRI aborted CQ Event work queue list */
+       INIT_LIST_HEAD(&phba->sli4_hba.sp_fcp_xri_aborted_work_queue);
+       /* Slow-path XRI aborted CQ Event work queue list */
+       INIT_LIST_HEAD(&phba->sli4_hba.sp_els_xri_aborted_work_queue);
+       /* Receive queue CQ Event work queue list */
+       INIT_LIST_HEAD(&phba->sli4_hba.sp_unsol_work_queue);
+
+       /* Initialize the driver internal SLI layer lists. */
        lpfc_sli_setup(phba);
        lpfc_sli_queue_setup(phba);
 
-       retval = lpfc_mem_alloc(phba);
-       if (retval) {
-               error = retval;
-               goto out_free_hbqslimp;
-       }
+       /* Allocate device driver memory */
+       rc = lpfc_mem_alloc(phba, SGL_ALIGN_SZ);
+       if (rc)
+               return -ENOMEM;
 
-       /* Initialize and populate the iocb list per host.  */
-       INIT_LIST_HEAD(&phba->lpfc_iocb_list);
-       for (i = 0; i < LPFC_IOCB_LIST_CNT; i++) {
-               iocbq_entry = kzalloc(sizeof(struct lpfc_iocbq), GFP_KERNEL);
-               if (iocbq_entry == NULL) {
-                       printk(KERN_ERR "%s: only allocated %d iocbs of "
-                               "expected %d count. Unloading driver.\n",
-                               __func__, i, LPFC_IOCB_LIST_CNT);
-                       error = -ENOMEM;
-                       goto out_free_iocbq;
-               }
+       /* Create the bootstrap mailbox command */
+       rc = lpfc_create_bootstrap_mbox(phba);
+       if (unlikely(rc))
+               goto out_free_mem;
 
-               iotag = lpfc_sli_next_iotag(phba, iocbq_entry);
-               if (iotag == 0) {
-                       kfree (iocbq_entry);
-                       printk(KERN_ERR "%s: failed to allocate IOTAG. "
-                              "Unloading driver.\n",
-                               __func__);
-                       error = -ENOMEM;
-                       goto out_free_iocbq;
-               }
+       /* Set up the host's endian order with the device. */
+       rc = lpfc_setup_endian_order(phba);
+       if (unlikely(rc))
+               goto out_free_bsmbx;
 
-               spin_lock_irq(&phba->hbalock);
-               list_add(&iocbq_entry->list, &phba->lpfc_iocb_list);
-               phba->total_iocbq_bufs++;
-               spin_unlock_irq(&phba->hbalock);
-       }
+       /* Set up the hba's configuration parameters. */
+       rc = lpfc_sli4_read_config(phba);
+       if (unlikely(rc))
+               goto out_free_bsmbx;
 
-       /* Initialize HBA structure */
-       phba->fc_edtov = FF_DEF_EDTOV;
-       phba->fc_ratov = FF_DEF_RATOV;
-       phba->fc_altov = FF_DEF_ALTOV;
-       phba->fc_arbtov = FF_DEF_ARBTOV;
+       /* Perform a function reset */
+       rc = lpfc_pci_function_reset(phba);
+       if (unlikely(rc))
+               goto out_free_bsmbx;
 
-       INIT_LIST_HEAD(&phba->work_list);
-       phba->work_ha_mask = (HA_ERATT | HA_MBATT | HA_LATT);
-       phba->work_ha_mask |= (HA_RXMASK << (LPFC_ELS_RING * 4));
+       /* Create all the SLI4 queues */
+       rc = lpfc_sli4_queue_create(phba);
+       if (rc)
+               goto out_free_bsmbx;
 
-       /* Initialize the wait queue head for the kernel thread */
-       init_waitqueue_head(&phba->work_waitq);
+       /* Create driver internal CQE event pool */
+       rc = lpfc_sli4_cq_event_pool_create(phba);
+       if (rc)
+               goto out_destroy_queue;
 
-       /* Startup the kernel thread for this host adapter. */
-       phba->worker_thread = kthread_run(lpfc_do_work, phba,
-                                      "lpfc_worker_%d", phba->brd_no);
-       if (IS_ERR(phba->worker_thread)) {
-               error = PTR_ERR(phba->worker_thread);
-               goto out_free_iocbq;
+       /* Initialize and populate the iocb list per host */
+       rc = lpfc_init_sgl_list(phba);
+       if (rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1400 Failed to initialize sgl list.\n");
+               goto out_destroy_cq_event_pool;
+       }
+       rc = lpfc_init_active_sgl_array(phba);
+       if (rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1430 Failed to initialize sgl list.\n");
+               goto out_free_sgl_list;
        }
 
-       /* Initialize the list of scsi buffers used by driver for scsi IO. */
-       spin_lock_init(&phba->scsi_buf_list_lock);
-       INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list);
+       rc = lpfc_sli4_init_rpi_hdrs(phba);
+       if (rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1432 Failed to initialize rpi headers.\n");
+               goto out_free_active_sgl;
+       }
 
-       /* Initialize list of fabric iocbs */
-       INIT_LIST_HEAD(&phba->fabric_iocb_list);
+       phba->sli4_hba.fcp_eq_hdl = kzalloc((sizeof(struct lpfc_fcp_eq_hdl) *
+                                   phba->cfg_fcp_eq_count), GFP_KERNEL);
+       if (!phba->sli4_hba.fcp_eq_hdl) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2572 Failed allocate memory for fast-path "
+                               "per-EQ handle array\n");
+               goto out_remove_rpi_hdrs;
+       }
 
-       /* Initialize list to save ELS buffers */
-       INIT_LIST_HEAD(&phba->elsbuf);
+       phba->sli4_hba.msix_entries = kzalloc((sizeof(struct msix_entry) *
+                                     phba->sli4_hba.cfg_eqn), GFP_KERNEL);
+       if (!phba->sli4_hba.msix_entries) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2573 Failed allocate memory for msi-x "
+                               "interrupt vector entries\n");
+               goto out_free_fcp_eq_hdl;
+       }
 
-       vport = lpfc_create_port(phba, phba->brd_no, &phba->pcidev->dev);
-       if (!vport)
-               goto out_kthread_stop;
+       return rc;
 
-       shost = lpfc_shost_from_vport(vport);
-       phba->pport = vport;
-       lpfc_debugfs_initialize(vport);
+out_free_fcp_eq_hdl:
+       kfree(phba->sli4_hba.fcp_eq_hdl);
+out_remove_rpi_hdrs:
+       lpfc_sli4_remove_rpi_hdrs(phba);
+out_free_active_sgl:
+       lpfc_free_active_sgl(phba);
+out_free_sgl_list:
+       lpfc_free_sgl_list(phba);
+out_destroy_cq_event_pool:
+       lpfc_sli4_cq_event_pool_destroy(phba);
+out_destroy_queue:
+       lpfc_sli4_queue_destroy(phba);
+out_free_bsmbx:
+       lpfc_destroy_bootstrap_mbox(phba);
+out_free_mem:
+       lpfc_mem_free(phba);
+       return rc;
+}
 
-       pci_set_drvdata(pdev, shost);
+/**
+ * lpfc_sli4_driver_resource_unset - Unset drvr internal resources for SLI4 dev
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to unset the driver internal resources set up
+ * specific for supporting the SLI-4 HBA device it attached to.
+ **/
+static void
+lpfc_sli4_driver_resource_unset(struct lpfc_hba *phba)
+{
+       struct lpfc_fcf_conn_entry *conn_entry, *next_conn_entry;
 
-       phba->MBslimaddr = phba->slim_memmap_p;
-       phba->HAregaddr = phba->ctrl_regs_memmap_p + HA_REG_OFFSET;
-       phba->CAregaddr = phba->ctrl_regs_memmap_p + CA_REG_OFFSET;
-       phba->HSregaddr = phba->ctrl_regs_memmap_p + HS_REG_OFFSET;
-       phba->HCregaddr = phba->ctrl_regs_memmap_p + HC_REG_OFFSET;
+       /* unregister default FCFI from the HBA */
+       lpfc_sli4_fcfi_unreg(phba, phba->fcf.fcfi);
 
-       /* Configure sysfs attributes */
-       if (lpfc_alloc_sysfs_attr(vport)) {
-               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
-                               "1476 Failed to allocate sysfs attr\n");
-               error = -ENOMEM;
-               goto out_destroy_port;
-       }
+       /* Free the default FCR table */
+       lpfc_sli_remove_dflt_fcf(phba);
 
-       cfg_mode = phba->cfg_use_msi;
-       while (true) {
-               /* Configure and enable interrupt */
-               intr_mode = lpfc_enable_intr(phba, cfg_mode);
-               if (intr_mode == LPFC_INTR_ERROR) {
-                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
-                                       "0426 Failed to enable interrupt.\n");
-                       goto out_free_sysfs_attr;
-               }
-               /* HBA SLI setup */
-               if (lpfc_sli_hba_setup(phba)) {
-                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
-                                       "1477 Failed to set up hba\n");
-                       error = -ENODEV;
-                       goto out_remove_device;
-               }
+       /* Free memory allocated for msi-x interrupt vector entries */
+       kfree(phba->sli4_hba.msix_entries);
 
-               /* Wait 50ms for the interrupts of previous mailbox commands */
-               msleep(50);
-               /* Check active interrupts received */
-               if (phba->sli.slistat.sli_intr > LPFC_MSIX_VECTORS) {
-                       /* Log the current active interrupt mode */
-                       phba->intr_mode = intr_mode;
-                       lpfc_log_intr_mode(phba, intr_mode);
-                       break;
-               } else {
-                       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
-                                       "0451 Configure interrupt mode (%d) "
-                                       "failed active interrupt test.\n",
-                                       intr_mode);
-                       if (intr_mode == 0) {
-                               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
-                                               "0479 Failed to enable "
-                                               "interrupt.\n");
-                               error = -ENODEV;
-                               goto out_remove_device;
-                       }
-                       /* Stop HBA SLI setups */
-                       lpfc_stop_port(phba);
-                       /* Disable the current interrupt mode */
-                       lpfc_disable_intr(phba);
-                       /* Try next level of interrupt mode */
-                       cfg_mode = --intr_mode;
-               }
+       /* Free memory allocated for fast-path work queue handles */
+       kfree(phba->sli4_hba.fcp_eq_hdl);
+
+       /* Free the allocated rpi headers. */
+       lpfc_sli4_remove_rpi_hdrs(phba);
+
+       /* Free the ELS sgl list */
+       lpfc_free_active_sgl(phba);
+       lpfc_free_sgl_list(phba);
+
+       /* Free the SCSI sgl management array */
+       kfree(phba->sli4_hba.lpfc_scsi_psb_array);
+
+       /* Free the SLI4 queues */
+       lpfc_sli4_queue_destroy(phba);
+
+       /* Free the completion queue EQ event pool */
+       lpfc_sli4_cq_event_release_all(phba);
+       lpfc_sli4_cq_event_pool_destroy(phba);
+
+       /* Reset SLI4 HBA FCoE function */
+       lpfc_pci_function_reset(phba);
+
+       /* Free the bsmbx region. */
+       lpfc_destroy_bootstrap_mbox(phba);
+
+       /* Free the SLI Layer memory with SLI4 HBAs */
+       lpfc_mem_free_all(phba);
+
+       /* Free the current connect table */
+       list_for_each_entry_safe(conn_entry, next_conn_entry,
+               &phba->fcf_conn_rec_list, list)
+               kfree(conn_entry);
+
+       return;
+}
+
+/**
+ * lpfc_init_api_table_setup - Set up init api fucntion jump table
+ * @phba: The hba struct for which this call is being executed.
+ * @dev_grp: The HBA PCI-Device group number.
+ *
+ * This routine sets up the device INIT interface API function jump table
+ * in @phba struct.
+ *
+ * Returns: 0 - success, -ENODEV - failure.
+ **/
+int
+lpfc_init_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
+{
+       switch (dev_grp) {
+       case LPFC_PCI_DEV_LP:
+               phba->lpfc_hba_down_post = lpfc_hba_down_post_s3;
+               phba->lpfc_handle_eratt = lpfc_handle_eratt_s3;
+               phba->lpfc_stop_port = lpfc_stop_port_s3;
+               break;
+       case LPFC_PCI_DEV_OC:
+               phba->lpfc_hba_down_post = lpfc_hba_down_post_s4;
+               phba->lpfc_handle_eratt = lpfc_handle_eratt_s4;
+               phba->lpfc_stop_port = lpfc_stop_port_s4;
+               break;
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1431 Invalid HBA PCI-device group: 0x%x\n",
+                               dev_grp);
+               return -ENODEV;
+               break;
        }
+       return 0;
+}
 
+/**
+ * lpfc_setup_driver_resource_phase1 - Phase1 etup driver internal resources.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to set up the driver internal resources before the
+ * device specific resource setup to support the HBA device it attached to.
+ *
+ * Return codes
+ *     0 - sucessful
+ *     other values - error
+ **/
+static int
+lpfc_setup_driver_resource_phase1(struct lpfc_hba *phba)
+{
        /*
-        * hba setup may have changed the hba_queue_depth so we need to adjust
-        * the value of can_queue.
+        * Driver resources common to all SLI revisions
         */
-       shost->can_queue = phba->cfg_hba_queue_depth - 10;
-       if (phba->sli3_options & LPFC_SLI3_BG_ENABLED) {
+       atomic_set(&phba->fast_event_count, 0);
+       spin_lock_init(&phba->hbalock);
 
-               if (lpfc_prot_mask && lpfc_prot_guard) {
-                       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
-                                       "1478 Registering BlockGuard with the "
-                                       "SCSI layer\n");
+       /* Initialize ndlp management spinlock */
+       spin_lock_init(&phba->ndlp_lock);
 
-                       scsi_host_set_prot(shost, lpfc_prot_mask);
-                       scsi_host_set_guard(shost, lpfc_prot_guard);
-               }
+       INIT_LIST_HEAD(&phba->port_list);
+       INIT_LIST_HEAD(&phba->work_list);
+       init_waitqueue_head(&phba->wait_4_mlo_m_q);
+
+       /* Initialize the wait queue head for the kernel thread */
+       init_waitqueue_head(&phba->work_waitq);
+
+       /* Initialize the scsi buffer list used by driver for scsi IO */
+       spin_lock_init(&phba->scsi_buf_list_lock);
+       INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list);
+
+       /* Initialize the fabric iocb list */
+       INIT_LIST_HEAD(&phba->fabric_iocb_list);
+
+       /* Initialize list to save ELS buffers */
+       INIT_LIST_HEAD(&phba->elsbuf);
+
+       /* Initialize FCF connection rec list */
+       INIT_LIST_HEAD(&phba->fcf_conn_rec_list);
+
+       return 0;
+}
+
+/**
+ * lpfc_setup_driver_resource_phase2 - Phase2 setup driver internal resources.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to set up the driver internal resources after the
+ * device specific resource setup to support the HBA device it attached to.
+ *
+ * Return codes
+ *     0 - sucessful
+ *     other values - error
+ **/
+static int
+lpfc_setup_driver_resource_phase2(struct lpfc_hba *phba)
+{
+       int error;
+
+       /* Startup the kernel thread for this host adapter. */
+       phba->worker_thread = kthread_run(lpfc_do_work, phba,
+                                         "lpfc_worker_%d", phba->brd_no);
+       if (IS_ERR(phba->worker_thread)) {
+               error = PTR_ERR(phba->worker_thread);
+               return error;
        }
 
-       if (!_dump_buf_data) {
-               int pagecnt = 10;
-               while (pagecnt) {
-                       spin_lock_init(&_dump_buf_lock);
-                       _dump_buf_data =
-                               (char *) __get_free_pages(GFP_KERNEL, pagecnt);
-                       if (_dump_buf_data) {
-                               printk(KERN_ERR "BLKGRD allocated %d pages for "
-                                               "_dump_buf_data at 0x%p\n",
-                                               (1 << pagecnt), _dump_buf_data);
-                               _dump_buf_data_order = pagecnt;
-                               memset(_dump_buf_data, 0, ((1 << PAGE_SHIFT)
-                                                          << pagecnt));
-                               break;
-                       } else {
-                               --pagecnt;
-                       }
+       return 0;
+}
 
-               }
+/**
+ * lpfc_unset_driver_resource_phase2 - Phase2 unset driver internal resources.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to unset the driver internal resources set up after
+ * the device specific resource setup for supporting the HBA device it
+ * attached to.
+ **/
+static void
+lpfc_unset_driver_resource_phase2(struct lpfc_hba *phba)
+{
+       /* Stop kernel worker thread */
+       kthread_stop(phba->worker_thread);
+}
 
-               if (!_dump_buf_data_order)
-                       printk(KERN_ERR "BLKGRD ERROR unable to allocate "
-                                       "memory for hexdump\n");
+/**
+ * lpfc_free_iocb_list - Free iocb list.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to free the driver's IOCB list and memory.
+ **/
+static void
+lpfc_free_iocb_list(struct lpfc_hba *phba)
+{
+       struct lpfc_iocbq *iocbq_entry = NULL, *iocbq_next = NULL;
 
-       } else {
-               printk(KERN_ERR "BLKGRD already allocated _dump_buf_data=0x%p"
-                      "\n", _dump_buf_data);
+       spin_lock_irq(&phba->hbalock);
+       list_for_each_entry_safe(iocbq_entry, iocbq_next,
+                                &phba->lpfc_iocb_list, list) {
+               list_del(&iocbq_entry->list);
+               kfree(iocbq_entry);
+               phba->total_iocbq_bufs--;
        }
+       spin_unlock_irq(&phba->hbalock);
 
+       return;
+}
 
-       if (!_dump_buf_dif) {
-               int pagecnt = 10;
-               while (pagecnt) {
-                       _dump_buf_dif =
-                               (char *) __get_free_pages(GFP_KERNEL, pagecnt);
-                       if (_dump_buf_dif) {
-                               printk(KERN_ERR "BLKGRD allocated %d pages for "
-                                               "_dump_buf_dif at 0x%p\n",
-                                               (1 << pagecnt), _dump_buf_dif);
-                               _dump_buf_dif_order = pagecnt;
-                               memset(_dump_buf_dif, 0, ((1 << PAGE_SHIFT)
-                                                         << pagecnt));
-                               break;
-                       } else {
-                               --pagecnt;
-                       }
+/**
+ * lpfc_init_iocb_list - Allocate and initialize iocb list.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to allocate and initizlize the driver's IOCB
+ * list and set up the IOCB tag array accordingly.
+ *
+ * Return codes
+ *     0 - sucessful
+ *     other values - error
+ **/
+static int
+lpfc_init_iocb_list(struct lpfc_hba *phba, int iocb_count)
+{
+       struct lpfc_iocbq *iocbq_entry = NULL;
+       uint16_t iotag;
+       int i;
 
+       /* Initialize and populate the iocb list per host.  */
+       INIT_LIST_HEAD(&phba->lpfc_iocb_list);
+       for (i = 0; i < iocb_count; i++) {
+               iocbq_entry = kzalloc(sizeof(struct lpfc_iocbq), GFP_KERNEL);
+               if (iocbq_entry == NULL) {
+                       printk(KERN_ERR "%s: only allocated %d iocbs of "
+                               "expected %d count. Unloading driver.\n",
+                               __func__, i, LPFC_IOCB_LIST_CNT);
+                       goto out_free_iocbq;
                }
 
-               if (!_dump_buf_dif_order)
-                       printk(KERN_ERR "BLKGRD ERROR unable to allocate "
-                                       "memory for hexdump\n");
+               iotag = lpfc_sli_next_iotag(phba, iocbq_entry);
+               if (iotag == 0) {
+                       kfree(iocbq_entry);
+                       printk(KERN_ERR "%s: failed to allocate IOTAG. "
+                               "Unloading driver.\n", __func__);
+                       goto out_free_iocbq;
+               }
+               iocbq_entry->sli4_xritag = NO_XRI;
 
-       } else {
-               printk(KERN_ERR "BLKGRD already allocated _dump_buf_dif=0x%p\n",
-                               _dump_buf_dif);
+               spin_lock_irq(&phba->hbalock);
+               list_add(&iocbq_entry->list, &phba->lpfc_iocb_list);
+               phba->total_iocbq_bufs++;
+               spin_unlock_irq(&phba->hbalock);
        }
 
-       lpfc_host_attrib_init(shost);
+       return 0;
 
-       if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
-               spin_lock_irq(shost->host_lock);
-               lpfc_poll_start_timer(phba);
-               spin_unlock_irq(shost->host_lock);
+out_free_iocbq:
+       lpfc_free_iocb_list(phba);
+
+       return -ENOMEM;
+}
+
+/**
+ * lpfc_free_sgl_list - Free sgl list.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to free the driver's sgl list and memory.
+ **/
+static void
+lpfc_free_sgl_list(struct lpfc_hba *phba)
+{
+       struct lpfc_sglq *sglq_entry = NULL, *sglq_next = NULL;
+       LIST_HEAD(sglq_list);
+       int rc = 0;
+
+       spin_lock_irq(&phba->hbalock);
+       list_splice_init(&phba->sli4_hba.lpfc_sgl_list, &sglq_list);
+       spin_unlock_irq(&phba->hbalock);
+
+       list_for_each_entry_safe(sglq_entry, sglq_next,
+                                &sglq_list, list) {
+               list_del(&sglq_entry->list);
+               lpfc_mbuf_free(phba, sglq_entry->virt, sglq_entry->phys);
+               kfree(sglq_entry);
+               phba->sli4_hba.total_sglq_bufs--;
+       }
+       rc = lpfc_sli4_remove_all_sgl_pages(phba);
+       if (rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                       "2005 Unable to deregister pages from HBA: %x", rc);
        }
+       kfree(phba->sli4_hba.lpfc_els_sgl_array);
+}
 
-       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
-                       "0428 Perform SCSI scan\n");
-       /* Send board arrival event to upper layer */
-       adapter_event.event_type = FC_REG_ADAPTER_EVENT;
-       adapter_event.subcategory = LPFC_EVENT_ARRIVAL;
-       fc_host_post_vendor_event(shost, fc_get_event_number(),
-               sizeof(adapter_event),
-               (char *) &adapter_event,
-               LPFC_NL_VENDOR_ID);
+/**
+ * lpfc_init_active_sgl_array - Allocate the buf to track active ELS XRIs.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to allocate the driver's active sgl memory.
+ * This array will hold the sglq_entry's for active IOs.
+ **/
+static int
+lpfc_init_active_sgl_array(struct lpfc_hba *phba)
+{
+       int size;
+       size = sizeof(struct lpfc_sglq *);
+       size *= phba->sli4_hba.max_cfg_param.max_xri;
 
+       phba->sli4_hba.lpfc_sglq_active_list =
+               kzalloc(size, GFP_KERNEL);
+       if (!phba->sli4_hba.lpfc_sglq_active_list)
+               return -ENOMEM;
        return 0;
+}
+
+/**
+ * lpfc_free_active_sgl - Free the buf that tracks active ELS XRIs.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to walk through the array of active sglq entries
+ * and free all of the resources.
+ * This is just a place holder for now.
+ **/
+static void
+lpfc_free_active_sgl(struct lpfc_hba *phba)
+{
+       kfree(phba->sli4_hba.lpfc_sglq_active_list);
+}
+
+/**
+ * lpfc_init_sgl_list - Allocate and initialize sgl list.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to allocate and initizlize the driver's sgl
+ * list and set up the sgl xritag tag array accordingly.
+ *
+ * Return codes
+ *     0 - sucessful
+ *     other values - error
+ **/
+static int
+lpfc_init_sgl_list(struct lpfc_hba *phba)
+{
+       struct lpfc_sglq *sglq_entry = NULL;
+       int i;
+       int els_xri_cnt;
+
+       els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba);
+       lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+                               "2400 lpfc_init_sgl_list els %d.\n",
+                               els_xri_cnt);
+       /* Initialize and populate the sglq list per host/VF. */
+       INIT_LIST_HEAD(&phba->sli4_hba.lpfc_sgl_list);
+       INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_els_sgl_list);
+
+       /* Sanity check on XRI management */
+       if (phba->sli4_hba.max_cfg_param.max_xri <= els_xri_cnt) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "2562 No room left for SCSI XRI allocation: "
+                               "max_xri=%d, els_xri=%d\n",
+                               phba->sli4_hba.max_cfg_param.max_xri,
+                               els_xri_cnt);
+               return -ENOMEM;
+       }
+
+       /* Allocate memory for the ELS XRI management array */
+       phba->sli4_hba.lpfc_els_sgl_array =
+                       kzalloc((sizeof(struct lpfc_sglq *) * els_xri_cnt),
+                       GFP_KERNEL);
+
+       if (!phba->sli4_hba.lpfc_els_sgl_array) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "2401 Failed to allocate memory for ELS "
+                               "XRI management array of size %d.\n",
+                               els_xri_cnt);
+               return -ENOMEM;
+       }
+
+       /* Keep the SCSI XRI into the XRI management array */
+       phba->sli4_hba.scsi_xri_max =
+                       phba->sli4_hba.max_cfg_param.max_xri - els_xri_cnt;
+       phba->sli4_hba.scsi_xri_cnt = 0;
+
+       phba->sli4_hba.lpfc_scsi_psb_array =
+                       kzalloc((sizeof(struct lpfc_scsi_buf *) *
+                       phba->sli4_hba.scsi_xri_max), GFP_KERNEL);
+
+       if (!phba->sli4_hba.lpfc_scsi_psb_array) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "2563 Failed to allocate memory for SCSI "
+                               "XRI management array of size %d.\n",
+                               phba->sli4_hba.scsi_xri_max);
+               kfree(phba->sli4_hba.lpfc_els_sgl_array);
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < els_xri_cnt; i++) {
+               sglq_entry = kzalloc(sizeof(struct lpfc_sglq), GFP_KERNEL);
+               if (sglq_entry == NULL) {
+                       printk(KERN_ERR "%s: only allocated %d sgls of "
+                               "expected %d count. Unloading driver.\n",
+                               __func__, i, els_xri_cnt);
+                       goto out_free_mem;
+               }
+
+               sglq_entry->sli4_xritag = lpfc_sli4_next_xritag(phba);
+               if (sglq_entry->sli4_xritag == NO_XRI) {
+                       kfree(sglq_entry);
+                       printk(KERN_ERR "%s: failed to allocate XRI.\n"
+                               "Unloading driver.\n", __func__);
+                       goto out_free_mem;
+               }
+               sglq_entry->buff_type = GEN_BUFF_TYPE;
+               sglq_entry->virt = lpfc_mbuf_alloc(phba, 0, &sglq_entry->phys);
+               if (sglq_entry->virt == NULL) {
+                       kfree(sglq_entry);
+                       printk(KERN_ERR "%s: failed to allocate mbuf.\n"
+                               "Unloading driver.\n", __func__);
+                       goto out_free_mem;
+               }
+               sglq_entry->sgl = sglq_entry->virt;
+               memset(sglq_entry->sgl, 0, LPFC_BPL_SIZE);
+
+               /* The list order is used by later block SGL registraton */
+               spin_lock_irq(&phba->hbalock);
+               list_add_tail(&sglq_entry->list, &phba->sli4_hba.lpfc_sgl_list);
+               phba->sli4_hba.lpfc_els_sgl_array[i] = sglq_entry;
+               phba->sli4_hba.total_sglq_bufs++;
+               spin_unlock_irq(&phba->hbalock);
+       }
+       return 0;
+
+out_free_mem:
+       kfree(phba->sli4_hba.lpfc_scsi_psb_array);
+       lpfc_free_sgl_list(phba);
+       return -ENOMEM;
+}
+
+/**
+ * lpfc_sli4_init_rpi_hdrs - Post the rpi header memory region to the port
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to post rpi header templates to the
+ * HBA consistent with the SLI-4 interface spec.  This routine
+ * posts a PAGE_SIZE memory region to the port to hold up to
+ * PAGE_SIZE modulo 64 rpi context headers.
+ * No locks are held here because this is an initialization routine
+ * called only from probe or lpfc_online when interrupts are not
+ * enabled and the driver is reinitializing the device.
+ *
+ * Return codes
+ *     0 - sucessful
+ *     ENOMEM - No availble memory
+ *      EIO - The mailbox failed to complete successfully.
+ **/
+int
+lpfc_sli4_init_rpi_hdrs(struct lpfc_hba *phba)
+{
+       int rc = 0;
+       int longs;
+       uint16_t rpi_count;
+       struct lpfc_rpi_hdr *rpi_hdr;
+
+       INIT_LIST_HEAD(&phba->sli4_hba.lpfc_rpi_hdr_list);
+
+       /*
+        * Provision an rpi bitmask range for discovery. The total count
+        * is the difference between max and base + 1.
+        */
+       rpi_count = phba->sli4_hba.max_cfg_param.rpi_base +
+                   phba->sli4_hba.max_cfg_param.max_rpi - 1;
+
+       longs = ((rpi_count) + BITS_PER_LONG - 1) / BITS_PER_LONG;
+       phba->sli4_hba.rpi_bmask = kzalloc(longs * sizeof(unsigned long),
+                                          GFP_KERNEL);
+       if (!phba->sli4_hba.rpi_bmask)
+               return -ENOMEM;
+
+       rpi_hdr = lpfc_sli4_create_rpi_hdr(phba);
+       if (!rpi_hdr) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+                               "0391 Error during rpi post operation\n");
+               lpfc_sli4_remove_rpis(phba);
+               rc = -ENODEV;
+       }
+
+       return rc;
+}
+
+/**
+ * lpfc_sli4_create_rpi_hdr - Allocate an rpi header memory region
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to allocate a single 4KB memory region to
+ * support rpis and stores them in the phba.  This single region
+ * provides support for up to 64 rpis.  The region is used globally
+ * by the device.
+ *
+ * Returns:
+ *   A valid rpi hdr on success.
+ *   A NULL pointer on any failure.
+ **/
+struct lpfc_rpi_hdr *
+lpfc_sli4_create_rpi_hdr(struct lpfc_hba *phba)
+{
+       uint16_t rpi_limit, curr_rpi_range;
+       struct lpfc_dmabuf *dmabuf;
+       struct lpfc_rpi_hdr *rpi_hdr;
+
+       rpi_limit = phba->sli4_hba.max_cfg_param.rpi_base +
+                   phba->sli4_hba.max_cfg_param.max_rpi - 1;
+
+       spin_lock_irq(&phba->hbalock);
+       curr_rpi_range = phba->sli4_hba.next_rpi;
+       spin_unlock_irq(&phba->hbalock);
+
+       /*
+        * The port has a limited number of rpis. The increment here
+        * is LPFC_RPI_HDR_COUNT - 1 to account for the starting value
+        * and to allow the full max_rpi range per port.
+        */
+       if ((curr_rpi_range + (LPFC_RPI_HDR_COUNT - 1)) > rpi_limit)
+               return NULL;
+
+       /*
+        * First allocate the protocol header region for the port.  The
+        * port expects a 4KB DMA-mapped memory region that is 4K aligned.
+        */
+       dmabuf = kzalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+       if (!dmabuf)
+               return NULL;
+
+       dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev,
+                                         LPFC_HDR_TEMPLATE_SIZE,
+                                         &dmabuf->phys,
+                                         GFP_KERNEL);
+       if (!dmabuf->virt) {
+               rpi_hdr = NULL;
+               goto err_free_dmabuf;
+       }
+
+       memset(dmabuf->virt, 0, LPFC_HDR_TEMPLATE_SIZE);
+       if (!IS_ALIGNED(dmabuf->phys, LPFC_HDR_TEMPLATE_SIZE)) {
+               rpi_hdr = NULL;
+               goto err_free_coherent;
+       }
+
+       /* Save the rpi header data for cleanup later. */
+       rpi_hdr = kzalloc(sizeof(struct lpfc_rpi_hdr), GFP_KERNEL);
+       if (!rpi_hdr)
+               goto err_free_coherent;
+
+       rpi_hdr->dmabuf = dmabuf;
+       rpi_hdr->len = LPFC_HDR_TEMPLATE_SIZE;
+       rpi_hdr->page_count = 1;
+       spin_lock_irq(&phba->hbalock);
+       rpi_hdr->start_rpi = phba->sli4_hba.next_rpi;
+       list_add_tail(&rpi_hdr->list, &phba->sli4_hba.lpfc_rpi_hdr_list);
+
+       /*
+        * The next_rpi stores the next module-64 rpi value to post
+        * in any subsequent rpi memory region postings.
+        */
+       phba->sli4_hba.next_rpi += LPFC_RPI_HDR_COUNT;
+       spin_unlock_irq(&phba->hbalock);
+       return rpi_hdr;
+
+ err_free_coherent:
+       dma_free_coherent(&phba->pcidev->dev, LPFC_HDR_TEMPLATE_SIZE,
+                         dmabuf->virt, dmabuf->phys);
+ err_free_dmabuf:
+       kfree(dmabuf);
+       return NULL;
+}
+
+/**
+ * lpfc_sli4_remove_rpi_hdrs - Remove all rpi header memory regions
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to remove all memory resources allocated
+ * to support rpis. This routine presumes the caller has released all
+ * rpis consumed by fabric or port logins and is prepared to have
+ * the header pages removed.
+ **/
+void
+lpfc_sli4_remove_rpi_hdrs(struct lpfc_hba *phba)
+{
+       struct lpfc_rpi_hdr *rpi_hdr, *next_rpi_hdr;
+
+       list_for_each_entry_safe(rpi_hdr, next_rpi_hdr,
+                                &phba->sli4_hba.lpfc_rpi_hdr_list, list) {
+               list_del(&rpi_hdr->list);
+               dma_free_coherent(&phba->pcidev->dev, rpi_hdr->len,
+                                 rpi_hdr->dmabuf->virt, rpi_hdr->dmabuf->phys);
+               kfree(rpi_hdr->dmabuf);
+               kfree(rpi_hdr);
+       }
+
+       phba->sli4_hba.next_rpi = phba->sli4_hba.max_cfg_param.rpi_base;
+       memset(phba->sli4_hba.rpi_bmask, 0, sizeof(*phba->sli4_hba.rpi_bmask));
+}
+
+/**
+ * lpfc_hba_alloc - Allocate driver hba data structure for a device.
+ * @pdev: pointer to pci device data structure.
+ *
+ * This routine is invoked to allocate the driver hba data structure for an
+ * HBA device. If the allocation is successful, the phba reference to the
+ * PCI device data structure is set.
+ *
+ * Return codes
+ *      pointer to @phba - sucessful
+ *      NULL - error
+ **/
+static struct lpfc_hba *
+lpfc_hba_alloc(struct pci_dev *pdev)
+{
+       struct lpfc_hba *phba;
+
+       /* Allocate memory for HBA structure */
+       phba = kzalloc(sizeof(struct lpfc_hba), GFP_KERNEL);
+       if (!phba) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1417 Failed to allocate hba struct.\n");
+               return NULL;
+       }
+
+       /* Set reference to PCI device in HBA structure */
+       phba->pcidev = pdev;
+
+       /* Assign an unused board number */
+       phba->brd_no = lpfc_get_instance();
+       if (phba->brd_no < 0) {
+               kfree(phba);
+               return NULL;
+       }
+
+       return phba;
+}
+
+/**
+ * lpfc_hba_free - Free driver hba data structure with a device.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to free the driver hba data structure with an
+ * HBA device.
+ **/
+static void
+lpfc_hba_free(struct lpfc_hba *phba)
+{
+       /* Release the driver assigned board number */
+       idr_remove(&lpfc_hba_index, phba->brd_no);
+
+       kfree(phba);
+       return;
+}
+
+/**
+ * lpfc_create_shost - Create hba physical port with associated scsi host.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to create HBA physical port and associate a SCSI
+ * host with it.
+ *
+ * Return codes
+ *      0 - sucessful
+ *      other values - error
+ **/
+static int
+lpfc_create_shost(struct lpfc_hba *phba)
+{
+       struct lpfc_vport *vport;
+       struct Scsi_Host  *shost;
+
+       /* Initialize HBA FC structure */
+       phba->fc_edtov = FF_DEF_EDTOV;
+       phba->fc_ratov = FF_DEF_RATOV;
+       phba->fc_altov = FF_DEF_ALTOV;
+       phba->fc_arbtov = FF_DEF_ARBTOV;
+
+       vport = lpfc_create_port(phba, phba->brd_no, &phba->pcidev->dev);
+       if (!vport)
+               return -ENODEV;
+
+       shost = lpfc_shost_from_vport(vport);
+       phba->pport = vport;
+       lpfc_debugfs_initialize(vport);
+       /* Put reference to SCSI host to driver's device private data */
+       pci_set_drvdata(phba->pcidev, shost);
+
+       return 0;
+}
+
+/**
+ * lpfc_destroy_shost - Destroy hba physical port with associated scsi host.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to destroy HBA physical port and the associated
+ * SCSI host.
+ **/
+static void
+lpfc_destroy_shost(struct lpfc_hba *phba)
+{
+       struct lpfc_vport *vport = phba->pport;
+
+       /* Destroy physical port that associated with the SCSI host */
+       destroy_port(vport);
+
+       return;
+}
+
+/**
+ * lpfc_setup_bg - Setup Block guard structures and debug areas.
+ * @phba: pointer to lpfc hba data structure.
+ * @shost: the shost to be used to detect Block guard settings.
+ *
+ * This routine sets up the local Block guard protocol settings for @shost.
+ * This routine also allocates memory for debugging bg buffers.
+ **/
+static void
+lpfc_setup_bg(struct lpfc_hba *phba, struct Scsi_Host *shost)
+{
+       int pagecnt = 10;
+       if (lpfc_prot_mask && lpfc_prot_guard) {
+               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                               "1478 Registering BlockGuard with the "
+                               "SCSI layer\n");
+               scsi_host_set_prot(shost, lpfc_prot_mask);
+               scsi_host_set_guard(shost, lpfc_prot_guard);
+       }
+       if (!_dump_buf_data) {
+               while (pagecnt) {
+                       spin_lock_init(&_dump_buf_lock);
+                       _dump_buf_data =
+                               (char *) __get_free_pages(GFP_KERNEL, pagecnt);
+                       if (_dump_buf_data) {
+                               printk(KERN_ERR "BLKGRD allocated %d pages for "
+                                      "_dump_buf_data at 0x%p\n",
+                                      (1 << pagecnt), _dump_buf_data);
+                               _dump_buf_data_order = pagecnt;
+                               memset(_dump_buf_data, 0,
+                                      ((1 << PAGE_SHIFT) << pagecnt));
+                               break;
+                       } else
+                               --pagecnt;
+               }
+               if (!_dump_buf_data_order)
+                       printk(KERN_ERR "BLKGRD ERROR unable to allocate "
+                              "memory for hexdump\n");
+       } else
+               printk(KERN_ERR "BLKGRD already allocated _dump_buf_data=0x%p"
+                      "\n", _dump_buf_data);
+       if (!_dump_buf_dif) {
+               while (pagecnt) {
+                       _dump_buf_dif =
+                               (char *) __get_free_pages(GFP_KERNEL, pagecnt);
+                       if (_dump_buf_dif) {
+                               printk(KERN_ERR "BLKGRD allocated %d pages for "
+                                      "_dump_buf_dif at 0x%p\n",
+                                      (1 << pagecnt), _dump_buf_dif);
+                               _dump_buf_dif_order = pagecnt;
+                               memset(_dump_buf_dif, 0,
+                                      ((1 << PAGE_SHIFT) << pagecnt));
+                               break;
+                       } else
+                               --pagecnt;
+               }
+               if (!_dump_buf_dif_order)
+                       printk(KERN_ERR "BLKGRD ERROR unable to allocate "
+                              "memory for hexdump\n");
+       } else
+               printk(KERN_ERR "BLKGRD already allocated _dump_buf_dif=0x%p\n",
+                      _dump_buf_dif);
+}
+
+/**
+ * lpfc_post_init_setup - Perform necessary device post initialization setup.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to perform all the necessary post initialization
+ * setup for the device.
+ **/
+static void
+lpfc_post_init_setup(struct lpfc_hba *phba)
+{
+       struct Scsi_Host  *shost;
+       struct lpfc_adapter_event_header adapter_event;
+
+       /* Get the default values for Model Name and Description */
+       lpfc_get_hba_model_desc(phba, phba->ModelName, phba->ModelDesc);
+
+       /*
+        * hba setup may have changed the hba_queue_depth so we need to
+        * adjust the value of can_queue.
+        */
+       shost = pci_get_drvdata(phba->pcidev);
+       shost->can_queue = phba->cfg_hba_queue_depth - 10;
+       if (phba->sli3_options & LPFC_SLI3_BG_ENABLED)
+               lpfc_setup_bg(phba, shost);
+
+       lpfc_host_attrib_init(shost);
+
+       if (phba->cfg_poll & DISABLE_FCP_RING_INT) {
+               spin_lock_irq(shost->host_lock);
+               lpfc_poll_start_timer(phba);
+               spin_unlock_irq(shost->host_lock);
+       }
+
+       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                       "0428 Perform SCSI scan\n");
+       /* Send board arrival event to upper layer */
+       adapter_event.event_type = FC_REG_ADAPTER_EVENT;
+       adapter_event.subcategory = LPFC_EVENT_ARRIVAL;
+       fc_host_post_vendor_event(shost, fc_get_event_number(),
+                                 sizeof(adapter_event),
+                                 (char *) &adapter_event,
+                                 LPFC_NL_VENDOR_ID);
+       return;
+}
+
+/**
+ * lpfc_sli_pci_mem_setup - Setup SLI3 HBA PCI memory space.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to set up the PCI device memory space for device
+ * with SLI-3 interface spec.
+ *
+ * Return codes
+ *     0 - sucessful
+ *     other values - error
+ **/
+static int
+lpfc_sli_pci_mem_setup(struct lpfc_hba *phba)
+{
+       struct pci_dev *pdev;
+       unsigned long bar0map_len, bar2map_len;
+       int i, hbq_count;
+       void *ptr;
+       int error = -ENODEV;
+
+       /* Obtain PCI device reference */
+       if (!phba->pcidev)
+               return error;
+       else
+               pdev = phba->pcidev;
+
+       /* Set the device DMA mask size */
+       if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0)
+               if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0)
+                       return error;
+
+       /* Get the bus address of Bar0 and Bar2 and the number of bytes
+        * required by each mapping.
+        */
+       phba->pci_bar0_map = pci_resource_start(pdev, 0);
+       bar0map_len = pci_resource_len(pdev, 0);
+
+       phba->pci_bar2_map = pci_resource_start(pdev, 2);
+       bar2map_len = pci_resource_len(pdev, 2);
+
+       /* Map HBA SLIM to a kernel virtual address. */
+       phba->slim_memmap_p = ioremap(phba->pci_bar0_map, bar0map_len);
+       if (!phba->slim_memmap_p) {
+               dev_printk(KERN_ERR, &pdev->dev,
+                          "ioremap failed for SLIM memory.\n");
+               goto out;
+       }
+
+       /* Map HBA Control Registers to a kernel virtual address. */
+       phba->ctrl_regs_memmap_p = ioremap(phba->pci_bar2_map, bar2map_len);
+       if (!phba->ctrl_regs_memmap_p) {
+               dev_printk(KERN_ERR, &pdev->dev,
+                          "ioremap failed for HBA control registers.\n");
+               goto out_iounmap_slim;
+       }
+
+       /* Allocate memory for SLI-2 structures */
+       phba->slim2p.virt = dma_alloc_coherent(&pdev->dev,
+                                              SLI2_SLIM_SIZE,
+                                              &phba->slim2p.phys,
+                                              GFP_KERNEL);
+       if (!phba->slim2p.virt)
+               goto out_iounmap;
+
+       memset(phba->slim2p.virt, 0, SLI2_SLIM_SIZE);
+       phba->mbox = phba->slim2p.virt + offsetof(struct lpfc_sli2_slim, mbx);
+       phba->pcb = (phba->slim2p.virt + offsetof(struct lpfc_sli2_slim, pcb));
+       phba->IOCBs = (phba->slim2p.virt +
+                      offsetof(struct lpfc_sli2_slim, IOCBs));
+
+       phba->hbqslimp.virt = dma_alloc_coherent(&pdev->dev,
+                                                lpfc_sli_hbq_size(),
+                                                &phba->hbqslimp.phys,
+                                                GFP_KERNEL);
+       if (!phba->hbqslimp.virt)
+               goto out_free_slim;
+
+       hbq_count = lpfc_sli_hbq_count();
+       ptr = phba->hbqslimp.virt;
+       for (i = 0; i < hbq_count; ++i) {
+               phba->hbqs[i].hbq_virt = ptr;
+               INIT_LIST_HEAD(&phba->hbqs[i].hbq_buffer_list);
+               ptr += (lpfc_hbq_defs[i]->entry_count *
+                       sizeof(struct lpfc_hbq_entry));
+       }
+       phba->hbqs[LPFC_ELS_HBQ].hbq_alloc_buffer = lpfc_els_hbq_alloc;
+       phba->hbqs[LPFC_ELS_HBQ].hbq_free_buffer = lpfc_els_hbq_free;
+
+       memset(phba->hbqslimp.virt, 0, lpfc_sli_hbq_size());
+
+       INIT_LIST_HEAD(&phba->rb_pend_list);
+
+       phba->MBslimaddr = phba->slim_memmap_p;
+       phba->HAregaddr = phba->ctrl_regs_memmap_p + HA_REG_OFFSET;
+       phba->CAregaddr = phba->ctrl_regs_memmap_p + CA_REG_OFFSET;
+       phba->HSregaddr = phba->ctrl_regs_memmap_p + HS_REG_OFFSET;
+       phba->HCregaddr = phba->ctrl_regs_memmap_p + HC_REG_OFFSET;
+
+       return 0;
+
+out_free_slim:
+       dma_free_coherent(&pdev->dev, SLI2_SLIM_SIZE,
+                         phba->slim2p.virt, phba->slim2p.phys);
+out_iounmap:
+       iounmap(phba->ctrl_regs_memmap_p);
+out_iounmap_slim:
+       iounmap(phba->slim_memmap_p);
+out:
+       return error;
+}
+
+/**
+ * lpfc_sli_pci_mem_unset - Unset SLI3 HBA PCI memory space.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to unset the PCI device memory space for device
+ * with SLI-3 interface spec.
+ **/
+static void
+lpfc_sli_pci_mem_unset(struct lpfc_hba *phba)
+{
+       struct pci_dev *pdev;
+
+       /* Obtain PCI device reference */
+       if (!phba->pcidev)
+               return;
+       else
+               pdev = phba->pcidev;
+
+       /* Free coherent DMA memory allocated */
+       dma_free_coherent(&pdev->dev, lpfc_sli_hbq_size(),
+                         phba->hbqslimp.virt, phba->hbqslimp.phys);
+       dma_free_coherent(&pdev->dev, SLI2_SLIM_SIZE,
+                         phba->slim2p.virt, phba->slim2p.phys);
+
+       /* I/O memory unmap */
+       iounmap(phba->ctrl_regs_memmap_p);
+       iounmap(phba->slim_memmap_p);
+
+       return;
+}
+
+/**
+ * lpfc_sli4_post_status_check - Wait for SLI4 POST done and check status
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to wait for SLI4 device Power On Self Test (POST)
+ * done and check status.
+ *
+ * Return 0 if successful, otherwise -ENODEV.
+ **/
+int
+lpfc_sli4_post_status_check(struct lpfc_hba *phba)
+{
+       struct lpfc_register sta_reg, uerrlo_reg, uerrhi_reg, scratchpad;
+       uint32_t onlnreg0, onlnreg1;
+       int i, port_error = -ENODEV;
+
+       if (!phba->sli4_hba.STAregaddr)
+               return -ENODEV;
+
+       /* With uncoverable error, log the error message and return error */
+       onlnreg0 = readl(phba->sli4_hba.ONLINE0regaddr);
+       onlnreg1 = readl(phba->sli4_hba.ONLINE1regaddr);
+       if ((onlnreg0 != LPFC_ONLINE_NERR) || (onlnreg1 != LPFC_ONLINE_NERR)) {
+               uerrlo_reg.word0 = readl(phba->sli4_hba.UERRLOregaddr);
+               uerrhi_reg.word0 = readl(phba->sli4_hba.UERRHIregaddr);
+               if (uerrlo_reg.word0 || uerrhi_reg.word0) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                       "1422 HBA Unrecoverable error: "
+                                       "uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, "
+                                       "online0_reg=0x%x, online1_reg=0x%x\n",
+                                       uerrlo_reg.word0, uerrhi_reg.word0,
+                                       onlnreg0, onlnreg1);
+               }
+               return -ENODEV;
+       }
+
+       /* Wait up to 30 seconds for the SLI Port POST done and ready */
+       for (i = 0; i < 3000; i++) {
+               sta_reg.word0 = readl(phba->sli4_hba.STAregaddr);
+               /* Encounter fatal POST error, break out */
+               if (bf_get(lpfc_hst_state_perr, &sta_reg)) {
+                       port_error = -ENODEV;
+                       break;
+               }
+               if (LPFC_POST_STAGE_ARMFW_READY ==
+                   bf_get(lpfc_hst_state_port_status, &sta_reg)) {
+                       port_error = 0;
+                       break;
+               }
+               msleep(10);
+       }
+
+       if (port_error)
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                       "1408 Failure HBA POST Status: sta_reg=0x%x, "
+                       "perr=x%x, sfi=x%x, nip=x%x, ipc=x%x, xrom=x%x, "
+                       "dl=x%x, pstatus=x%x\n", sta_reg.word0,
+                       bf_get(lpfc_hst_state_perr, &sta_reg),
+                       bf_get(lpfc_hst_state_sfi, &sta_reg),
+                       bf_get(lpfc_hst_state_nip, &sta_reg),
+                       bf_get(lpfc_hst_state_ipc, &sta_reg),
+                       bf_get(lpfc_hst_state_xrom, &sta_reg),
+                       bf_get(lpfc_hst_state_dl, &sta_reg),
+                       bf_get(lpfc_hst_state_port_status, &sta_reg));
+
+       /* Log device information */
+       scratchpad.word0 =  readl(phba->sli4_hba.SCRATCHPADregaddr);
+       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                       "2534 Device Info: ChipType=0x%x, SliRev=0x%x, "
+                       "FeatureL1=0x%x, FeatureL2=0x%x\n",
+                       bf_get(lpfc_scratchpad_chiptype, &scratchpad),
+                       bf_get(lpfc_scratchpad_slirev, &scratchpad),
+                       bf_get(lpfc_scratchpad_featurelevel1, &scratchpad),
+                       bf_get(lpfc_scratchpad_featurelevel2, &scratchpad));
+
+       return port_error;
+}
+
+/**
+ * lpfc_sli4_bar0_register_memmap - Set up SLI4 BAR0 register memory map.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to set up SLI4 BAR0 PCI config space register
+ * memory map.
+ **/
+static void
+lpfc_sli4_bar0_register_memmap(struct lpfc_hba *phba)
+{
+       phba->sli4_hba.UERRLOregaddr = phba->sli4_hba.conf_regs_memmap_p +
+                                       LPFC_UERR_STATUS_LO;
+       phba->sli4_hba.UERRHIregaddr = phba->sli4_hba.conf_regs_memmap_p +
+                                       LPFC_UERR_STATUS_HI;
+       phba->sli4_hba.ONLINE0regaddr = phba->sli4_hba.conf_regs_memmap_p +
+                                       LPFC_ONLINE0;
+       phba->sli4_hba.ONLINE1regaddr = phba->sli4_hba.conf_regs_memmap_p +
+                                       LPFC_ONLINE1;
+       phba->sli4_hba.SCRATCHPADregaddr = phba->sli4_hba.conf_regs_memmap_p +
+                                       LPFC_SCRATCHPAD;
+}
+
+/**
+ * lpfc_sli4_bar1_register_memmap - Set up SLI4 BAR1 register memory map.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to set up SLI4 BAR1 control status register (CSR)
+ * memory map.
+ **/
+static void
+lpfc_sli4_bar1_register_memmap(struct lpfc_hba *phba)
+{
+
+       phba->sli4_hba.STAregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
+                                   LPFC_HST_STATE;
+       phba->sli4_hba.ISRregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
+                                   LPFC_HST_ISR0;
+       phba->sli4_hba.IMRregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
+                                   LPFC_HST_IMR0;
+       phba->sli4_hba.ISCRregaddr = phba->sli4_hba.ctrl_regs_memmap_p +
+                                    LPFC_HST_ISCR0;
+       return;
+}
+
+/**
+ * lpfc_sli4_bar2_register_memmap - Set up SLI4 BAR2 register memory map.
+ * @phba: pointer to lpfc hba data structure.
+ * @vf: virtual function number
+ *
+ * This routine is invoked to set up SLI4 BAR2 doorbell register memory map
+ * based on the given viftual function number, @vf.
+ *
+ * Return 0 if successful, otherwise -ENODEV.
+ **/
+static int
+lpfc_sli4_bar2_register_memmap(struct lpfc_hba *phba, uint32_t vf)
+{
+       if (vf > LPFC_VIR_FUNC_MAX)
+               return -ENODEV;
+
+       phba->sli4_hba.RQDBregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
+                               vf * LPFC_VFR_PAGE_SIZE + LPFC_RQ_DOORBELL);
+       phba->sli4_hba.WQDBregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
+                               vf * LPFC_VFR_PAGE_SIZE + LPFC_WQ_DOORBELL);
+       phba->sli4_hba.EQCQDBregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
+                               vf * LPFC_VFR_PAGE_SIZE + LPFC_EQCQ_DOORBELL);
+       phba->sli4_hba.MQDBregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
+                               vf * LPFC_VFR_PAGE_SIZE + LPFC_MQ_DOORBELL);
+       phba->sli4_hba.BMBXregaddr = (phba->sli4_hba.drbl_regs_memmap_p +
+                               vf * LPFC_VFR_PAGE_SIZE + LPFC_BMBX);
+       return 0;
+}
+
+/**
+ * lpfc_create_bootstrap_mbox - Create the bootstrap mailbox
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to create the bootstrap mailbox
+ * region consistent with the SLI-4 interface spec.  This
+ * routine allocates all memory necessary to communicate
+ * mailbox commands to the port and sets up all alignment
+ * needs.  No locks are expected to be held when calling
+ * this routine.
+ *
+ * Return codes
+ *     0 - sucessful
+ *     ENOMEM - could not allocated memory.
+ **/
+static int
+lpfc_create_bootstrap_mbox(struct lpfc_hba *phba)
+{
+       uint32_t bmbx_size;
+       struct lpfc_dmabuf *dmabuf;
+       struct dma_address *dma_address;
+       uint32_t pa_addr;
+       uint64_t phys_addr;
+
+       dmabuf = kzalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+       if (!dmabuf)
+               return -ENOMEM;
+
+       /*
+        * The bootstrap mailbox region is comprised of 2 parts
+        * plus an alignment restriction of 16 bytes.
+        */
+       bmbx_size = sizeof(struct lpfc_bmbx_create) + (LPFC_ALIGN_16_BYTE - 1);
+       dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev,
+                                         bmbx_size,
+                                         &dmabuf->phys,
+                                         GFP_KERNEL);
+       if (!dmabuf->virt) {
+               kfree(dmabuf);
+               return -ENOMEM;
+       }
+       memset(dmabuf->virt, 0, bmbx_size);
+
+       /*
+        * Initialize the bootstrap mailbox pointers now so that the register
+        * operations are simple later.  The mailbox dma address is required
+        * to be 16-byte aligned.  Also align the virtual memory as each
+        * maibox is copied into the bmbx mailbox region before issuing the
+        * command to the port.
+        */
+       phba->sli4_hba.bmbx.dmabuf = dmabuf;
+       phba->sli4_hba.bmbx.bmbx_size = bmbx_size;
+
+       phba->sli4_hba.bmbx.avirt = PTR_ALIGN(dmabuf->virt,
+                                             LPFC_ALIGN_16_BYTE);
+       phba->sli4_hba.bmbx.aphys = ALIGN(dmabuf->phys,
+                                             LPFC_ALIGN_16_BYTE);
+
+       /*
+        * Set the high and low physical addresses now.  The SLI4 alignment
+        * requirement is 16 bytes and the mailbox is posted to the port
+        * as two 30-bit addresses.  The other data is a bit marking whether
+        * the 30-bit address is the high or low address.
+        * Upcast bmbx aphys to 64bits so shift instruction compiles
+        * clean on 32 bit machines.
+        */
+       dma_address = &phba->sli4_hba.bmbx.dma_address;
+       phys_addr = (uint64_t)phba->sli4_hba.bmbx.aphys;
+       pa_addr = (uint32_t) ((phys_addr >> 34) & 0x3fffffff);
+       dma_address->addr_hi = (uint32_t) ((pa_addr << 2) |
+                                          LPFC_BMBX_BIT1_ADDR_HI);
+
+       pa_addr = (uint32_t) ((phba->sli4_hba.bmbx.aphys >> 4) & 0x3fffffff);
+       dma_address->addr_lo = (uint32_t) ((pa_addr << 2) |
+                                          LPFC_BMBX_BIT1_ADDR_LO);
+       return 0;
+}
+
+/**
+ * lpfc_destroy_bootstrap_mbox - Destroy all bootstrap mailbox resources
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to teardown the bootstrap mailbox
+ * region and release all host resources. This routine requires
+ * the caller to ensure all mailbox commands recovered, no
+ * additional mailbox comands are sent, and interrupts are disabled
+ * before calling this routine.
+ *
+ **/
+static void
+lpfc_destroy_bootstrap_mbox(struct lpfc_hba *phba)
+{
+       dma_free_coherent(&phba->pcidev->dev,
+                         phba->sli4_hba.bmbx.bmbx_size,
+                         phba->sli4_hba.bmbx.dmabuf->virt,
+                         phba->sli4_hba.bmbx.dmabuf->phys);
+
+       kfree(phba->sli4_hba.bmbx.dmabuf);
+       memset(&phba->sli4_hba.bmbx, 0, sizeof(struct lpfc_bmbx));
+}
+
+/**
+ * lpfc_sli4_read_config - Get the config parameters.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to read the configuration parameters from the HBA.
+ * The configuration parameters are used to set the base and maximum values
+ * for RPI's XRI's VPI's VFI's and FCFIs. These values also affect the resource
+ * allocation for the port.
+ *
+ * Return codes
+ *     0 - sucessful
+ *     ENOMEM - No availble memory
+ *      EIO - The mailbox failed to complete successfully.
+ **/
+static int
+lpfc_sli4_read_config(struct lpfc_hba *phba)
+{
+       LPFC_MBOXQ_t *pmb;
+       struct lpfc_mbx_read_config *rd_config;
+       uint32_t rc = 0;
+
+       pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!pmb) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "2011 Unable to allocate memory for issuing "
+                               "SLI_CONFIG_SPECIAL mailbox command\n");
+               return -ENOMEM;
+       }
+
+       lpfc_read_config(phba, pmb);
+
+       rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
+       if (rc != MBX_SUCCESS) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                       "2012 Mailbox failed , mbxCmd x%x "
+                       "READ_CONFIG, mbxStatus x%x\n",
+                       bf_get(lpfc_mqe_command, &pmb->u.mqe),
+                       bf_get(lpfc_mqe_status, &pmb->u.mqe));
+               rc = -EIO;
+       } else {
+               rd_config = &pmb->u.mqe.un.rd_config;
+               phba->sli4_hba.max_cfg_param.max_xri =
+                       bf_get(lpfc_mbx_rd_conf_xri_count, rd_config);
+               phba->sli4_hba.max_cfg_param.xri_base =
+                       bf_get(lpfc_mbx_rd_conf_xri_base, rd_config);
+               phba->sli4_hba.max_cfg_param.max_vpi =
+                       bf_get(lpfc_mbx_rd_conf_vpi_count, rd_config);
+               phba->sli4_hba.max_cfg_param.vpi_base =
+                       bf_get(lpfc_mbx_rd_conf_vpi_base, rd_config);
+               phba->sli4_hba.max_cfg_param.max_rpi =
+                       bf_get(lpfc_mbx_rd_conf_rpi_count, rd_config);
+               phba->sli4_hba.max_cfg_param.rpi_base =
+                       bf_get(lpfc_mbx_rd_conf_rpi_base, rd_config);
+               phba->sli4_hba.max_cfg_param.max_vfi =
+                       bf_get(lpfc_mbx_rd_conf_vfi_count, rd_config);
+               phba->sli4_hba.max_cfg_param.vfi_base =
+                       bf_get(lpfc_mbx_rd_conf_vfi_base, rd_config);
+               phba->sli4_hba.max_cfg_param.max_fcfi =
+                       bf_get(lpfc_mbx_rd_conf_fcfi_count, rd_config);
+               phba->sli4_hba.max_cfg_param.fcfi_base =
+                       bf_get(lpfc_mbx_rd_conf_fcfi_base, rd_config);
+               phba->sli4_hba.max_cfg_param.max_eq =
+                       bf_get(lpfc_mbx_rd_conf_eq_count, rd_config);
+               phba->sli4_hba.max_cfg_param.max_rq =
+                       bf_get(lpfc_mbx_rd_conf_rq_count, rd_config);
+               phba->sli4_hba.max_cfg_param.max_wq =
+                       bf_get(lpfc_mbx_rd_conf_wq_count, rd_config);
+               phba->sli4_hba.max_cfg_param.max_cq =
+                       bf_get(lpfc_mbx_rd_conf_cq_count, rd_config);
+               phba->lmt = bf_get(lpfc_mbx_rd_conf_lmt, rd_config);
+               phba->sli4_hba.next_xri = phba->sli4_hba.max_cfg_param.xri_base;
+               phba->vpi_base = phba->sli4_hba.max_cfg_param.vpi_base;
+               phba->vfi_base = phba->sli4_hba.max_cfg_param.vfi_base;
+               phba->sli4_hba.next_rpi = phba->sli4_hba.max_cfg_param.rpi_base;
+               phba->max_vpi = phba->sli4_hba.max_cfg_param.max_vpi;
+               phba->max_vports = phba->max_vpi;
+               lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+                               "2003 cfg params XRI(B:%d M:%d), "
+                               "VPI(B:%d M:%d) "
+                               "VFI(B:%d M:%d) "
+                               "RPI(B:%d M:%d) "
+                               "FCFI(B:%d M:%d)\n",
+                               phba->sli4_hba.max_cfg_param.xri_base,
+                               phba->sli4_hba.max_cfg_param.max_xri,
+                               phba->sli4_hba.max_cfg_param.vpi_base,
+                               phba->sli4_hba.max_cfg_param.max_vpi,
+                               phba->sli4_hba.max_cfg_param.vfi_base,
+                               phba->sli4_hba.max_cfg_param.max_vfi,
+                               phba->sli4_hba.max_cfg_param.rpi_base,
+                               phba->sli4_hba.max_cfg_param.max_rpi,
+                               phba->sli4_hba.max_cfg_param.fcfi_base,
+                               phba->sli4_hba.max_cfg_param.max_fcfi);
+       }
+       mempool_free(pmb, phba->mbox_mem_pool);
+
+       /* Reset the DFT_HBA_Q_DEPTH to the max xri  */
+       if (phba->cfg_hba_queue_depth > (phba->sli4_hba.max_cfg_param.max_xri))
+               phba->cfg_hba_queue_depth =
+                               phba->sli4_hba.max_cfg_param.max_xri;
+       return rc;
+}
+
+/**
+ * lpfc_dev_endian_order_setup - Notify the port of the host's endian order.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to setup the host-side endian order to the
+ * HBA consistent with the SLI-4 interface spec.
+ *
+ * Return codes
+ *     0 - sucessful
+ *     ENOMEM - No availble memory
+ *      EIO - The mailbox failed to complete successfully.
+ **/
+static int
+lpfc_setup_endian_order(struct lpfc_hba *phba)
+{
+       LPFC_MBOXQ_t *mboxq;
+       uint32_t rc = 0;
+       uint32_t endian_mb_data[2] = {HOST_ENDIAN_LOW_WORD0,
+                                     HOST_ENDIAN_HIGH_WORD1};
+
+       mboxq = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mboxq) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0492 Unable to allocate memory for issuing "
+                               "SLI_CONFIG_SPECIAL mailbox command\n");
+               return -ENOMEM;
+       }
+
+       /*
+        * The SLI4_CONFIG_SPECIAL mailbox command requires the first two
+        * words to contain special data values and no other data.
+        */
+       memset(mboxq, 0, sizeof(LPFC_MBOXQ_t));
+       memcpy(&mboxq->u.mqe, &endian_mb_data, sizeof(endian_mb_data));
+       rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+       if (rc != MBX_SUCCESS) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0493 SLI_CONFIG_SPECIAL mailbox failed with "
+                               "status x%x\n",
+                               rc);
+               rc = -EIO;
+       }
+
+       mempool_free(mboxq, phba->mbox_mem_pool);
+       return rc;
+}
+
+/**
+ * lpfc_sli4_queue_create - Create all the SLI4 queues
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to allocate all the SLI4 queues for the FCoE HBA
+ * operation. For each SLI4 queue type, the parameters such as queue entry
+ * count (queue depth) shall be taken from the module parameter. For now,
+ * we just use some constant number as place holder.
+ *
+ * Return codes
+ *      0 - sucessful
+ *      ENOMEM - No availble memory
+ *      EIO - The mailbox failed to complete successfully.
+ **/
+static int
+lpfc_sli4_queue_create(struct lpfc_hba *phba)
+{
+       struct lpfc_queue *qdesc;
+       int fcp_eqidx, fcp_cqidx, fcp_wqidx;
+       int cfg_fcp_wq_count;
+       int cfg_fcp_eq_count;
+
+       /*
+        * Sanity check for confiugred queue parameters against the run-time
+        * device parameters
+        */
+
+       /* Sanity check on FCP fast-path WQ parameters */
+       cfg_fcp_wq_count = phba->cfg_fcp_wq_count;
+       if (cfg_fcp_wq_count >
+           (phba->sli4_hba.max_cfg_param.max_wq - LPFC_SP_WQN_DEF)) {
+               cfg_fcp_wq_count = phba->sli4_hba.max_cfg_param.max_wq -
+                                  LPFC_SP_WQN_DEF;
+               if (cfg_fcp_wq_count < LPFC_FP_WQN_MIN) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                       "2581 Not enough WQs (%d) from "
+                                       "the pci function for supporting "
+                                       "FCP WQs (%d)\n",
+                                       phba->sli4_hba.max_cfg_param.max_wq,
+                                       phba->cfg_fcp_wq_count);
+                       goto out_error;
+               }
+               lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+                               "2582 Not enough WQs (%d) from the pci "
+                               "function for supporting the requested "
+                               "FCP WQs (%d), the actual FCP WQs can "
+                               "be supported: %d\n",
+                               phba->sli4_hba.max_cfg_param.max_wq,
+                               phba->cfg_fcp_wq_count, cfg_fcp_wq_count);
+       }
+       /* The actual number of FCP work queues adopted */
+       phba->cfg_fcp_wq_count = cfg_fcp_wq_count;
+
+       /* Sanity check on FCP fast-path EQ parameters */
+       cfg_fcp_eq_count = phba->cfg_fcp_eq_count;
+       if (cfg_fcp_eq_count >
+           (phba->sli4_hba.max_cfg_param.max_eq - LPFC_SP_EQN_DEF)) {
+               cfg_fcp_eq_count = phba->sli4_hba.max_cfg_param.max_eq -
+                                  LPFC_SP_EQN_DEF;
+               if (cfg_fcp_eq_count < LPFC_FP_EQN_MIN) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                       "2574 Not enough EQs (%d) from the "
+                                       "pci function for supporting FCP "
+                                       "EQs (%d)\n",
+                                       phba->sli4_hba.max_cfg_param.max_eq,
+                                       phba->cfg_fcp_eq_count);
+                       goto out_error;
+               }
+               lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+                               "2575 Not enough EQs (%d) from the pci "
+                               "function for supporting the requested "
+                               "FCP EQs (%d), the actual FCP EQs can "
+                               "be supported: %d\n",
+                               phba->sli4_hba.max_cfg_param.max_eq,
+                               phba->cfg_fcp_eq_count, cfg_fcp_eq_count);
+       }
+       /* It does not make sense to have more EQs than WQs */
+       if (cfg_fcp_eq_count > phba->cfg_fcp_wq_count) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+                               "2593 The number of FCP EQs (%d) is more "
+                               "than the number of FCP WQs (%d), take "
+                               "the number of FCP EQs same as than of "
+                               "WQs (%d)\n", cfg_fcp_eq_count,
+                               phba->cfg_fcp_wq_count,
+                               phba->cfg_fcp_wq_count);
+               cfg_fcp_eq_count = phba->cfg_fcp_wq_count;
+       }
+       /* The actual number of FCP event queues adopted */
+       phba->cfg_fcp_eq_count = cfg_fcp_eq_count;
+       /* The overall number of event queues used */
+       phba->sli4_hba.cfg_eqn = phba->cfg_fcp_eq_count + LPFC_SP_EQN_DEF;
+
+       /*
+        * Create Event Queues (EQs)
+        */
+
+       /* Get EQ depth from module parameter, fake the default for now */
+       phba->sli4_hba.eq_esize = LPFC_EQE_SIZE_4B;
+       phba->sli4_hba.eq_ecount = LPFC_EQE_DEF_COUNT;
+
+       /* Create slow path event queue */
+       qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.eq_esize,
+                                     phba->sli4_hba.eq_ecount);
+       if (!qdesc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0496 Failed allocate slow-path EQ\n");
+               goto out_error;
+       }
+       phba->sli4_hba.sp_eq = qdesc;
+
+       /* Create fast-path FCP Event Queue(s) */
+       phba->sli4_hba.fp_eq = kzalloc((sizeof(struct lpfc_queue *) *
+                              phba->cfg_fcp_eq_count), GFP_KERNEL);
+       if (!phba->sli4_hba.fp_eq) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2576 Failed allocate memory for fast-path "
+                               "EQ record array\n");
+               goto out_free_sp_eq;
+       }
+       for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_eq_count; fcp_eqidx++) {
+               qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.eq_esize,
+                                             phba->sli4_hba.eq_ecount);
+               if (!qdesc) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                       "0497 Failed allocate fast-path EQ\n");
+                       goto out_free_fp_eq;
+               }
+               phba->sli4_hba.fp_eq[fcp_eqidx] = qdesc;
+       }
+
+       /*
+        * Create Complete Queues (CQs)
+        */
+
+       /* Get CQ depth from module parameter, fake the default for now */
+       phba->sli4_hba.cq_esize = LPFC_CQE_SIZE;
+       phba->sli4_hba.cq_ecount = LPFC_CQE_DEF_COUNT;
+
+       /* Create slow-path Mailbox Command Complete Queue */
+       qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize,
+                                     phba->sli4_hba.cq_ecount);
+       if (!qdesc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0500 Failed allocate slow-path mailbox CQ\n");
+               goto out_free_fp_eq;
+       }
+       phba->sli4_hba.mbx_cq = qdesc;
+
+       /* Create slow-path ELS Complete Queue */
+       qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize,
+                                     phba->sli4_hba.cq_ecount);
+       if (!qdesc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0501 Failed allocate slow-path ELS CQ\n");
+               goto out_free_mbx_cq;
+       }
+       phba->sli4_hba.els_cq = qdesc;
+
+       /* Create slow-path Unsolicited Receive Complete Queue */
+       qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize,
+                                     phba->sli4_hba.cq_ecount);
+       if (!qdesc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0502 Failed allocate slow-path USOL RX CQ\n");
+               goto out_free_els_cq;
+       }
+       phba->sli4_hba.rxq_cq = qdesc;
+
+       /* Create fast-path FCP Completion Queue(s), one-to-one with EQs */
+       phba->sli4_hba.fcp_cq = kzalloc((sizeof(struct lpfc_queue *) *
+                               phba->cfg_fcp_eq_count), GFP_KERNEL);
+       if (!phba->sli4_hba.fcp_cq) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2577 Failed allocate memory for fast-path "
+                               "CQ record array\n");
+               goto out_free_rxq_cq;
+       }
+       for (fcp_cqidx = 0; fcp_cqidx < phba->cfg_fcp_eq_count; fcp_cqidx++) {
+               qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize,
+                                             phba->sli4_hba.cq_ecount);
+               if (!qdesc) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                       "0499 Failed allocate fast-path FCP "
+                                       "CQ (%d)\n", fcp_cqidx);
+                       goto out_free_fcp_cq;
+               }
+               phba->sli4_hba.fcp_cq[fcp_cqidx] = qdesc;
+       }
+
+       /* Create Mailbox Command Queue */
+       phba->sli4_hba.mq_esize = LPFC_MQE_SIZE;
+       phba->sli4_hba.mq_ecount = LPFC_MQE_DEF_COUNT;
+
+       qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.mq_esize,
+                                     phba->sli4_hba.mq_ecount);
+       if (!qdesc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0505 Failed allocate slow-path MQ\n");
+               goto out_free_fcp_cq;
+       }
+       phba->sli4_hba.mbx_wq = qdesc;
+
+       /*
+        * Create all the Work Queues (WQs)
+        */
+       phba->sli4_hba.wq_esize = LPFC_WQE_SIZE;
+       phba->sli4_hba.wq_ecount = LPFC_WQE_DEF_COUNT;
+
+       /* Create slow-path ELS Work Queue */
+       qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.wq_esize,
+                                     phba->sli4_hba.wq_ecount);
+       if (!qdesc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0504 Failed allocate slow-path ELS WQ\n");
+               goto out_free_mbx_wq;
+       }
+       phba->sli4_hba.els_wq = qdesc;
+
+       /* Create fast-path FCP Work Queue(s) */
+       phba->sli4_hba.fcp_wq = kzalloc((sizeof(struct lpfc_queue *) *
+                               phba->cfg_fcp_wq_count), GFP_KERNEL);
+       if (!phba->sli4_hba.fcp_wq) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2578 Failed allocate memory for fast-path "
+                               "WQ record array\n");
+               goto out_free_els_wq;
+       }
+       for (fcp_wqidx = 0; fcp_wqidx < phba->cfg_fcp_wq_count; fcp_wqidx++) {
+               qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.wq_esize,
+                                             phba->sli4_hba.wq_ecount);
+               if (!qdesc) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                       "0503 Failed allocate fast-path FCP "
+                                       "WQ (%d)\n", fcp_wqidx);
+                       goto out_free_fcp_wq;
+               }
+               phba->sli4_hba.fcp_wq[fcp_wqidx] = qdesc;
+       }
+
+       /*
+        * Create Receive Queue (RQ)
+        */
+       phba->sli4_hba.rq_esize = LPFC_RQE_SIZE;
+       phba->sli4_hba.rq_ecount = LPFC_RQE_DEF_COUNT;
+
+       /* Create Receive Queue for header */
+       qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.rq_esize,
+                                     phba->sli4_hba.rq_ecount);
+       if (!qdesc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0506 Failed allocate receive HRQ\n");
+               goto out_free_fcp_wq;
+       }
+       phba->sli4_hba.hdr_rq = qdesc;
+
+       /* Create Receive Queue for data */
+       qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.rq_esize,
+                                     phba->sli4_hba.rq_ecount);
+       if (!qdesc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0507 Failed allocate receive DRQ\n");
+               goto out_free_hdr_rq;
+       }
+       phba->sli4_hba.dat_rq = qdesc;
+
+       return 0;
+
+out_free_hdr_rq:
+       lpfc_sli4_queue_free(phba->sli4_hba.hdr_rq);
+       phba->sli4_hba.hdr_rq = NULL;
+out_free_fcp_wq:
+       for (--fcp_wqidx; fcp_wqidx >= 0; fcp_wqidx--) {
+               lpfc_sli4_queue_free(phba->sli4_hba.fcp_wq[fcp_wqidx]);
+               phba->sli4_hba.fcp_wq[fcp_wqidx] = NULL;
+       }
+       kfree(phba->sli4_hba.fcp_wq);
+out_free_els_wq:
+       lpfc_sli4_queue_free(phba->sli4_hba.els_wq);
+       phba->sli4_hba.els_wq = NULL;
+out_free_mbx_wq:
+       lpfc_sli4_queue_free(phba->sli4_hba.mbx_wq);
+       phba->sli4_hba.mbx_wq = NULL;
+out_free_fcp_cq:
+       for (--fcp_cqidx; fcp_cqidx >= 0; fcp_cqidx--) {
+               lpfc_sli4_queue_free(phba->sli4_hba.fcp_cq[fcp_cqidx]);
+               phba->sli4_hba.fcp_cq[fcp_cqidx] = NULL;
+       }
+       kfree(phba->sli4_hba.fcp_cq);
+out_free_rxq_cq:
+       lpfc_sli4_queue_free(phba->sli4_hba.rxq_cq);
+       phba->sli4_hba.rxq_cq = NULL;
+out_free_els_cq:
+       lpfc_sli4_queue_free(phba->sli4_hba.els_cq);
+       phba->sli4_hba.els_cq = NULL;
+out_free_mbx_cq:
+       lpfc_sli4_queue_free(phba->sli4_hba.mbx_cq);
+       phba->sli4_hba.mbx_cq = NULL;
+out_free_fp_eq:
+       for (--fcp_eqidx; fcp_eqidx >= 0; fcp_eqidx--) {
+               lpfc_sli4_queue_free(phba->sli4_hba.fp_eq[fcp_eqidx]);
+               phba->sli4_hba.fp_eq[fcp_eqidx] = NULL;
+       }
+       kfree(phba->sli4_hba.fp_eq);
+out_free_sp_eq:
+       lpfc_sli4_queue_free(phba->sli4_hba.sp_eq);
+       phba->sli4_hba.sp_eq = NULL;
+out_error:
+       return -ENOMEM;
+}
+
+/**
+ * lpfc_sli4_queue_destroy - Destroy all the SLI4 queues
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to release all the SLI4 queues with the FCoE HBA
+ * operation.
+ *
+ * Return codes
+ *      0 - sucessful
+ *      ENOMEM - No availble memory
+ *      EIO - The mailbox failed to complete successfully.
+ **/
+static void
+lpfc_sli4_queue_destroy(struct lpfc_hba *phba)
+{
+       int fcp_qidx;
+
+       /* Release mailbox command work queue */
+       lpfc_sli4_queue_free(phba->sli4_hba.mbx_wq);
+       phba->sli4_hba.mbx_wq = NULL;
+
+       /* Release ELS work queue */
+       lpfc_sli4_queue_free(phba->sli4_hba.els_wq);
+       phba->sli4_hba.els_wq = NULL;
+
+       /* Release FCP work queue */
+       for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_wq_count; fcp_qidx++)
+               lpfc_sli4_queue_free(phba->sli4_hba.fcp_wq[fcp_qidx]);
+       kfree(phba->sli4_hba.fcp_wq);
+       phba->sli4_hba.fcp_wq = NULL;
+
+       /* Release unsolicited receive queue */
+       lpfc_sli4_queue_free(phba->sli4_hba.hdr_rq);
+       phba->sli4_hba.hdr_rq = NULL;
+       lpfc_sli4_queue_free(phba->sli4_hba.dat_rq);
+       phba->sli4_hba.dat_rq = NULL;
+
+       /* Release unsolicited receive complete queue */
+       lpfc_sli4_queue_free(phba->sli4_hba.rxq_cq);
+       phba->sli4_hba.rxq_cq = NULL;
+
+       /* Release ELS complete queue */
+       lpfc_sli4_queue_free(phba->sli4_hba.els_cq);
+       phba->sli4_hba.els_cq = NULL;
+
+       /* Release mailbox command complete queue */
+       lpfc_sli4_queue_free(phba->sli4_hba.mbx_cq);
+       phba->sli4_hba.mbx_cq = NULL;
+
+       /* Release FCP response complete queue */
+       for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_eq_count; fcp_qidx++)
+               lpfc_sli4_queue_free(phba->sli4_hba.fcp_cq[fcp_qidx]);
+       kfree(phba->sli4_hba.fcp_cq);
+       phba->sli4_hba.fcp_cq = NULL;
+
+       /* Release fast-path event queue */
+       for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_eq_count; fcp_qidx++)
+               lpfc_sli4_queue_free(phba->sli4_hba.fp_eq[fcp_qidx]);
+       kfree(phba->sli4_hba.fp_eq);
+       phba->sli4_hba.fp_eq = NULL;
+
+       /* Release slow-path event queue */
+       lpfc_sli4_queue_free(phba->sli4_hba.sp_eq);
+       phba->sli4_hba.sp_eq = NULL;
+
+       return;
+}
+
+/**
+ * lpfc_sli4_queue_setup - Set up all the SLI4 queues
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to set up all the SLI4 queues for the FCoE HBA
+ * operation.
+ *
+ * Return codes
+ *      0 - sucessful
+ *      ENOMEM - No availble memory
+ *      EIO - The mailbox failed to complete successfully.
+ **/
+int
+lpfc_sli4_queue_setup(struct lpfc_hba *phba)
+{
+       int rc = -ENOMEM;
+       int fcp_eqidx, fcp_cqidx, fcp_wqidx;
+       int fcp_cq_index = 0;
+
+       /*
+        * Set up Event Queues (EQs)
+        */
+
+       /* Set up slow-path event queue */
+       if (!phba->sli4_hba.sp_eq) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0520 Slow-path EQ not allocated\n");
+               goto out_error;
+       }
+       rc = lpfc_eq_create(phba, phba->sli4_hba.sp_eq,
+                           LPFC_SP_DEF_IMAX);
+       if (rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0521 Failed setup of slow-path EQ: "
+                               "rc = 0x%x\n", rc);
+               goto out_error;
+       }
+       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                       "2583 Slow-path EQ setup: queue-id=%d\n",
+                       phba->sli4_hba.sp_eq->queue_id);
+
+       /* Set up fast-path event queue */
+       for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_eq_count; fcp_eqidx++) {
+               if (!phba->sli4_hba.fp_eq[fcp_eqidx]) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                       "0522 Fast-path EQ (%d) not "
+                                       "allocated\n", fcp_eqidx);
+                       goto out_destroy_fp_eq;
+               }
+               rc = lpfc_eq_create(phba, phba->sli4_hba.fp_eq[fcp_eqidx],
+                                   phba->cfg_fcp_imax);
+               if (rc) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                       "0523 Failed setup of fast-path EQ "
+                                       "(%d), rc = 0x%x\n", fcp_eqidx, rc);
+                       goto out_destroy_fp_eq;
+               }
+               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                               "2584 Fast-path EQ setup: "
+                               "queue[%d]-id=%d\n", fcp_eqidx,
+                               phba->sli4_hba.fp_eq[fcp_eqidx]->queue_id);
+       }
+
+       /*
+        * Set up Complete Queues (CQs)
+        */
+
+       /* Set up slow-path MBOX Complete Queue as the first CQ */
+       if (!phba->sli4_hba.mbx_cq) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0528 Mailbox CQ not allocated\n");
+               goto out_destroy_fp_eq;
+       }
+       rc = lpfc_cq_create(phba, phba->sli4_hba.mbx_cq, phba->sli4_hba.sp_eq,
+                           LPFC_MCQ, LPFC_MBOX);
+       if (rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0529 Failed setup of slow-path mailbox CQ: "
+                               "rc = 0x%x\n", rc);
+               goto out_destroy_fp_eq;
+       }
+       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                       "2585 MBX CQ setup: cq-id=%d, parent eq-id=%d\n",
+                       phba->sli4_hba.mbx_cq->queue_id,
+                       phba->sli4_hba.sp_eq->queue_id);
+
+       /* Set up slow-path ELS Complete Queue */
+       if (!phba->sli4_hba.els_cq) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0530 ELS CQ not allocated\n");
+               goto out_destroy_mbx_cq;
+       }
+       rc = lpfc_cq_create(phba, phba->sli4_hba.els_cq, phba->sli4_hba.sp_eq,
+                           LPFC_WCQ, LPFC_ELS);
+       if (rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0531 Failed setup of slow-path ELS CQ: "
+                               "rc = 0x%x\n", rc);
+               goto out_destroy_mbx_cq;
+       }
+       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                       "2586 ELS CQ setup: cq-id=%d, parent eq-id=%d\n",
+                       phba->sli4_hba.els_cq->queue_id,
+                       phba->sli4_hba.sp_eq->queue_id);
+
+       /* Set up slow-path Unsolicited Receive Complete Queue */
+       if (!phba->sli4_hba.rxq_cq) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0532 USOL RX CQ not allocated\n");
+               goto out_destroy_els_cq;
+       }
+       rc = lpfc_cq_create(phba, phba->sli4_hba.rxq_cq, phba->sli4_hba.sp_eq,
+                           LPFC_RCQ, LPFC_USOL);
+       if (rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0533 Failed setup of slow-path USOL RX CQ: "
+                               "rc = 0x%x\n", rc);
+               goto out_destroy_els_cq;
+       }
+       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                       "2587 USL CQ setup: cq-id=%d, parent eq-id=%d\n",
+                       phba->sli4_hba.rxq_cq->queue_id,
+                       phba->sli4_hba.sp_eq->queue_id);
+
+       /* Set up fast-path FCP Response Complete Queue */
+       for (fcp_cqidx = 0; fcp_cqidx < phba->cfg_fcp_eq_count; fcp_cqidx++) {
+               if (!phba->sli4_hba.fcp_cq[fcp_cqidx]) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                       "0526 Fast-path FCP CQ (%d) not "
+                                       "allocated\n", fcp_cqidx);
+                       goto out_destroy_fcp_cq;
+               }
+               rc = lpfc_cq_create(phba, phba->sli4_hba.fcp_cq[fcp_cqidx],
+                                   phba->sli4_hba.fp_eq[fcp_cqidx],
+                                   LPFC_WCQ, LPFC_FCP);
+               if (rc) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                       "0527 Failed setup of fast-path FCP "
+                                       "CQ (%d), rc = 0x%x\n", fcp_cqidx, rc);
+                       goto out_destroy_fcp_cq;
+               }
+               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                               "2588 FCP CQ setup: cq[%d]-id=%d, "
+                               "parent eq[%d]-id=%d\n",
+                               fcp_cqidx,
+                               phba->sli4_hba.fcp_cq[fcp_cqidx]->queue_id,
+                               fcp_cqidx,
+                               phba->sli4_hba.fp_eq[fcp_cqidx]->queue_id);
+       }
+
+       /*
+        * Set up all the Work Queues (WQs)
+        */
+
+       /* Set up Mailbox Command Queue */
+       if (!phba->sli4_hba.mbx_wq) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0538 Slow-path MQ not allocated\n");
+               goto out_destroy_fcp_cq;
+       }
+       rc = lpfc_mq_create(phba, phba->sli4_hba.mbx_wq,
+                           phba->sli4_hba.mbx_cq, LPFC_MBOX);
+       if (rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0539 Failed setup of slow-path MQ: "
+                               "rc = 0x%x\n", rc);
+               goto out_destroy_fcp_cq;
+       }
+       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                       "2589 MBX MQ setup: wq-id=%d, parent cq-id=%d\n",
+                       phba->sli4_hba.mbx_wq->queue_id,
+                       phba->sli4_hba.mbx_cq->queue_id);
+
+       /* Set up slow-path ELS Work Queue */
+       if (!phba->sli4_hba.els_wq) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0536 Slow-path ELS WQ not allocated\n");
+               goto out_destroy_mbx_wq;
+       }
+       rc = lpfc_wq_create(phba, phba->sli4_hba.els_wq,
+                           phba->sli4_hba.els_cq, LPFC_ELS);
+       if (rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0537 Failed setup of slow-path ELS WQ: "
+                               "rc = 0x%x\n", rc);
+               goto out_destroy_mbx_wq;
+       }
+       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                       "2590 ELS WQ setup: wq-id=%d, parent cq-id=%d\n",
+                       phba->sli4_hba.els_wq->queue_id,
+                       phba->sli4_hba.els_cq->queue_id);
+
+       /* Set up fast-path FCP Work Queue */
+       for (fcp_wqidx = 0; fcp_wqidx < phba->cfg_fcp_wq_count; fcp_wqidx++) {
+               if (!phba->sli4_hba.fcp_wq[fcp_wqidx]) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                       "0534 Fast-path FCP WQ (%d) not "
+                                       "allocated\n", fcp_wqidx);
+                       goto out_destroy_fcp_wq;
+               }
+               rc = lpfc_wq_create(phba, phba->sli4_hba.fcp_wq[fcp_wqidx],
+                                   phba->sli4_hba.fcp_cq[fcp_cq_index],
+                                   LPFC_FCP);
+               if (rc) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                       "0535 Failed setup of fast-path FCP "
+                                       "WQ (%d), rc = 0x%x\n", fcp_wqidx, rc);
+                       goto out_destroy_fcp_wq;
+               }
+               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                               "2591 FCP WQ setup: wq[%d]-id=%d, "
+                               "parent cq[%d]-id=%d\n",
+                               fcp_wqidx,
+                               phba->sli4_hba.fcp_wq[fcp_wqidx]->queue_id,
+                               fcp_cq_index,
+                               phba->sli4_hba.fcp_cq[fcp_cq_index]->queue_id);
+               /* Round robin FCP Work Queue's Completion Queue assignment */
+               fcp_cq_index = ((fcp_cq_index + 1) % phba->cfg_fcp_eq_count);
+       }
+
+       /*
+        * Create Receive Queue (RQ)
+        */
+       if (!phba->sli4_hba.hdr_rq || !phba->sli4_hba.dat_rq) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0540 Receive Queue not allocated\n");
+               goto out_destroy_fcp_wq;
+       }
+       rc = lpfc_rq_create(phba, phba->sli4_hba.hdr_rq, phba->sli4_hba.dat_rq,
+                           phba->sli4_hba.rxq_cq, LPFC_USOL);
+       if (rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0541 Failed setup of Receive Queue: "
+                               "rc = 0x%x\n", rc);
+               goto out_destroy_fcp_wq;
+       }
+       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                       "2592 USL RQ setup: hdr-rq-id=%d, dat-rq-id=%d "
+                       "parent cq-id=%d\n",
+                       phba->sli4_hba.hdr_rq->queue_id,
+                       phba->sli4_hba.dat_rq->queue_id,
+                       phba->sli4_hba.rxq_cq->queue_id);
+       return 0;
+
+out_destroy_fcp_wq:
+       for (--fcp_wqidx; fcp_wqidx >= 0; fcp_wqidx--)
+               lpfc_wq_destroy(phba, phba->sli4_hba.fcp_wq[fcp_wqidx]);
+       lpfc_wq_destroy(phba, phba->sli4_hba.els_wq);
+out_destroy_mbx_wq:
+       lpfc_mq_destroy(phba, phba->sli4_hba.mbx_wq);
+out_destroy_fcp_cq:
+       for (--fcp_cqidx; fcp_cqidx >= 0; fcp_cqidx--)
+               lpfc_cq_destroy(phba, phba->sli4_hba.fcp_cq[fcp_cqidx]);
+       lpfc_cq_destroy(phba, phba->sli4_hba.rxq_cq);
+out_destroy_els_cq:
+       lpfc_cq_destroy(phba, phba->sli4_hba.els_cq);
+out_destroy_mbx_cq:
+       lpfc_cq_destroy(phba, phba->sli4_hba.mbx_cq);
+out_destroy_fp_eq:
+       for (--fcp_eqidx; fcp_eqidx >= 0; fcp_eqidx--)
+               lpfc_eq_destroy(phba, phba->sli4_hba.fp_eq[fcp_eqidx]);
+       lpfc_eq_destroy(phba, phba->sli4_hba.sp_eq);
+out_error:
+       return rc;
+}
+
+/**
+ * lpfc_sli4_queue_unset - Unset all the SLI4 queues
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to unset all the SLI4 queues with the FCoE HBA
+ * operation.
+ *
+ * Return codes
+ *      0 - sucessful
+ *      ENOMEM - No availble memory
+ *      EIO - The mailbox failed to complete successfully.
+ **/
+void
+lpfc_sli4_queue_unset(struct lpfc_hba *phba)
+{
+       int fcp_qidx;
+
+       /* Unset mailbox command work queue */
+       lpfc_mq_destroy(phba, phba->sli4_hba.mbx_wq);
+       /* Unset ELS work queue */
+       lpfc_wq_destroy(phba, phba->sli4_hba.els_wq);
+       /* Unset unsolicited receive queue */
+       lpfc_rq_destroy(phba, phba->sli4_hba.hdr_rq, phba->sli4_hba.dat_rq);
+       /* Unset FCP work queue */
+       for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_wq_count; fcp_qidx++)
+               lpfc_wq_destroy(phba, phba->sli4_hba.fcp_wq[fcp_qidx]);
+       /* Unset mailbox command complete queue */
+       lpfc_cq_destroy(phba, phba->sli4_hba.mbx_cq);
+       /* Unset ELS complete queue */
+       lpfc_cq_destroy(phba, phba->sli4_hba.els_cq);
+       /* Unset unsolicited receive complete queue */
+       lpfc_cq_destroy(phba, phba->sli4_hba.rxq_cq);
+       /* Unset FCP response complete queue */
+       for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_eq_count; fcp_qidx++)
+               lpfc_cq_destroy(phba, phba->sli4_hba.fcp_cq[fcp_qidx]);
+       /* Unset fast-path event queue */
+       for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_eq_count; fcp_qidx++)
+               lpfc_eq_destroy(phba, phba->sli4_hba.fp_eq[fcp_qidx]);
+       /* Unset slow-path event queue */
+       lpfc_eq_destroy(phba, phba->sli4_hba.sp_eq);
+}
+
+/**
+ * lpfc_sli4_cq_event_pool_create - Create completion-queue event free pool
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to allocate and set up a pool of completion queue
+ * events. The body of the completion queue event is a completion queue entry
+ * CQE. For now, this pool is used for the interrupt service routine to queue
+ * the following HBA completion queue events for the worker thread to process:
+ *   - Mailbox asynchronous events
+ *   - Receive queue completion unsolicited events
+ * Later, this can be used for all the slow-path events.
+ *
+ * Return codes
+ *      0 - sucessful
+ *      -ENOMEM - No availble memory
+ **/
+static int
+lpfc_sli4_cq_event_pool_create(struct lpfc_hba *phba)
+{
+       struct lpfc_cq_event *cq_event;
+       int i;
+
+       for (i = 0; i < (4 * phba->sli4_hba.cq_ecount); i++) {
+               cq_event = kmalloc(sizeof(struct lpfc_cq_event), GFP_KERNEL);
+               if (!cq_event)
+                       goto out_pool_create_fail;
+               list_add_tail(&cq_event->list,
+                             &phba->sli4_hba.sp_cqe_event_pool);
+       }
+       return 0;
+
+out_pool_create_fail:
+       lpfc_sli4_cq_event_pool_destroy(phba);
+       return -ENOMEM;
+}
+
+/**
+ * lpfc_sli4_cq_event_pool_destroy - Free completion-queue event free pool
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to free the pool of completion queue events at
+ * driver unload time. Note that, it is the responsibility of the driver
+ * cleanup routine to free all the outstanding completion-queue events
+ * allocated from this pool back into the pool before invoking this routine
+ * to destroy the pool.
+ **/
+static void
+lpfc_sli4_cq_event_pool_destroy(struct lpfc_hba *phba)
+{
+       struct lpfc_cq_event *cq_event, *next_cq_event;
+
+       list_for_each_entry_safe(cq_event, next_cq_event,
+                                &phba->sli4_hba.sp_cqe_event_pool, list) {
+               list_del(&cq_event->list);
+               kfree(cq_event);
+       }
+}
+
+/**
+ * __lpfc_sli4_cq_event_alloc - Allocate a completion-queue event from free pool
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is the lock free version of the API invoked to allocate a
+ * completion-queue event from the free pool.
+ *
+ * Return: Pointer to the newly allocated completion-queue event if successful
+ *         NULL otherwise.
+ **/
+struct lpfc_cq_event *
+__lpfc_sli4_cq_event_alloc(struct lpfc_hba *phba)
+{
+       struct lpfc_cq_event *cq_event = NULL;
+
+       list_remove_head(&phba->sli4_hba.sp_cqe_event_pool, cq_event,
+                        struct lpfc_cq_event, list);
+       return cq_event;
+}
+
+/**
+ * lpfc_sli4_cq_event_alloc - Allocate a completion-queue event from free pool
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is the lock version of the API invoked to allocate a
+ * completion-queue event from the free pool.
+ *
+ * Return: Pointer to the newly allocated completion-queue event if successful
+ *         NULL otherwise.
+ **/
+struct lpfc_cq_event *
+lpfc_sli4_cq_event_alloc(struct lpfc_hba *phba)
+{
+       struct lpfc_cq_event *cq_event;
+       unsigned long iflags;
+
+       spin_lock_irqsave(&phba->hbalock, iflags);
+       cq_event = __lpfc_sli4_cq_event_alloc(phba);
+       spin_unlock_irqrestore(&phba->hbalock, iflags);
+       return cq_event;
+}
+
+/**
+ * __lpfc_sli4_cq_event_release - Release a completion-queue event to free pool
+ * @phba: pointer to lpfc hba data structure.
+ * @cq_event: pointer to the completion queue event to be freed.
+ *
+ * This routine is the lock free version of the API invoked to release a
+ * completion-queue event back into the free pool.
+ **/
+void
+__lpfc_sli4_cq_event_release(struct lpfc_hba *phba,
+                            struct lpfc_cq_event *cq_event)
+{
+       list_add_tail(&cq_event->list, &phba->sli4_hba.sp_cqe_event_pool);
+}
+
+/**
+ * lpfc_sli4_cq_event_release - Release a completion-queue event to free pool
+ * @phba: pointer to lpfc hba data structure.
+ * @cq_event: pointer to the completion queue event to be freed.
+ *
+ * This routine is the lock version of the API invoked to release a
+ * completion-queue event back into the free pool.
+ **/
+void
+lpfc_sli4_cq_event_release(struct lpfc_hba *phba,
+                          struct lpfc_cq_event *cq_event)
+{
+       unsigned long iflags;
+       spin_lock_irqsave(&phba->hbalock, iflags);
+       __lpfc_sli4_cq_event_release(phba, cq_event);
+       spin_unlock_irqrestore(&phba->hbalock, iflags);
+}
+
+/**
+ * lpfc_sli4_cq_event_release_all - Release all cq events to the free pool
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is to free all the pending completion-queue events to the
+ * back into the free pool for device reset.
+ **/
+static void
+lpfc_sli4_cq_event_release_all(struct lpfc_hba *phba)
+{
+       LIST_HEAD(cqelist);
+       struct lpfc_cq_event *cqe;
+       unsigned long iflags;
+
+       /* Retrieve all the pending WCQEs from pending WCQE lists */
+       spin_lock_irqsave(&phba->hbalock, iflags);
+       /* Pending FCP XRI abort events */
+       list_splice_init(&phba->sli4_hba.sp_fcp_xri_aborted_work_queue,
+                        &cqelist);
+       /* Pending ELS XRI abort events */
+       list_splice_init(&phba->sli4_hba.sp_els_xri_aborted_work_queue,
+                        &cqelist);
+       /* Pending asynnc events */
+       list_splice_init(&phba->sli4_hba.sp_asynce_work_queue,
+                        &cqelist);
+       spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+       while (!list_empty(&cqelist)) {
+               list_remove_head(&cqelist, cqe, struct lpfc_cq_event, list);
+               lpfc_sli4_cq_event_release(phba, cqe);
+       }
+}
+
+/**
+ * lpfc_pci_function_reset - Reset pci function.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to request a PCI function reset. It will destroys
+ * all resources assigned to the PCI function which originates this request.
+ *
+ * Return codes
+ *      0 - sucessful
+ *      ENOMEM - No availble memory
+ *      EIO - The mailbox failed to complete successfully.
+ **/
+int
+lpfc_pci_function_reset(struct lpfc_hba *phba)
+{
+       LPFC_MBOXQ_t *mboxq;
+       uint32_t rc = 0;
+       uint32_t shdr_status, shdr_add_status;
+       union lpfc_sli4_cfg_shdr *shdr;
+
+       mboxq = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mboxq) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0494 Unable to allocate memory for issuing "
+                               "SLI_FUNCTION_RESET mailbox command\n");
+               return -ENOMEM;
+       }
+
+       /* Set up PCI function reset SLI4_CONFIG mailbox-ioctl command */
+       lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON,
+                        LPFC_MBOX_OPCODE_FUNCTION_RESET, 0,
+                        LPFC_SLI4_MBX_EMBED);
+       rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+       shdr = (union lpfc_sli4_cfg_shdr *)
+               &mboxq->u.mqe.un.sli4_config.header.cfg_shdr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+       if (rc != MBX_TIMEOUT)
+               mempool_free(mboxq, phba->mbox_mem_pool);
+       if (shdr_status || shdr_add_status || rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0495 SLI_FUNCTION_RESET mailbox failed with "
+                               "status x%x add_status x%x, mbx status x%x\n",
+                               shdr_status, shdr_add_status, rc);
+               rc = -ENXIO;
+       }
+       return rc;
+}
+
+/**
+ * lpfc_sli4_send_nop_mbox_cmds - Send sli-4 nop mailbox commands
+ * @phba: pointer to lpfc hba data structure.
+ * @cnt: number of nop mailbox commands to send.
+ *
+ * This routine is invoked to send a number @cnt of NOP mailbox command and
+ * wait for each command to complete.
+ *
+ * Return: the number of NOP mailbox command completed.
+ **/
+static int
+lpfc_sli4_send_nop_mbox_cmds(struct lpfc_hba *phba, uint32_t cnt)
+{
+       LPFC_MBOXQ_t *mboxq;
+       int length, cmdsent;
+       uint32_t mbox_tmo;
+       uint32_t rc = 0;
+       uint32_t shdr_status, shdr_add_status;
+       union lpfc_sli4_cfg_shdr *shdr;
+
+       if (cnt == 0) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+                               "2518 Requested to send 0 NOP mailbox cmd\n");
+               return cnt;
+       }
+
+       mboxq = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mboxq) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2519 Unable to allocate memory for issuing "
+                               "NOP mailbox command\n");
+               return 0;
+       }
+
+       /* Set up NOP SLI4_CONFIG mailbox-ioctl command */
+       length = (sizeof(struct lpfc_mbx_nop) -
+                 sizeof(struct lpfc_sli4_cfg_mhdr));
+       lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON,
+                        LPFC_MBOX_OPCODE_NOP, length, LPFC_SLI4_MBX_EMBED);
+
+       mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
+       for (cmdsent = 0; cmdsent < cnt; cmdsent++) {
+               if (!phba->sli4_hba.intr_enable)
+                       rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+               else
+                       rc = lpfc_sli_issue_mbox_wait(phba, mboxq, mbox_tmo);
+               if (rc == MBX_TIMEOUT)
+                       break;
+               /* Check return status */
+               shdr = (union lpfc_sli4_cfg_shdr *)
+                       &mboxq->u.mqe.un.sli4_config.header.cfg_shdr;
+               shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+               shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,
+                                        &shdr->response);
+               if (shdr_status || shdr_add_status || rc) {
+                       lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+                                       "2520 NOP mailbox command failed "
+                                       "status x%x add_status x%x mbx "
+                                       "status x%x\n", shdr_status,
+                                       shdr_add_status, rc);
+                       break;
+               }
+       }
+
+       if (rc != MBX_TIMEOUT)
+               mempool_free(mboxq, phba->mbox_mem_pool);
+
+       return cmdsent;
+}
+
+/**
+ * lpfc_sli4_fcfi_unreg - Unregister fcfi to device
+ * @phba: pointer to lpfc hba data structure.
+ * @fcfi: fcf index.
+ *
+ * This routine is invoked to unregister a FCFI from device.
+ **/
+void
+lpfc_sli4_fcfi_unreg(struct lpfc_hba *phba, uint16_t fcfi)
+{
+       LPFC_MBOXQ_t *mbox;
+       uint32_t mbox_tmo;
+       int rc;
+       unsigned long flags;
+
+       mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+
+       if (!mbox)
+               return;
+
+       lpfc_unreg_fcfi(mbox, fcfi);
+
+       if (!phba->sli4_hba.intr_enable)
+               rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+       else {
+               mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
+               rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo);
+       }
+       if (rc != MBX_TIMEOUT)
+               mempool_free(mbox, phba->mbox_mem_pool);
+       if (rc != MBX_SUCCESS)
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "2517 Unregister FCFI command failed "
+                               "status %d, mbxStatus x%x\n", rc,
+                               bf_get(lpfc_mqe_status, &mbox->u.mqe));
+       else {
+               spin_lock_irqsave(&phba->hbalock, flags);
+               /* Mark the FCFI is no longer registered */
+               phba->fcf.fcf_flag &=
+                       ~(FCF_AVAILABLE | FCF_REGISTERED | FCF_DISCOVERED);
+               spin_unlock_irqrestore(&phba->hbalock, flags);
+       }
+}
+
+/**
+ * lpfc_sli4_pci_mem_setup - Setup SLI4 HBA PCI memory space.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to set up the PCI device memory space for device
+ * with SLI-4 interface spec.
+ *
+ * Return codes
+ *     0 - sucessful
+ *     other values - error
+ **/
+static int
+lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
+{
+       struct pci_dev *pdev;
+       unsigned long bar0map_len, bar1map_len, bar2map_len;
+       int error = -ENODEV;
+
+       /* Obtain PCI device reference */
+       if (!phba->pcidev)
+               return error;
+       else
+               pdev = phba->pcidev;
+
+       /* Set the device DMA mask size */
+       if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0)
+               if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0)
+                       return error;
+
+       /* Get the bus address of SLI4 device Bar0, Bar1, and Bar2 and the
+        * number of bytes required by each mapping. They are actually
+        * mapping to the PCI BAR regions 1, 2, and 4 by the SLI4 device.
+        */
+       phba->pci_bar0_map = pci_resource_start(pdev, LPFC_SLI4_BAR0);
+       bar0map_len = pci_resource_len(pdev, LPFC_SLI4_BAR0);
+
+       phba->pci_bar1_map = pci_resource_start(pdev, LPFC_SLI4_BAR1);
+       bar1map_len = pci_resource_len(pdev, LPFC_SLI4_BAR1);
+
+       phba->pci_bar2_map = pci_resource_start(pdev, LPFC_SLI4_BAR2);
+       bar2map_len = pci_resource_len(pdev, LPFC_SLI4_BAR2);
+
+       /* Map SLI4 PCI Config Space Register base to a kernel virtual addr */
+       phba->sli4_hba.conf_regs_memmap_p =
+                               ioremap(phba->pci_bar0_map, bar0map_len);
+       if (!phba->sli4_hba.conf_regs_memmap_p) {
+               dev_printk(KERN_ERR, &pdev->dev,
+                          "ioremap failed for SLI4 PCI config registers.\n");
+               goto out;
+       }
+
+       /* Map SLI4 HBA Control Register base to a kernel virtual address. */
+       phba->sli4_hba.ctrl_regs_memmap_p =
+                               ioremap(phba->pci_bar1_map, bar1map_len);
+       if (!phba->sli4_hba.ctrl_regs_memmap_p) {
+               dev_printk(KERN_ERR, &pdev->dev,
+                          "ioremap failed for SLI4 HBA control registers.\n");
+               goto out_iounmap_conf;
+       }
+
+       /* Map SLI4 HBA Doorbell Register base to a kernel virtual address. */
+       phba->sli4_hba.drbl_regs_memmap_p =
+                               ioremap(phba->pci_bar2_map, bar2map_len);
+       if (!phba->sli4_hba.drbl_regs_memmap_p) {
+               dev_printk(KERN_ERR, &pdev->dev,
+                          "ioremap failed for SLI4 HBA doorbell registers.\n");
+               goto out_iounmap_ctrl;
+       }
+
+       /* Set up BAR0 PCI config space register memory map */
+       lpfc_sli4_bar0_register_memmap(phba);
+
+       /* Set up BAR1 register memory map */
+       lpfc_sli4_bar1_register_memmap(phba);
+
+       /* Set up BAR2 register memory map */
+       error = lpfc_sli4_bar2_register_memmap(phba, LPFC_VF0);
+       if (error)
+               goto out_iounmap_all;
+
+       return 0;
+
+out_iounmap_all:
+       iounmap(phba->sli4_hba.drbl_regs_memmap_p);
+out_iounmap_ctrl:
+       iounmap(phba->sli4_hba.ctrl_regs_memmap_p);
+out_iounmap_conf:
+       iounmap(phba->sli4_hba.conf_regs_memmap_p);
+out:
+       return error;
+}
+
+/**
+ * lpfc_sli4_pci_mem_unset - Unset SLI4 HBA PCI memory space.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to unset the PCI device memory space for device
+ * with SLI-4 interface spec.
+ **/
+static void
+lpfc_sli4_pci_mem_unset(struct lpfc_hba *phba)
+{
+       struct pci_dev *pdev;
+
+       /* Obtain PCI device reference */
+       if (!phba->pcidev)
+               return;
+       else
+               pdev = phba->pcidev;
+
+       /* Free coherent DMA memory allocated */
+
+       /* Unmap I/O memory space */
+       iounmap(phba->sli4_hba.drbl_regs_memmap_p);
+       iounmap(phba->sli4_hba.ctrl_regs_memmap_p);
+       iounmap(phba->sli4_hba.conf_regs_memmap_p);
+
+       return;
+}
+
+/**
+ * lpfc_sli_enable_msix - Enable MSI-X interrupt mode on SLI-3 device
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to enable the MSI-X interrupt vectors to device
+ * with SLI-3 interface specs. The kernel function pci_enable_msix() is
+ * called to enable the MSI-X vectors. Note that pci_enable_msix(), once
+ * invoked, enables either all or nothing, depending on the current
+ * availability of PCI vector resources. The device driver is responsible
+ * for calling the individual request_irq() to register each MSI-X vector
+ * with a interrupt handler, which is done in this function. Note that
+ * later when device is unloading, the driver should always call free_irq()
+ * on all MSI-X vectors it has done request_irq() on before calling
+ * pci_disable_msix(). Failure to do so results in a BUG_ON() and a device
+ * will be left with MSI-X enabled and leaks its vectors.
+ *
+ * Return codes
+ *   0 - sucessful
+ *   other values - error
+ **/
+static int
+lpfc_sli_enable_msix(struct lpfc_hba *phba)
+{
+       int rc, i;
+       LPFC_MBOXQ_t *pmb;
+
+       /* Set up MSI-X multi-message vectors */
+       for (i = 0; i < LPFC_MSIX_VECTORS; i++)
+               phba->msix_entries[i].entry = i;
+
+       /* Configure MSI-X capability structure */
+       rc = pci_enable_msix(phba->pcidev, phba->msix_entries,
+                               ARRAY_SIZE(phba->msix_entries));
+       if (rc) {
+               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                               "0420 PCI enable MSI-X failed (%d)\n", rc);
+               goto msi_fail_out;
+       }
+       for (i = 0; i < LPFC_MSIX_VECTORS; i++)
+               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                               "0477 MSI-X entry[%d]: vector=x%x "
+                               "message=%d\n", i,
+                               phba->msix_entries[i].vector,
+                               phba->msix_entries[i].entry);
+       /*
+        * Assign MSI-X vectors to interrupt handlers
+        */
+
+       /* vector-0 is associated to slow-path handler */
+       rc = request_irq(phba->msix_entries[0].vector,
+                        &lpfc_sli_sp_intr_handler, IRQF_SHARED,
+                        LPFC_SP_DRIVER_HANDLER_NAME, phba);
+       if (rc) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+                               "0421 MSI-X slow-path request_irq failed "
+                               "(%d)\n", rc);
+               goto msi_fail_out;
+       }
+
+       /* vector-1 is associated to fast-path handler */
+       rc = request_irq(phba->msix_entries[1].vector,
+                        &lpfc_sli_fp_intr_handler, IRQF_SHARED,
+                        LPFC_FP_DRIVER_HANDLER_NAME, phba);
+
+       if (rc) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+                               "0429 MSI-X fast-path request_irq failed "
+                               "(%d)\n", rc);
+               goto irq_fail_out;
+       }
+
+       /*
+        * Configure HBA MSI-X attention conditions to messages
+        */
+       pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+
+       if (!pmb) {
+               rc = -ENOMEM;
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0474 Unable to allocate memory for issuing "
+                               "MBOX_CONFIG_MSI command\n");
+               goto mem_fail_out;
+       }
+       rc = lpfc_config_msi(phba, pmb);
+       if (rc)
+               goto mbx_fail_out;
+       rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
+       if (rc != MBX_SUCCESS) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
+                               "0351 Config MSI mailbox command failed, "
+                               "mbxCmd x%x, mbxStatus x%x\n",
+                               pmb->u.mb.mbxCommand, pmb->u.mb.mbxStatus);
+               goto mbx_fail_out;
+       }
+
+       /* Free memory allocated for mailbox command */
+       mempool_free(pmb, phba->mbox_mem_pool);
+       return rc;
+
+mbx_fail_out:
+       /* Free memory allocated for mailbox command */
+       mempool_free(pmb, phba->mbox_mem_pool);
+
+mem_fail_out:
+       /* free the irq already requested */
+       free_irq(phba->msix_entries[1].vector, phba);
+
+irq_fail_out:
+       /* free the irq already requested */
+       free_irq(phba->msix_entries[0].vector, phba);
+
+msi_fail_out:
+       /* Unconfigure MSI-X capability structure */
+       pci_disable_msix(phba->pcidev);
+       return rc;
+}
+
+/**
+ * lpfc_sli_disable_msix - Disable MSI-X interrupt mode on SLI-3 device.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to release the MSI-X vectors and then disable the
+ * MSI-X interrupt mode to device with SLI-3 interface spec.
+ **/
+static void
+lpfc_sli_disable_msix(struct lpfc_hba *phba)
+{
+       int i;
+
+       /* Free up MSI-X multi-message vectors */
+       for (i = 0; i < LPFC_MSIX_VECTORS; i++)
+               free_irq(phba->msix_entries[i].vector, phba);
+       /* Disable MSI-X */
+       pci_disable_msix(phba->pcidev);
+
+       return;
+}
+
+/**
+ * lpfc_sli_enable_msi - Enable MSI interrupt mode on SLI-3 device.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to enable the MSI interrupt mode to device with
+ * SLI-3 interface spec. The kernel function pci_enable_msi() is called to
+ * enable the MSI vector. The device driver is responsible for calling the
+ * request_irq() to register MSI vector with a interrupt the handler, which
+ * is done in this function.
+ *
+ * Return codes
+ *     0 - sucessful
+ *     other values - error
+ */
+static int
+lpfc_sli_enable_msi(struct lpfc_hba *phba)
+{
+       int rc;
+
+       rc = pci_enable_msi(phba->pcidev);
+       if (!rc)
+               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                               "0462 PCI enable MSI mode success.\n");
+       else {
+               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                               "0471 PCI enable MSI mode failed (%d)\n", rc);
+               return rc;
+       }
+
+       rc = request_irq(phba->pcidev->irq, lpfc_sli_intr_handler,
+                        IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+       if (rc) {
+               pci_disable_msi(phba->pcidev);
+               lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+                               "0478 MSI request_irq failed (%d)\n", rc);
+       }
+       return rc;
+}
+
+/**
+ * lpfc_sli_disable_msi - Disable MSI interrupt mode to SLI-3 device.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to disable the MSI interrupt mode to device with
+ * SLI-3 interface spec. The driver calls free_irq() on MSI vector it has
+ * done request_irq() on before calling pci_disable_msi(). Failure to do so
+ * results in a BUG_ON() and a device will be left with MSI enabled and leaks
+ * its vector.
+ */
+static void
+lpfc_sli_disable_msi(struct lpfc_hba *phba)
+{
+       free_irq(phba->pcidev->irq, phba);
+       pci_disable_msi(phba->pcidev);
+       return;
+}
+
+/**
+ * lpfc_sli_enable_intr - Enable device interrupt to SLI-3 device.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to enable device interrupt and associate driver's
+ * interrupt handler(s) to interrupt vector(s) to device with SLI-3 interface
+ * spec. Depends on the interrupt mode configured to the driver, the driver
+ * will try to fallback from the configured interrupt mode to an interrupt
+ * mode which is supported by the platform, kernel, and device in the order
+ * of:
+ * MSI-X -> MSI -> IRQ.
+ *
+ * Return codes
+ *   0 - sucessful
+ *   other values - error
+ **/
+static uint32_t
+lpfc_sli_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode)
+{
+       uint32_t intr_mode = LPFC_INTR_ERROR;
+       int retval;
+
+       if (cfg_mode == 2) {
+               /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */
+               retval = lpfc_sli_config_port(phba, LPFC_SLI_REV3);
+               if (!retval) {
+                       /* Now, try to enable MSI-X interrupt mode */
+                       retval = lpfc_sli_enable_msix(phba);
+                       if (!retval) {
+                               /* Indicate initialization to MSI-X mode */
+                               phba->intr_type = MSIX;
+                               intr_mode = 2;
+                       }
+               }
+       }
+
+       /* Fallback to MSI if MSI-X initialization failed */
+       if (cfg_mode >= 1 && phba->intr_type == NONE) {
+               retval = lpfc_sli_enable_msi(phba);
+               if (!retval) {
+                       /* Indicate initialization to MSI mode */
+                       phba->intr_type = MSI;
+                       intr_mode = 1;
+               }
+       }
+
+       /* Fallback to INTx if both MSI-X/MSI initalization failed */
+       if (phba->intr_type == NONE) {
+               retval = request_irq(phba->pcidev->irq, lpfc_sli_intr_handler,
+                                    IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+               if (!retval) {
+                       /* Indicate initialization to INTx mode */
+                       phba->intr_type = INTx;
+                       intr_mode = 0;
+               }
+       }
+       return intr_mode;
+}
+
+/**
+ * lpfc_sli_disable_intr - Disable device interrupt to SLI-3 device.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to disable device interrupt and disassociate the
+ * driver's interrupt handler(s) from interrupt vector(s) to device with
+ * SLI-3 interface spec. Depending on the interrupt mode, the driver will
+ * release the interrupt vector(s) for the message signaled interrupt.
+ **/
+static void
+lpfc_sli_disable_intr(struct lpfc_hba *phba)
+{
+       /* Disable the currently initialized interrupt mode */
+       if (phba->intr_type == MSIX)
+               lpfc_sli_disable_msix(phba);
+       else if (phba->intr_type == MSI)
+               lpfc_sli_disable_msi(phba);
+       else if (phba->intr_type == INTx)
+               free_irq(phba->pcidev->irq, phba);
+
+       /* Reset interrupt management states */
+       phba->intr_type = NONE;
+       phba->sli.slistat.sli_intr = 0;
+
+       return;
+}
+
+/**
+ * lpfc_sli4_enable_msix - Enable MSI-X interrupt mode to SLI-4 device
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to enable the MSI-X interrupt vectors to device
+ * with SLI-4 interface spec. The kernel function pci_enable_msix() is called
+ * to enable the MSI-X vectors. Note that pci_enable_msix(), once invoked,
+ * enables either all or nothing, depending on the current availability of
+ * PCI vector resources. The device driver is responsible for calling the
+ * individual request_irq() to register each MSI-X vector with a interrupt
+ * handler, which is done in this function. Note that later when device is
+ * unloading, the driver should always call free_irq() on all MSI-X vectors
+ * it has done request_irq() on before calling pci_disable_msix(). Failure
+ * to do so results in a BUG_ON() and a device will be left with MSI-X
+ * enabled and leaks its vectors.
+ *
+ * Return codes
+ * 0 - sucessful
+ * other values - error
+ **/
+static int
+lpfc_sli4_enable_msix(struct lpfc_hba *phba)
+{
+       int rc, index;
+
+       /* Set up MSI-X multi-message vectors */
+       for (index = 0; index < phba->sli4_hba.cfg_eqn; index++)
+               phba->sli4_hba.msix_entries[index].entry = index;
+
+       /* Configure MSI-X capability structure */
+       rc = pci_enable_msix(phba->pcidev, phba->sli4_hba.msix_entries,
+                            phba->sli4_hba.cfg_eqn);
+       if (rc) {
+               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                               "0484 PCI enable MSI-X failed (%d)\n", rc);
+               goto msi_fail_out;
+       }
+       /* Log MSI-X vector assignment */
+       for (index = 0; index < phba->sli4_hba.cfg_eqn; index++)
+               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                               "0489 MSI-X entry[%d]: vector=x%x "
+                               "message=%d\n", index,
+                               phba->sli4_hba.msix_entries[index].vector,
+                               phba->sli4_hba.msix_entries[index].entry);
+       /*
+        * Assign MSI-X vectors to interrupt handlers
+        */
+
+       /* The first vector must associated to slow-path handler for MQ */
+       rc = request_irq(phba->sli4_hba.msix_entries[0].vector,
+                        &lpfc_sli4_sp_intr_handler, IRQF_SHARED,
+                        LPFC_SP_DRIVER_HANDLER_NAME, phba);
+       if (rc) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+                               "0485 MSI-X slow-path request_irq failed "
+                               "(%d)\n", rc);
+               goto msi_fail_out;
+       }
+
+       /* The rest of the vector(s) are associated to fast-path handler(s) */
+       for (index = 1; index < phba->sli4_hba.cfg_eqn; index++) {
+               phba->sli4_hba.fcp_eq_hdl[index - 1].idx = index - 1;
+               phba->sli4_hba.fcp_eq_hdl[index - 1].phba = phba;
+               rc = request_irq(phba->sli4_hba.msix_entries[index].vector,
+                                &lpfc_sli4_fp_intr_handler, IRQF_SHARED,
+                                LPFC_FP_DRIVER_HANDLER_NAME,
+                                &phba->sli4_hba.fcp_eq_hdl[index - 1]);
+               if (rc) {
+                       lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+                                       "0486 MSI-X fast-path (%d) "
+                                       "request_irq failed (%d)\n", index, rc);
+                       goto cfg_fail_out;
+               }
+       }
+
+       return rc;
+
+cfg_fail_out:
+       /* free the irq already requested */
+       for (--index; index >= 1; index--)
+               free_irq(phba->sli4_hba.msix_entries[index - 1].vector,
+                        &phba->sli4_hba.fcp_eq_hdl[index - 1]);
+
+       /* free the irq already requested */
+       free_irq(phba->sli4_hba.msix_entries[0].vector, phba);
+
+msi_fail_out:
+       /* Unconfigure MSI-X capability structure */
+       pci_disable_msix(phba->pcidev);
+       return rc;
+}
+
+/**
+ * lpfc_sli4_disable_msix - Disable MSI-X interrupt mode to SLI-4 device
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to release the MSI-X vectors and then disable the
+ * MSI-X interrupt mode to device with SLI-4 interface spec.
+ **/
+static void
+lpfc_sli4_disable_msix(struct lpfc_hba *phba)
+{
+       int index;
+
+       /* Free up MSI-X multi-message vectors */
+       free_irq(phba->sli4_hba.msix_entries[0].vector, phba);
+
+       for (index = 1; index < phba->sli4_hba.cfg_eqn; index++)
+               free_irq(phba->sli4_hba.msix_entries[index].vector,
+                        &phba->sli4_hba.fcp_eq_hdl[index - 1]);
+       /* Disable MSI-X */
+       pci_disable_msix(phba->pcidev);
+
+       return;
+}
+
+/**
+ * lpfc_sli4_enable_msi - Enable MSI interrupt mode to SLI-4 device
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to enable the MSI interrupt mode to device with
+ * SLI-4 interface spec. The kernel function pci_enable_msi() is called
+ * to enable the MSI vector. The device driver is responsible for calling
+ * the request_irq() to register MSI vector with a interrupt the handler,
+ * which is done in this function.
+ *
+ * Return codes
+ *     0 - sucessful
+ *     other values - error
+ **/
+static int
+lpfc_sli4_enable_msi(struct lpfc_hba *phba)
+{
+       int rc, index;
+
+       rc = pci_enable_msi(phba->pcidev);
+       if (!rc)
+               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                               "0487 PCI enable MSI mode success.\n");
+       else {
+               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                               "0488 PCI enable MSI mode failed (%d)\n", rc);
+               return rc;
+       }
+
+       rc = request_irq(phba->pcidev->irq, lpfc_sli4_intr_handler,
+                        IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+       if (rc) {
+               pci_disable_msi(phba->pcidev);
+               lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+                               "0490 MSI request_irq failed (%d)\n", rc);
+       }
+
+       for (index = 0; index < phba->cfg_fcp_eq_count; index++) {
+               phba->sli4_hba.fcp_eq_hdl[index].idx = index;
+               phba->sli4_hba.fcp_eq_hdl[index].phba = phba;
+       }
+
+       return rc;
+}
+
+/**
+ * lpfc_sli4_disable_msi - Disable MSI interrupt mode to SLI-4 device
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to disable the MSI interrupt mode to device with
+ * SLI-4 interface spec. The driver calls free_irq() on MSI vector it has
+ * done request_irq() on before calling pci_disable_msi(). Failure to do so
+ * results in a BUG_ON() and a device will be left with MSI enabled and leaks
+ * its vector.
+ **/
+static void
+lpfc_sli4_disable_msi(struct lpfc_hba *phba)
+{
+       free_irq(phba->pcidev->irq, phba);
+       pci_disable_msi(phba->pcidev);
+       return;
+}
+
+/**
+ * lpfc_sli4_enable_intr - Enable device interrupt to SLI-4 device
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to enable device interrupt and associate driver's
+ * interrupt handler(s) to interrupt vector(s) to device with SLI-4
+ * interface spec. Depends on the interrupt mode configured to the driver,
+ * the driver will try to fallback from the configured interrupt mode to an
+ * interrupt mode which is supported by the platform, kernel, and device in
+ * the order of:
+ * MSI-X -> MSI -> IRQ.
+ *
+ * Return codes
+ *     0 - sucessful
+ *     other values - error
+ **/
+static uint32_t
+lpfc_sli4_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode)
+{
+       uint32_t intr_mode = LPFC_INTR_ERROR;
+       int retval, index;
+
+       if (cfg_mode == 2) {
+               /* Preparation before conf_msi mbox cmd */
+               retval = 0;
+               if (!retval) {
+                       /* Now, try to enable MSI-X interrupt mode */
+                       retval = lpfc_sli4_enable_msix(phba);
+                       if (!retval) {
+                               /* Indicate initialization to MSI-X mode */
+                               phba->intr_type = MSIX;
+                               intr_mode = 2;
+                       }
+               }
+       }
+
+       /* Fallback to MSI if MSI-X initialization failed */
+       if (cfg_mode >= 1 && phba->intr_type == NONE) {
+               retval = lpfc_sli4_enable_msi(phba);
+               if (!retval) {
+                       /* Indicate initialization to MSI mode */
+                       phba->intr_type = MSI;
+                       intr_mode = 1;
+               }
+       }
+
+       /* Fallback to INTx if both MSI-X/MSI initalization failed */
+       if (phba->intr_type == NONE) {
+               retval = request_irq(phba->pcidev->irq, lpfc_sli4_intr_handler,
+                                    IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+               if (!retval) {
+                       /* Indicate initialization to INTx mode */
+                       phba->intr_type = INTx;
+                       intr_mode = 0;
+                       for (index = 0; index < phba->cfg_fcp_eq_count;
+                            index++) {
+                               phba->sli4_hba.fcp_eq_hdl[index].idx = index;
+                               phba->sli4_hba.fcp_eq_hdl[index].phba = phba;
+                       }
+               }
+       }
+       return intr_mode;
+}
+
+/**
+ * lpfc_sli4_disable_intr - Disable device interrupt to SLI-4 device
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to disable device interrupt and disassociate
+ * the driver's interrupt handler(s) from interrupt vector(s) to device
+ * with SLI-4 interface spec. Depending on the interrupt mode, the driver
+ * will release the interrupt vector(s) for the message signaled interrupt.
+ **/
+static void
+lpfc_sli4_disable_intr(struct lpfc_hba *phba)
+{
+       /* Disable the currently initialized interrupt mode */
+       if (phba->intr_type == MSIX)
+               lpfc_sli4_disable_msix(phba);
+       else if (phba->intr_type == MSI)
+               lpfc_sli4_disable_msi(phba);
+       else if (phba->intr_type == INTx)
+               free_irq(phba->pcidev->irq, phba);
+
+       /* Reset interrupt management states */
+       phba->intr_type = NONE;
+       phba->sli.slistat.sli_intr = 0;
+
+       return;
+}
+
+/**
+ * lpfc_unset_hba - Unset SLI3 hba device initialization
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to unset the HBA device initialization steps to
+ * a device with SLI-3 interface spec.
+ **/
+static void
+lpfc_unset_hba(struct lpfc_hba *phba)
+{
+       struct lpfc_vport *vport = phba->pport;
+       struct Scsi_Host  *shost = lpfc_shost_from_vport(vport);
+
+       spin_lock_irq(shost->host_lock);
+       vport->load_flag |= FC_UNLOADING;
+       spin_unlock_irq(shost->host_lock);
+
+       lpfc_stop_hba_timers(phba);
+
+       phba->pport->work_port_events = 0;
+
+       lpfc_sli_hba_down(phba);
+
+       lpfc_sli_brdrestart(phba);
+
+       lpfc_sli_disable_intr(phba);
+
+       return;
+}
+
+/**
+ * lpfc_sli4_unset_hba - Unset SLI4 hba device initialization.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to unset the HBA device initialization steps to
+ * a device with SLI-4 interface spec.
+ **/
+static void
+lpfc_sli4_unset_hba(struct lpfc_hba *phba)
+{
+       struct lpfc_vport *vport = phba->pport;
+       struct Scsi_Host  *shost = lpfc_shost_from_vport(vport);
+
+       spin_lock_irq(shost->host_lock);
+       vport->load_flag |= FC_UNLOADING;
+       spin_unlock_irq(shost->host_lock);
+
+       phba->pport->work_port_events = 0;
+
+       lpfc_sli4_hba_down(phba);
+
+       lpfc_sli4_disable_intr(phba);
+
+       return;
+}
+
+/**
+ * lpfc_sli4_hba_unset - Unset the fcoe hba
+ * @phba: Pointer to HBA context object.
+ *
+ * This function is called in the SLI4 code path to reset the HBA's FCoE
+ * function. The caller is not required to hold any lock. This routine
+ * issues PCI function reset mailbox command to reset the FCoE function.
+ * At the end of the function, it calls lpfc_hba_down_post function to
+ * free any pending commands.
+ **/
+static void
+lpfc_sli4_hba_unset(struct lpfc_hba *phba)
+{
+       int wait_cnt = 0;
+       LPFC_MBOXQ_t *mboxq;
+
+       lpfc_stop_hba_timers(phba);
+       phba->sli4_hba.intr_enable = 0;
+
+       /*
+        * Gracefully wait out the potential current outstanding asynchronous
+        * mailbox command.
+        */
+
+       /* First, block any pending async mailbox command from posted */
+       spin_lock_irq(&phba->hbalock);
+       phba->sli.sli_flag |= LPFC_SLI_ASYNC_MBX_BLK;
+       spin_unlock_irq(&phba->hbalock);
+       /* Now, trying to wait it out if we can */
+       while (phba->sli.sli_flag & LPFC_SLI_MBOX_ACTIVE) {
+               msleep(10);
+               if (++wait_cnt > LPFC_ACTIVE_MBOX_WAIT_CNT)
+                       break;
+       }
+       /* Forcefully release the outstanding mailbox command if timed out */
+       if (phba->sli.sli_flag & LPFC_SLI_MBOX_ACTIVE) {
+               spin_lock_irq(&phba->hbalock);
+               mboxq = phba->sli.mbox_active;
+               mboxq->u.mb.mbxStatus = MBX_NOT_FINISHED;
+               __lpfc_mbox_cmpl_put(phba, mboxq);
+               phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+               phba->sli.mbox_active = NULL;
+               spin_unlock_irq(&phba->hbalock);
+       }
+
+       /* Tear down the queues in the HBA */
+       lpfc_sli4_queue_unset(phba);
+
+       /* Disable PCI subsystem interrupt */
+       lpfc_sli4_disable_intr(phba);
+
+       /* Stop kthread signal shall trigger work_done one more time */
+       kthread_stop(phba->worker_thread);
+
+       /* Stop the SLI4 device port */
+       phba->pport->work_port_events = 0;
+}
+
+/**
+ * lpfc_pci_probe_one_s3 - PCI probe func to reg SLI-3 device to PCI subsystem.
+ * @pdev: pointer to PCI device
+ * @pid: pointer to PCI device identifier
+ *
+ * This routine is to be called to attach a device with SLI-3 interface spec
+ * to the PCI subsystem. When an Emulex HBA with SLI-3 interface spec is
+ * presented on PCI bus, the kernel PCI subsystem looks at PCI device-specific
+ * information of the device and driver to see if the driver state that it can
+ * support this kind of device. If the match is successful, the driver core
+ * invokes this routine. If this routine determines it can claim the HBA, it
+ * does all the initialization that it needs to do to handle the HBA properly.
+ *
+ * Return code
+ *     0 - driver can claim the device
+ *     negative value - driver can not claim the device
+ **/
+static int __devinit
+lpfc_pci_probe_one_s3(struct pci_dev *pdev, const struct pci_device_id *pid)
+{
+       struct lpfc_hba   *phba;
+       struct lpfc_vport *vport = NULL;
+       int error;
+       uint32_t cfg_mode, intr_mode;
+
+       /* Allocate memory for HBA structure */
+       phba = lpfc_hba_alloc(pdev);
+       if (!phba)
+               return -ENOMEM;
+
+       /* Perform generic PCI device enabling operation */
+       error = lpfc_enable_pci_dev(phba);
+       if (error) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1401 Failed to enable pci device.\n");
+               goto out_free_phba;
+       }
+
+       /* Set up SLI API function jump table for PCI-device group-0 HBAs */
+       error = lpfc_api_table_setup(phba, LPFC_PCI_DEV_LP);
+       if (error)
+               goto out_disable_pci_dev;
+
+       /* Set up SLI-3 specific device PCI memory space */
+       error = lpfc_sli_pci_mem_setup(phba);
+       if (error) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1402 Failed to set up pci memory space.\n");
+               goto out_disable_pci_dev;
+       }
+
+       /* Set up phase-1 common device driver resources */
+       error = lpfc_setup_driver_resource_phase1(phba);
+       if (error) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1403 Failed to set up driver resource.\n");
+               goto out_unset_pci_mem_s3;
+       }
+
+       /* Set up SLI-3 specific device driver resources */
+       error = lpfc_sli_driver_resource_setup(phba);
+       if (error) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1404 Failed to set up driver resource.\n");
+               goto out_unset_pci_mem_s3;
+       }
+
+       /* Initialize and populate the iocb list per host */
+       error = lpfc_init_iocb_list(phba, LPFC_IOCB_LIST_CNT);
+       if (error) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1405 Failed to initialize iocb list.\n");
+               goto out_unset_driver_resource_s3;
+       }
+
+       /* Set up common device driver resources */
+       error = lpfc_setup_driver_resource_phase2(phba);
+       if (error) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1406 Failed to set up driver resource.\n");
+               goto out_free_iocb_list;
+       }
+
+       /* Create SCSI host to the physical port */
+       error = lpfc_create_shost(phba);
+       if (error) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1407 Failed to create scsi host.\n");
+               goto out_unset_driver_resource;
+       }
+
+       /* Configure sysfs attributes */
+       vport = phba->pport;
+       error = lpfc_alloc_sysfs_attr(vport);
+       if (error) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1476 Failed to allocate sysfs attr\n");
+               goto out_destroy_shost;
+       }
+
+       /* Now, trying to enable interrupt and bring up the device */
+       cfg_mode = phba->cfg_use_msi;
+       while (true) {
+               /* Put device to a known state before enabling interrupt */
+               lpfc_stop_port(phba);
+               /* Configure and enable interrupt */
+               intr_mode = lpfc_sli_enable_intr(phba, cfg_mode);
+               if (intr_mode == LPFC_INTR_ERROR) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                       "0431 Failed to enable interrupt.\n");
+                       error = -ENODEV;
+                       goto out_free_sysfs_attr;
+               }
+               /* SLI-3 HBA setup */
+               if (lpfc_sli_hba_setup(phba)) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                       "1477 Failed to set up hba\n");
+                       error = -ENODEV;
+                       goto out_remove_device;
+               }
+
+               /* Wait 50ms for the interrupts of previous mailbox commands */
+               msleep(50);
+               /* Check active interrupts on message signaled interrupts */
+               if (intr_mode == 0 ||
+                   phba->sli.slistat.sli_intr > LPFC_MSIX_VECTORS) {
+                       /* Log the current active interrupt mode */
+                       phba->intr_mode = intr_mode;
+                       lpfc_log_intr_mode(phba, intr_mode);
+                       break;
+               } else {
+                       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                                       "0447 Configure interrupt mode (%d) "
+                                       "failed active interrupt test.\n",
+                                       intr_mode);
+                       /* Disable the current interrupt mode */
+                       lpfc_sli_disable_intr(phba);
+                       /* Try next level of interrupt mode */
+                       cfg_mode = --intr_mode;
+               }
+       }
+
+       /* Perform post initialization setup */
+       lpfc_post_init_setup(phba);
+
+       /* Check if there are static vports to be created. */
+       lpfc_create_static_vport(phba);
+
+       return 0;
+
+out_remove_device:
+       lpfc_unset_hba(phba);
+out_free_sysfs_attr:
+       lpfc_free_sysfs_attr(vport);
+out_destroy_shost:
+       lpfc_destroy_shost(phba);
+out_unset_driver_resource:
+       lpfc_unset_driver_resource_phase2(phba);
+out_free_iocb_list:
+       lpfc_free_iocb_list(phba);
+out_unset_driver_resource_s3:
+       lpfc_sli_driver_resource_unset(phba);
+out_unset_pci_mem_s3:
+       lpfc_sli_pci_mem_unset(phba);
+out_disable_pci_dev:
+       lpfc_disable_pci_dev(phba);
+out_free_phba:
+       lpfc_hba_free(phba);
+       return error;
+}
+
+/**
+ * lpfc_pci_remove_one_s3 - PCI func to unreg SLI-3 device from PCI subsystem.
+ * @pdev: pointer to PCI device
+ *
+ * This routine is to be called to disattach a device with SLI-3 interface
+ * spec from PCI subsystem. When an Emulex HBA with SLI-3 interface spec is
+ * removed from PCI bus, it performs all the necessary cleanup for the HBA
+ * device to be removed from the PCI subsystem properly.
+ **/
+static void __devexit
+lpfc_pci_remove_one_s3(struct pci_dev *pdev)
+{
+       struct Scsi_Host  *shost = pci_get_drvdata(pdev);
+       struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+       struct lpfc_vport **vports;
+       struct lpfc_hba   *phba = vport->phba;
+       int i;
+       int bars = pci_select_bars(pdev, IORESOURCE_MEM);
+
+       spin_lock_irq(&phba->hbalock);
+       vport->load_flag |= FC_UNLOADING;
+       spin_unlock_irq(&phba->hbalock);
+
+       lpfc_free_sysfs_attr(vport);
+
+       /* Release all the vports against this physical port */
+       vports = lpfc_create_vport_work_array(phba);
+       if (vports != NULL)
+               for (i = 1; i <= phba->max_vports && vports[i] != NULL; i++)
+                       fc_vport_terminate(vports[i]->fc_vport);
+       lpfc_destroy_vport_work_array(phba, vports);
+
+       /* Remove FC host and then SCSI host with the physical port */
+       fc_remove_host(shost);
+       scsi_remove_host(shost);
+       lpfc_cleanup(vport);
+
+       /*
+        * Bring down the SLI Layer. This step disable all interrupts,
+        * clears the rings, discards all mailbox commands, and resets
+        * the HBA.
+        */
+
+       /* HBA interrupt will be diabled after this call */
+       lpfc_sli_hba_down(phba);
+       /* Stop kthread signal shall trigger work_done one more time */
+       kthread_stop(phba->worker_thread);
+       /* Final cleanup of txcmplq and reset the HBA */
+       lpfc_sli_brdrestart(phba);
+
+       lpfc_stop_hba_timers(phba);
+       spin_lock_irq(&phba->hbalock);
+       list_del_init(&vport->listentry);
+       spin_unlock_irq(&phba->hbalock);
+
+       lpfc_debugfs_terminate(vport);
+
+       /* Disable interrupt */
+       lpfc_sli_disable_intr(phba);
+
+       pci_set_drvdata(pdev, NULL);
+       scsi_host_put(shost);
+
+       /*
+        * Call scsi_free before mem_free since scsi bufs are released to their
+        * corresponding pools here.
+        */
+       lpfc_scsi_free(phba);
+       lpfc_mem_free_all(phba);
+
+       dma_free_coherent(&pdev->dev, lpfc_sli_hbq_size(),
+                         phba->hbqslimp.virt, phba->hbqslimp.phys);
+
+       /* Free resources associated with SLI2 interface */
+       dma_free_coherent(&pdev->dev, SLI2_SLIM_SIZE,
+                         phba->slim2p.virt, phba->slim2p.phys);
+
+       /* unmap adapter SLIM and Control Registers */
+       iounmap(phba->ctrl_regs_memmap_p);
+       iounmap(phba->slim_memmap_p);
+
+       lpfc_hba_free(phba);
 
-out_remove_device:
-       spin_lock_irq(shost->host_lock);
-       vport->load_flag |= FC_UNLOADING;
-       spin_unlock_irq(shost->host_lock);
-       lpfc_stop_phba_timers(phba);
-       phba->pport->work_port_events = 0;
-       lpfc_disable_intr(phba);
-       lpfc_sli_hba_down(phba);
-       lpfc_sli_brdrestart(phba);
-out_free_sysfs_attr:
-       lpfc_free_sysfs_attr(vport);
-out_destroy_port:
-       destroy_port(vport);
-out_kthread_stop:
-       kthread_stop(phba->worker_thread);
-out_free_iocbq:
-       list_for_each_entry_safe(iocbq_entry, iocbq_next,
-                                               &phba->lpfc_iocb_list, list) {
-               kfree(iocbq_entry);
-               phba->total_iocbq_bufs--;
-       }
-       lpfc_mem_free(phba);
-out_free_hbqslimp:
-       dma_free_coherent(&pdev->dev, lpfc_sli_hbq_size(),
-                         phba->hbqslimp.virt, phba->hbqslimp.phys);
-out_free_slim:
-       dma_free_coherent(&pdev->dev, SLI2_SLIM_SIZE,
-                         phba->slim2p.virt, phba->slim2p.phys);
-out_iounmap:
-       iounmap(phba->ctrl_regs_memmap_p);
-out_iounmap_slim:
-       iounmap(phba->slim_memmap_p);
-out_idr_remove:
-       idr_remove(&lpfc_hba_index, phba->brd_no);
-out_free_phba:
-       kfree(phba);
-out_release_regions:
        pci_release_selected_regions(pdev, bars);
-out_disable_device:
        pci_disable_device(pdev);
-out:
-       pci_set_drvdata(pdev, NULL);
-       if (shost)
-               scsi_host_put(shost);
+}
+
+/**
+ * lpfc_pci_suspend_one_s3 - PCI func to suspend SLI-3 device for power mgmnt
+ * @pdev: pointer to PCI device
+ * @msg: power management message
+ *
+ * This routine is to be called from the kernel's PCI subsystem to support
+ * system Power Management (PM) to device with SLI-3 interface spec. When
+ * PM invokes this method, it quiesces the device by stopping the driver's
+ * worker thread for the device, turning off device's interrupt and DMA,
+ * and bring the device offline. Note that as the driver implements the
+ * minimum PM requirements to a power-aware driver's PM support for the
+ * suspend/resume -- all the possible PM messages (SUSPEND, HIBERNATE, FREEZE)
+ * to the suspend() method call will be treated as SUSPEND and the driver will
+ * fully reinitialize its device during resume() method call, the driver will
+ * set device to PCI_D3hot state in PCI config space instead of setting it
+ * according to the @msg provided by the PM.
+ *
+ * Return code
+ *     0 - driver suspended the device
+ *     Error otherwise
+ **/
+static int
+lpfc_pci_suspend_one_s3(struct pci_dev *pdev, pm_message_t msg)
+{
+       struct Scsi_Host *shost = pci_get_drvdata(pdev);
+       struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+
+       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                       "0473 PCI device Power Management suspend.\n");
+
+       /* Bring down the device */
+       lpfc_offline_prep(phba);
+       lpfc_offline(phba);
+       kthread_stop(phba->worker_thread);
+
+       /* Disable interrupt from device */
+       lpfc_sli_disable_intr(phba);
+
+       /* Save device state to PCI config space */
+       pci_save_state(pdev);
+       pci_set_power_state(pdev, PCI_D3hot);
+
+       return 0;
+}
+
+/**
+ * lpfc_pci_resume_one_s3 - PCI func to resume SLI-3 device for power mgmnt
+ * @pdev: pointer to PCI device
+ *
+ * This routine is to be called from the kernel's PCI subsystem to support
+ * system Power Management (PM) to device with SLI-3 interface spec. When PM
+ * invokes this method, it restores the device's PCI config space state and
+ * fully reinitializes the device and brings it online. Note that as the
+ * driver implements the minimum PM requirements to a power-aware driver's
+ * PM for suspend/resume -- all the possible PM messages (SUSPEND, HIBERNATE,
+ * FREEZE) to the suspend() method call will be treated as SUSPEND and the
+ * driver will fully reinitialize its device during resume() method call,
+ * the device will be set to PCI_D0 directly in PCI config space before
+ * restoring the state.
+ *
+ * Return code
+ *     0 - driver suspended the device
+ *     Error otherwise
+ **/
+static int
+lpfc_pci_resume_one_s3(struct pci_dev *pdev)
+{
+       struct Scsi_Host *shost = pci_get_drvdata(pdev);
+       struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+       uint32_t intr_mode;
+       int error;
+
+       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                       "0452 PCI device Power Management resume.\n");
+
+       /* Restore device state from PCI config space */
+       pci_set_power_state(pdev, PCI_D0);
+       pci_restore_state(pdev);
+       if (pdev->is_busmaster)
+               pci_set_master(pdev);
+
+       /* Startup the kernel thread for this host adapter. */
+       phba->worker_thread = kthread_run(lpfc_do_work, phba,
+                                       "lpfc_worker_%d", phba->brd_no);
+       if (IS_ERR(phba->worker_thread)) {
+               error = PTR_ERR(phba->worker_thread);
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0434 PM resume failed to start worker "
+                               "thread: error=x%x.\n", error);
+               return error;
+       }
+
+       /* Configure and enable interrupt */
+       intr_mode = lpfc_sli_enable_intr(phba, phba->intr_mode);
+       if (intr_mode == LPFC_INTR_ERROR) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0430 PM resume Failed to enable interrupt\n");
+               return -EIO;
+       } else
+               phba->intr_mode = intr_mode;
+
+       /* Restart HBA and bring it online */
+       lpfc_sli_brdrestart(phba);
+       lpfc_online(phba);
+
+       /* Log the current active interrupt mode */
+       lpfc_log_intr_mode(phba, phba->intr_mode);
+
+       return 0;
+}
+
+/**
+ * lpfc_io_error_detected_s3 - Method for handling SLI-3 device PCI I/O error
+ * @pdev: pointer to PCI device.
+ * @state: the current PCI connection state.
+ *
+ * This routine is called from the PCI subsystem for I/O error handling to
+ * device with SLI-3 interface spec. This function is called by the PCI
+ * subsystem after a PCI bus error affecting this device has been detected.
+ * When this function is invoked, it will need to stop all the I/Os and
+ * interrupt(s) to the device. Once that is done, it will return
+ * PCI_ERS_RESULT_NEED_RESET for the PCI subsystem to perform proper recovery
+ * as desired.
+ *
+ * Return codes
+ *     PCI_ERS_RESULT_NEED_RESET - need to reset before recovery
+ *     PCI_ERS_RESULT_DISCONNECT - device could not be recovered
+ **/
+static pci_ers_result_t
+lpfc_io_error_detected_s3(struct pci_dev *pdev, pci_channel_state_t state)
+{
+       struct Scsi_Host *shost = pci_get_drvdata(pdev);
+       struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+       struct lpfc_sli *psli = &phba->sli;
+       struct lpfc_sli_ring  *pring;
+
+       if (state == pci_channel_io_perm_failure) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0472 PCI channel I/O permanent failure\n");
+               /* Block all SCSI devices' I/Os on the host */
+               lpfc_scsi_dev_block(phba);
+               /* Clean up all driver's outstanding SCSI I/Os */
+               lpfc_sli_flush_fcp_rings(phba);
+               return PCI_ERS_RESULT_DISCONNECT;
+       }
+
+       pci_disable_device(pdev);
+       /*
+        * There may be I/Os dropped by the firmware.
+        * Error iocb (I/O) on txcmplq and let the SCSI layer
+        * retry it after re-establishing link.
+        */
+       pring = &psli->ring[psli->fcp_ring];
+       lpfc_sli_abort_iocb_ring(phba, pring);
+
+       /* Disable interrupt */
+       lpfc_sli_disable_intr(phba);
+
+       /* Request a slot reset. */
+       return PCI_ERS_RESULT_NEED_RESET;
+}
+
+/**
+ * lpfc_io_slot_reset_s3 - Method for restarting PCI SLI-3 device from scratch.
+ * @pdev: pointer to PCI device.
+ *
+ * This routine is called from the PCI subsystem for error handling to
+ * device with SLI-3 interface spec. This is called after PCI bus has been
+ * reset to restart the PCI card from scratch, as if from a cold-boot.
+ * During the PCI subsystem error recovery, after driver returns
+ * PCI_ERS_RESULT_NEED_RESET, the PCI subsystem will perform proper error
+ * recovery and then call this routine before calling the .resume method
+ * to recover the device. This function will initialize the HBA device,
+ * enable the interrupt, but it will just put the HBA to offline state
+ * without passing any I/O traffic.
+ *
+ * Return codes
+ *     PCI_ERS_RESULT_RECOVERED - the device has been recovered
+ *     PCI_ERS_RESULT_DISCONNECT - device could not be recovered
+ */
+static pci_ers_result_t
+lpfc_io_slot_reset_s3(struct pci_dev *pdev)
+{
+       struct Scsi_Host *shost = pci_get_drvdata(pdev);
+       struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+       struct lpfc_sli *psli = &phba->sli;
+       uint32_t intr_mode;
+
+       dev_printk(KERN_INFO, &pdev->dev, "recovering from a slot reset.\n");
+       if (pci_enable_device_mem(pdev)) {
+               printk(KERN_ERR "lpfc: Cannot re-enable "
+                       "PCI device after reset.\n");
+               return PCI_ERS_RESULT_DISCONNECT;
+       }
+
+       pci_restore_state(pdev);
+       if (pdev->is_busmaster)
+               pci_set_master(pdev);
+
+       spin_lock_irq(&phba->hbalock);
+       psli->sli_flag &= ~LPFC_SLI_ACTIVE;
+       spin_unlock_irq(&phba->hbalock);
+
+       /* Configure and enable interrupt */
+       intr_mode = lpfc_sli_enable_intr(phba, phba->intr_mode);
+       if (intr_mode == LPFC_INTR_ERROR) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0427 Cannot re-enable interrupt after "
+                               "slot reset.\n");
+               return PCI_ERS_RESULT_DISCONNECT;
+       } else
+               phba->intr_mode = intr_mode;
+
+       /* Take device offline; this will perform cleanup */
+       lpfc_offline(phba);
+       lpfc_sli_brdrestart(phba);
+
+       /* Log the current active interrupt mode */
+       lpfc_log_intr_mode(phba, phba->intr_mode);
+
+       return PCI_ERS_RESULT_RECOVERED;
+}
+
+/**
+ * lpfc_io_resume_s3 - Method for resuming PCI I/O operation on SLI-3 device.
+ * @pdev: pointer to PCI device
+ *
+ * This routine is called from the PCI subsystem for error handling to device
+ * with SLI-3 interface spec. It is called when kernel error recovery tells
+ * the lpfc driver that it is ok to resume normal PCI operation after PCI bus
+ * error recovery. After this call, traffic can start to flow from this device
+ * again.
+ */
+static void
+lpfc_io_resume_s3(struct pci_dev *pdev)
+{
+       struct Scsi_Host *shost = pci_get_drvdata(pdev);
+       struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+
+       lpfc_online(phba);
+}
+
+/**
+ * lpfc_sli4_get_els_iocb_cnt - Calculate the # of ELS IOCBs to reserve
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * returns the number of ELS/CT IOCBs to reserve
+ **/
+int
+lpfc_sli4_get_els_iocb_cnt(struct lpfc_hba *phba)
+{
+       int max_xri = phba->sli4_hba.max_cfg_param.max_xri;
+
+       if (max_xri <= 100)
+               return 4;
+       else if (max_xri <= 256)
+               return 8;
+       else if (max_xri <= 512)
+               return 16;
+       else if (max_xri <= 1024)
+               return 32;
+       else
+               return 48;
+}
+
+/**
+ * lpfc_pci_probe_one_s4 - PCI probe func to reg SLI-4 device to PCI subsys
+ * @pdev: pointer to PCI device
+ * @pid: pointer to PCI device identifier
+ *
+ * This routine is called from the kernel's PCI subsystem to device with
+ * SLI-4 interface spec. When an Emulex HBA with SLI-4 interface spec is
+ * presented on PCI bus, the kernel PCI subsystem looks at PCI device-specific
+ * information of the device and driver to see if the driver state that it
+ * can support this kind of device. If the match is successful, the driver
+ * core invokes this routine. If this routine determines it can claim the HBA,
+ * it does all the initialization that it needs to do to handle the HBA
+ * properly.
+ *
+ * Return code
+ *     0 - driver can claim the device
+ *     negative value - driver can not claim the device
+ **/
+static int __devinit
+lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
+{
+       struct lpfc_hba   *phba;
+       struct lpfc_vport *vport = NULL;
+       int error;
+       uint32_t cfg_mode, intr_mode;
+       int mcnt;
+
+       /* Allocate memory for HBA structure */
+       phba = lpfc_hba_alloc(pdev);
+       if (!phba)
+               return -ENOMEM;
+
+       /* Perform generic PCI device enabling operation */
+       error = lpfc_enable_pci_dev(phba);
+       if (error) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1409 Failed to enable pci device.\n");
+               goto out_free_phba;
+       }
+
+       /* Set up SLI API function jump table for PCI-device group-1 HBAs */
+       error = lpfc_api_table_setup(phba, LPFC_PCI_DEV_OC);
+       if (error)
+               goto out_disable_pci_dev;
+
+       /* Set up SLI-4 specific device PCI memory space */
+       error = lpfc_sli4_pci_mem_setup(phba);
+       if (error) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1410 Failed to set up pci memory space.\n");
+               goto out_disable_pci_dev;
+       }
+
+       /* Set up phase-1 common device driver resources */
+       error = lpfc_setup_driver_resource_phase1(phba);
+       if (error) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1411 Failed to set up driver resource.\n");
+               goto out_unset_pci_mem_s4;
+       }
+
+       /* Set up SLI-4 Specific device driver resources */
+       error = lpfc_sli4_driver_resource_setup(phba);
+       if (error) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1412 Failed to set up driver resource.\n");
+               goto out_unset_pci_mem_s4;
+       }
+
+       /* Initialize and populate the iocb list per host */
+       error = lpfc_init_iocb_list(phba,
+                       phba->sli4_hba.max_cfg_param.max_xri);
+       if (error) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1413 Failed to initialize iocb list.\n");
+               goto out_unset_driver_resource_s4;
+       }
+
+       /* Set up common device driver resources */
+       error = lpfc_setup_driver_resource_phase2(phba);
+       if (error) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1414 Failed to set up driver resource.\n");
+               goto out_free_iocb_list;
+       }
+
+       /* Create SCSI host to the physical port */
+       error = lpfc_create_shost(phba);
+       if (error) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1415 Failed to create scsi host.\n");
+               goto out_unset_driver_resource;
+       }
+
+       /* Configure sysfs attributes */
+       vport = phba->pport;
+       error = lpfc_alloc_sysfs_attr(vport);
+       if (error) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1416 Failed to allocate sysfs attr\n");
+               goto out_destroy_shost;
+       }
+
+       /* Now, trying to enable interrupt and bring up the device */
+       cfg_mode = phba->cfg_use_msi;
+       while (true) {
+               /* Put device to a known state before enabling interrupt */
+               lpfc_stop_port(phba);
+               /* Configure and enable interrupt */
+               intr_mode = lpfc_sli4_enable_intr(phba, cfg_mode);
+               if (intr_mode == LPFC_INTR_ERROR) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                       "0426 Failed to enable interrupt.\n");
+                       error = -ENODEV;
+                       goto out_free_sysfs_attr;
+               }
+               /* Set up SLI-4 HBA */
+               if (lpfc_sli4_hba_setup(phba)) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                       "1421 Failed to set up hba\n");
+                       error = -ENODEV;
+                       goto out_disable_intr;
+               }
+
+               /* Send NOP mbx cmds for non-INTx mode active interrupt test */
+               if (intr_mode != 0)
+                       mcnt = lpfc_sli4_send_nop_mbox_cmds(phba,
+                                                           LPFC_ACT_INTR_CNT);
+
+               /* Check active interrupts received only for MSI/MSI-X */
+               if (intr_mode == 0 ||
+                   phba->sli.slistat.sli_intr >= LPFC_ACT_INTR_CNT) {
+                       /* Log the current active interrupt mode */
+                       phba->intr_mode = intr_mode;
+                       lpfc_log_intr_mode(phba, intr_mode);
+                       break;
+               }
+               lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                               "0451 Configure interrupt mode (%d) "
+                               "failed active interrupt test.\n",
+                               intr_mode);
+               /* Unset the preivous SLI-4 HBA setup */
+               lpfc_sli4_unset_hba(phba);
+               /* Try next level of interrupt mode */
+               cfg_mode = --intr_mode;
+       }
+
+       /* Perform post initialization setup */
+       lpfc_post_init_setup(phba);
+
+       return 0;
+
+out_disable_intr:
+       lpfc_sli4_disable_intr(phba);
+out_free_sysfs_attr:
+       lpfc_free_sysfs_attr(vport);
+out_destroy_shost:
+       lpfc_destroy_shost(phba);
+out_unset_driver_resource:
+       lpfc_unset_driver_resource_phase2(phba);
+out_free_iocb_list:
+       lpfc_free_iocb_list(phba);
+out_unset_driver_resource_s4:
+       lpfc_sli4_driver_resource_unset(phba);
+out_unset_pci_mem_s4:
+       lpfc_sli4_pci_mem_unset(phba);
+out_disable_pci_dev:
+       lpfc_disable_pci_dev(phba);
+out_free_phba:
+       lpfc_hba_free(phba);
        return error;
 }
 
 /**
- * lpfc_pci_remove_one - lpfc PCI func to unregister device from PCI subsystem
+ * lpfc_pci_remove_one_s4 - PCI func to unreg SLI-4 device from PCI subsystem
  * @pdev: pointer to PCI device
  *
- * This routine is to be registered to the kernel's PCI subsystem. When an
- * Emulex HBA is removed from PCI bus, it performs all the necessary cleanup
- * for the HBA device to be removed from the PCI subsystem properly.
+ * This routine is called from the kernel's PCI subsystem to device with
+ * SLI-4 interface spec. When an Emulex HBA with SLI-4 interface spec is
+ * removed from PCI bus, it performs all the necessary cleanup for the HBA
+ * device to be removed from the PCI subsystem properly.
  **/
 static void __devexit
-lpfc_pci_remove_one(struct pci_dev *pdev)
+lpfc_pci_remove_one_s4(struct pci_dev *pdev)
 {
-       struct Scsi_Host  *shost = pci_get_drvdata(pdev);
+       struct Scsi_Host *shost = pci_get_drvdata(pdev);
        struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
        struct lpfc_vport **vports;
-       struct lpfc_hba   *phba = vport->phba;
+       struct lpfc_hba *phba = vport->phba;
        int i;
-       int bars = pci_select_bars(pdev, IORESOURCE_MEM);
 
+       /* Mark the device unloading flag */
        spin_lock_irq(&phba->hbalock);
        vport->load_flag |= FC_UNLOADING;
        spin_unlock_irq(&phba->hbalock);
 
+       /* Free the HBA sysfs attributes */
        lpfc_free_sysfs_attr(vport);
 
        /* Release all the vports against this physical port */
        vports = lpfc_create_vport_work_array(phba);
        if (vports != NULL)
-               for (i = 1; i <= phba->max_vpi && vports[i] != NULL; i++)
+               for (i = 1; i <= phba->max_vports && vports[i] != NULL; i++)
                        fc_vport_terminate(vports[i]->fc_vport);
        lpfc_destroy_vport_work_array(phba, vports);
 
        /* Remove FC host and then SCSI host with the physical port */
        fc_remove_host(shost);
        scsi_remove_host(shost);
+
+       /* Perform cleanup on the physical port */
        lpfc_cleanup(vport);
 
        /*
-        * Bring down the SLI Layer. This step disable all interrupts,
+        * Bring down the SLI Layer. This step disables all interrupts,
         * clears the rings, discards all mailbox commands, and resets
-        * the HBA.
+        * the HBA FCoE function.
         */
+       lpfc_debugfs_terminate(vport);
+       lpfc_sli4_hba_unset(phba);
 
-       /* HBA interrupt will be diabled after this call */
-       lpfc_sli_hba_down(phba);
-       /* Stop kthread signal shall trigger work_done one more time */
-       kthread_stop(phba->worker_thread);
-       /* Final cleanup of txcmplq and reset the HBA */
-       lpfc_sli_brdrestart(phba);
-
-       lpfc_stop_phba_timers(phba);
        spin_lock_irq(&phba->hbalock);
        list_del_init(&vport->listentry);
        spin_unlock_irq(&phba->hbalock);
 
-       lpfc_debugfs_terminate(vport);
-
-       /* Disable interrupt */
-       lpfc_disable_intr(phba);
-
-       pci_set_drvdata(pdev, NULL);
-       scsi_host_put(shost);
-
-       /*
-        * Call scsi_free before mem_free since scsi bufs are released to their
-        * corresponding pools here.
+       /* Call scsi_free before lpfc_sli4_driver_resource_unset since scsi
+        * buffers are released to their corresponding pools here.
         */
        lpfc_scsi_free(phba);
-       lpfc_mem_free(phba);
-
-       dma_free_coherent(&pdev->dev, lpfc_sli_hbq_size(),
-                         phba->hbqslimp.virt, phba->hbqslimp.phys);
-
-       /* Free resources associated with SLI2 interface */
-       dma_free_coherent(&pdev->dev, SLI2_SLIM_SIZE,
-                         phba->slim2p.virt, phba->slim2p.phys);
+       lpfc_sli4_driver_resource_unset(phba);
 
-       /* unmap adapter SLIM and Control Registers */
-       iounmap(phba->ctrl_regs_memmap_p);
-       iounmap(phba->slim_memmap_p);
+       /* Unmap adapter Control and Doorbell registers */
+       lpfc_sli4_pci_mem_unset(phba);
 
-       idr_remove(&lpfc_hba_index, phba->brd_no);
+       /* Release PCI resources and disable device's PCI function */
+       scsi_host_put(shost);
+       lpfc_disable_pci_dev(phba);
 
-       kfree(phba);
+       /* Finally, free the driver's device data structure */
+       lpfc_hba_free(phba);
 
-       pci_release_selected_regions(pdev, bars);
-       pci_disable_device(pdev);
+       return;
 }
 
 /**
- * lpfc_pci_suspend_one - lpfc PCI func to suspend device for power management
+ * lpfc_pci_suspend_one_s4 - PCI func to suspend SLI-4 device for power mgmnt
  * @pdev: pointer to PCI device
  * @msg: power management message
  *
- * This routine is to be registered to the kernel's PCI subsystem to support
- * system Power Management (PM). When PM invokes this method, it quiesces the
- * device by stopping the driver's worker thread for the device, turning off
- * device's interrupt and DMA, and bring the device offline. Note that as the
- * driver implements the minimum PM requirements to a power-aware driver's PM
- * support for suspend/resume -- all the possible PM messages (SUSPEND,
- * HIBERNATE, FREEZE) to the suspend() method call will be treated as SUSPEND
- * and the driver will fully reinitialize its device during resume() method
- * call, the driver will set device to PCI_D3hot state in PCI config space
- * instead of setting it according to the @msg provided by the PM.
+ * This routine is called from the kernel's PCI subsystem to support system
+ * Power Management (PM) to device with SLI-4 interface spec. When PM invokes
+ * this method, it quiesces the device by stopping the driver's worker
+ * thread for the device, turning off device's interrupt and DMA, and bring
+ * the device offline. Note that as the driver implements the minimum PM
+ * requirements to a power-aware driver's PM support for suspend/resume -- all
+ * the possible PM messages (SUSPEND, HIBERNATE, FREEZE) to the suspend()
+ * method call will be treated as SUSPEND and the driver will fully
+ * reinitialize its device during resume() method call, the driver will set
+ * device to PCI_D3hot state in PCI config space instead of setting it
+ * according to the @msg provided by the PM.
  *
  * Return code
- *   0 - driver suspended the device
- *   Error otherwise
+ *     0 - driver suspended the device
+ *     Error otherwise
  **/
 static int
-lpfc_pci_suspend_one(struct pci_dev *pdev, pm_message_t msg)
+lpfc_pci_suspend_one_s4(struct pci_dev *pdev, pm_message_t msg)
 {
        struct Scsi_Host *shost = pci_get_drvdata(pdev);
        struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
 
        lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
-                       "0473 PCI device Power Management suspend.\n");
+                       "0298 PCI device Power Management suspend.\n");
 
        /* Bring down the device */
        lpfc_offline_prep(phba);
@@ -3194,7 +7476,7 @@ lpfc_pci_suspend_one(struct pci_dev *pdev, pm_message_t msg)
        kthread_stop(phba->worker_thread);
 
        /* Disable interrupt from device */
-       lpfc_disable_intr(phba);
+       lpfc_sli4_disable_intr(phba);
 
        /* Save device state to PCI config space */
        pci_save_state(pdev);
@@ -3204,25 +7486,26 @@ lpfc_pci_suspend_one(struct pci_dev *pdev, pm_message_t msg)
 }
 
 /**
- * lpfc_pci_resume_one - lpfc PCI func to resume device for power management
+ * lpfc_pci_resume_one_s4 - PCI func to resume SLI-4 device for power mgmnt
  * @pdev: pointer to PCI device
  *
- * This routine is to be registered to the kernel's PCI subsystem to support
- * system Power Management (PM). When PM invokes this method, it restores
- * the device's PCI config space state and fully reinitializes the device
- * and brings it online. Note that as the driver implements the minimum PM
- * requirements to a power-aware driver's PM for suspend/resume -- all
- * the possible PM messages (SUSPEND, HIBERNATE, FREEZE) to the suspend()
- * method call will be treated as SUSPEND and the driver will fully
- * reinitialize its device during resume() method call, the device will be
- * set to PCI_D0 directly in PCI config space before restoring the state.
+ * This routine is called from the kernel's PCI subsystem to support system
+ * Power Management (PM) to device with SLI-4 interface spac. When PM invokes
+ * this method, it restores the device's PCI config space state and fully
+ * reinitializes the device and brings it online. Note that as the driver
+ * implements the minimum PM requirements to a power-aware driver's PM for
+ * suspend/resume -- all the possible PM messages (SUSPEND, HIBERNATE, FREEZE)
+ * to the suspend() method call will be treated as SUSPEND and the driver
+ * will fully reinitialize its device during resume() method call, the device
+ * will be set to PCI_D0 directly in PCI config space before restoring the
+ * state.
  *
  * Return code
- *   0 - driver suspended the device
- *   Error otherwise
+ *     0 - driver suspended the device
+ *     Error otherwise
  **/
 static int
-lpfc_pci_resume_one(struct pci_dev *pdev)
+lpfc_pci_resume_one_s4(struct pci_dev *pdev)
 {
        struct Scsi_Host *shost = pci_get_drvdata(pdev);
        struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
@@ -3230,7 +7513,7 @@ lpfc_pci_resume_one(struct pci_dev *pdev)
        int error;
 
        lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
-                       "0452 PCI device Power Management resume.\n");
+                       "0292 PCI device Power Management resume.\n");
 
        /* Restore device state from PCI config space */
        pci_set_power_state(pdev, PCI_D0);
@@ -3238,22 +7521,22 @@ lpfc_pci_resume_one(struct pci_dev *pdev)
        if (pdev->is_busmaster)
                pci_set_master(pdev);
 
-       /* Startup the kernel thread for this host adapter. */
+        /* Startup the kernel thread for this host adapter. */
        phba->worker_thread = kthread_run(lpfc_do_work, phba,
                                        "lpfc_worker_%d", phba->brd_no);
        if (IS_ERR(phba->worker_thread)) {
                error = PTR_ERR(phba->worker_thread);
                lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
-                               "0434 PM resume failed to start worker "
+                               "0293 PM resume failed to start worker "
                                "thread: error=x%x.\n", error);
                return error;
        }
 
        /* Configure and enable interrupt */
-       intr_mode = lpfc_enable_intr(phba, phba->intr_mode);
+       intr_mode = lpfc_sli4_enable_intr(phba, phba->intr_mode);
        if (intr_mode == LPFC_INTR_ERROR) {
                lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
-                               "0430 PM resume Failed to enable interrupt\n");
+                               "0294 PM resume Failed to enable interrupt\n");
                return -EIO;
        } else
                phba->intr_mode = intr_mode;
@@ -3269,129 +7552,316 @@ lpfc_pci_resume_one(struct pci_dev *pdev)
 }
 
 /**
- * lpfc_io_error_detected - Driver method for handling PCI I/O error detected
+ * lpfc_io_error_detected_s4 - Method for handling PCI I/O error to SLI-4 device
  * @pdev: pointer to PCI device.
  * @state: the current PCI connection state.
  *
- * This routine is registered to the PCI subsystem for error handling. This
- * function is called by the PCI subsystem after a PCI bus error affecting
- * this device has been detected. When this function is invoked, it will
- * need to stop all the I/Os and interrupt(s) to the device. Once that is
- * done, it will return PCI_ERS_RESULT_NEED_RESET for the PCI subsystem to
- * perform proper recovery as desired.
+ * This routine is called from the PCI subsystem for error handling to device
+ * with SLI-4 interface spec. This function is called by the PCI subsystem
+ * after a PCI bus error affecting this device has been detected. When this
+ * function is invoked, it will need to stop all the I/Os and interrupt(s)
+ * to the device. Once that is done, it will return PCI_ERS_RESULT_NEED_RESET
+ * for the PCI subsystem to perform proper recovery as desired.
  *
  * Return codes
- *   PCI_ERS_RESULT_NEED_RESET - need to reset before recovery
- *   PCI_ERS_RESULT_DISCONNECT - device could not be recovered
+ *     PCI_ERS_RESULT_NEED_RESET - need to reset before recovery
+ *     PCI_ERS_RESULT_DISCONNECT - device could not be recovered
  **/
-static pci_ers_result_t lpfc_io_error_detected(struct pci_dev *pdev,
-                               pci_channel_state_t state)
+static pci_ers_result_t
+lpfc_io_error_detected_s4(struct pci_dev *pdev, pci_channel_state_t state)
+{
+       return PCI_ERS_RESULT_NEED_RESET;
+}
+
+/**
+ * lpfc_io_slot_reset_s4 - Method for restart PCI SLI-4 device from scratch
+ * @pdev: pointer to PCI device.
+ *
+ * This routine is called from the PCI subsystem for error handling to device
+ * with SLI-4 interface spec. It is called after PCI bus has been reset to
+ * restart the PCI card from scratch, as if from a cold-boot. During the
+ * PCI subsystem error recovery, after the driver returns
+ * PCI_ERS_RESULT_NEED_RESET, the PCI subsystem will perform proper error
+ * recovery and then call this routine before calling the .resume method to
+ * recover the device. This function will initialize the HBA device, enable
+ * the interrupt, but it will just put the HBA to offline state without
+ * passing any I/O traffic.
+ *
+ * Return codes
+ *     PCI_ERS_RESULT_RECOVERED - the device has been recovered
+ *     PCI_ERS_RESULT_DISCONNECT - device could not be recovered
+ */
+static pci_ers_result_t
+lpfc_io_slot_reset_s4(struct pci_dev *pdev)
+{
+       return PCI_ERS_RESULT_RECOVERED;
+}
+
+/**
+ * lpfc_io_resume_s4 - Method for resuming PCI I/O operation to SLI-4 device
+ * @pdev: pointer to PCI device
+ *
+ * This routine is called from the PCI subsystem for error handling to device
+ * with SLI-4 interface spec. It is called when kernel error recovery tells
+ * the lpfc driver that it is ok to resume normal PCI operation after PCI bus
+ * error recovery. After this call, traffic can start to flow from this device
+ * again.
+ **/
+static void
+lpfc_io_resume_s4(struct pci_dev *pdev)
+{
+       return;
+}
+
+/**
+ * lpfc_pci_probe_one - lpfc PCI probe func to reg dev to PCI subsystem
+ * @pdev: pointer to PCI device
+ * @pid: pointer to PCI device identifier
+ *
+ * This routine is to be registered to the kernel's PCI subsystem. When an
+ * Emulex HBA device is presented on PCI bus, the kernel PCI subsystem looks
+ * at PCI device-specific information of the device and driver to see if the
+ * driver state that it can support this kind of device. If the match is
+ * successful, the driver core invokes this routine. This routine dispatches
+ * the action to the proper SLI-3 or SLI-4 device probing routine, which will
+ * do all the initialization that it needs to do to handle the HBA device
+ * properly.
+ *
+ * Return code
+ *     0 - driver can claim the device
+ *     negative value - driver can not claim the device
+ **/
+static int __devinit
+lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
+{
+       int rc;
+       uint16_t dev_id;
+
+       if (pci_read_config_word(pdev, PCI_DEVICE_ID, &dev_id))
+               return -ENODEV;
+
+       switch (dev_id) {
+       case PCI_DEVICE_ID_TIGERSHARK:
+       case PCI_DEVICE_ID_TIGERSHARK_S:
+               rc = lpfc_pci_probe_one_s4(pdev, pid);
+               break;
+       default:
+               rc = lpfc_pci_probe_one_s3(pdev, pid);
+               break;
+       }
+       return rc;
+}
+
+/**
+ * lpfc_pci_remove_one - lpfc PCI func to unreg dev from PCI subsystem
+ * @pdev: pointer to PCI device
+ *
+ * This routine is to be registered to the kernel's PCI subsystem. When an
+ * Emulex HBA is removed from PCI bus, the driver core invokes this routine.
+ * This routine dispatches the action to the proper SLI-3 or SLI-4 device
+ * remove routine, which will perform all the necessary cleanup for the
+ * device to be removed from the PCI subsystem properly.
+ **/
+static void __devexit
+lpfc_pci_remove_one(struct pci_dev *pdev)
 {
        struct Scsi_Host *shost = pci_get_drvdata(pdev);
        struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
-       struct lpfc_sli *psli = &phba->sli;
-       struct lpfc_sli_ring  *pring;
 
-       if (state == pci_channel_io_perm_failure) {
+       switch (phba->pci_dev_grp) {
+       case LPFC_PCI_DEV_LP:
+               lpfc_pci_remove_one_s3(pdev);
+               break;
+       case LPFC_PCI_DEV_OC:
+               lpfc_pci_remove_one_s4(pdev);
+               break;
+       default:
                lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
-                               "0472 PCI channel I/O permanent failure\n");
-               /* Block all SCSI devices' I/Os on the host */
-               lpfc_scsi_dev_block(phba);
-               /* Clean up all driver's outstanding SCSI I/Os */
-               lpfc_sli_flush_fcp_rings(phba);
-               return PCI_ERS_RESULT_DISCONNECT;
+                               "1424 Invalid PCI device group: 0x%x\n",
+                               phba->pci_dev_grp);
+               break;
        }
+       return;
+}
 
-       pci_disable_device(pdev);
-       /*
-        * There may be I/Os dropped by the firmware.
-        * Error iocb (I/O) on txcmplq and let the SCSI layer
-        * retry it after re-establishing link.
-        */
-       pring = &psli->ring[psli->fcp_ring];
-       lpfc_sli_abort_iocb_ring(phba, pring);
+/**
+ * lpfc_pci_suspend_one - lpfc PCI func to suspend dev for power management
+ * @pdev: pointer to PCI device
+ * @msg: power management message
+ *
+ * This routine is to be registered to the kernel's PCI subsystem to support
+ * system Power Management (PM). When PM invokes this method, it dispatches
+ * the action to the proper SLI-3 or SLI-4 device suspend routine, which will
+ * suspend the device.
+ *
+ * Return code
+ *     0 - driver suspended the device
+ *     Error otherwise
+ **/
+static int
+lpfc_pci_suspend_one(struct pci_dev *pdev, pm_message_t msg)
+{
+       struct Scsi_Host *shost = pci_get_drvdata(pdev);
+       struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+       int rc = -ENODEV;
 
-       /* Disable interrupt */
-       lpfc_disable_intr(phba);
+       switch (phba->pci_dev_grp) {
+       case LPFC_PCI_DEV_LP:
+               rc = lpfc_pci_suspend_one_s3(pdev, msg);
+               break;
+       case LPFC_PCI_DEV_OC:
+               rc = lpfc_pci_suspend_one_s4(pdev, msg);
+               break;
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1425 Invalid PCI device group: 0x%x\n",
+                               phba->pci_dev_grp);
+               break;
+       }
+       return rc;
+}
 
-       /* Request a slot reset. */
-       return PCI_ERS_RESULT_NEED_RESET;
+/**
+ * lpfc_pci_resume_one - lpfc PCI func to resume dev for power management
+ * @pdev: pointer to PCI device
+ *
+ * This routine is to be registered to the kernel's PCI subsystem to support
+ * system Power Management (PM). When PM invokes this method, it dispatches
+ * the action to the proper SLI-3 or SLI-4 device resume routine, which will
+ * resume the device.
+ *
+ * Return code
+ *     0 - driver suspended the device
+ *     Error otherwise
+ **/
+static int
+lpfc_pci_resume_one(struct pci_dev *pdev)
+{
+       struct Scsi_Host *shost = pci_get_drvdata(pdev);
+       struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+       int rc = -ENODEV;
+
+       switch (phba->pci_dev_grp) {
+       case LPFC_PCI_DEV_LP:
+               rc = lpfc_pci_resume_one_s3(pdev);
+               break;
+       case LPFC_PCI_DEV_OC:
+               rc = lpfc_pci_resume_one_s4(pdev);
+               break;
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1426 Invalid PCI device group: 0x%x\n",
+                               phba->pci_dev_grp);
+               break;
+       }
+       return rc;
 }
 
 /**
- * lpfc_io_slot_reset - Restart a PCI device from scratch
+ * lpfc_io_error_detected - lpfc method for handling PCI I/O error
  * @pdev: pointer to PCI device.
+ * @state: the current PCI connection state.
  *
- * This routine is registered to the PCI subsystem for error handling. This is
- * called after PCI bus has been reset to restart the PCI card from scratch,
- * as if from a cold-boot. During the PCI subsystem error recovery, after the
- * driver returns PCI_ERS_RESULT_NEED_RESET, the PCI subsystem will perform
- * proper error recovery and then call this routine before calling the .resume
- * method to recover the device. This function will initialize the HBA device,
- * enable the interrupt, but it will just put the HBA to offline state without
- * passing any I/O traffic.
+ * This routine is registered to the PCI subsystem for error handling. This
+ * function is called by the PCI subsystem after a PCI bus error affecting
+ * this device has been detected. When this routine is invoked, it dispatches
+ * the action to the proper SLI-3 or SLI-4 device error detected handling
+ * routine, which will perform the proper error detected operation.
  *
  * Return codes
- *   PCI_ERS_RESULT_RECOVERED - the device has been recovered
- *   PCI_ERS_RESULT_DISCONNECT - device could not be recovered
- */
-static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev)
+ *     PCI_ERS_RESULT_NEED_RESET - need to reset before recovery
+ *     PCI_ERS_RESULT_DISCONNECT - device could not be recovered
+ **/
+static pci_ers_result_t
+lpfc_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
 {
        struct Scsi_Host *shost = pci_get_drvdata(pdev);
        struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
-       struct lpfc_sli *psli = &phba->sli;
-       uint32_t intr_mode;
+       pci_ers_result_t rc = PCI_ERS_RESULT_DISCONNECT;
 
-       dev_printk(KERN_INFO, &pdev->dev, "recovering from a slot reset.\n");
-       if (pci_enable_device_mem(pdev)) {
-               printk(KERN_ERR "lpfc: Cannot re-enable "
-                       "PCI device after reset.\n");
-               return PCI_ERS_RESULT_DISCONNECT;
+       switch (phba->pci_dev_grp) {
+       case LPFC_PCI_DEV_LP:
+               rc = lpfc_io_error_detected_s3(pdev, state);
+               break;
+       case LPFC_PCI_DEV_OC:
+               rc = lpfc_io_error_detected_s4(pdev, state);
+               break;
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1427 Invalid PCI device group: 0x%x\n",
+                               phba->pci_dev_grp);
+               break;
        }
+       return rc;
+}
 
-       pci_restore_state(pdev);
-       if (pdev->is_busmaster)
-               pci_set_master(pdev);
-
-       spin_lock_irq(&phba->hbalock);
-       psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
-       spin_unlock_irq(&phba->hbalock);
+/**
+ * lpfc_io_slot_reset - lpfc method for restart PCI dev from scratch
+ * @pdev: pointer to PCI device.
+ *
+ * This routine is registered to the PCI subsystem for error handling. This
+ * function is called after PCI bus has been reset to restart the PCI card
+ * from scratch, as if from a cold-boot. When this routine is invoked, it
+ * dispatches the action to the proper SLI-3 or SLI-4 device reset handling
+ * routine, which will perform the proper device reset.
+ *
+ * Return codes
+ *     PCI_ERS_RESULT_RECOVERED - the device has been recovered
+ *     PCI_ERS_RESULT_DISCONNECT - device could not be recovered
+ **/
+static pci_ers_result_t
+lpfc_io_slot_reset(struct pci_dev *pdev)
+{
+       struct Scsi_Host *shost = pci_get_drvdata(pdev);
+       struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+       pci_ers_result_t rc = PCI_ERS_RESULT_DISCONNECT;
 
-       /* Configure and enable interrupt */
-       intr_mode = lpfc_enable_intr(phba, phba->intr_mode);
-       if (intr_mode == LPFC_INTR_ERROR) {
+       switch (phba->pci_dev_grp) {
+       case LPFC_PCI_DEV_LP:
+               rc = lpfc_io_slot_reset_s3(pdev);
+               break;
+       case LPFC_PCI_DEV_OC:
+               rc = lpfc_io_slot_reset_s4(pdev);
+               break;
+       default:
                lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
-                               "0427 Cannot re-enable interrupt after "
-                               "slot reset.\n");
-               return PCI_ERS_RESULT_DISCONNECT;
-       } else
-               phba->intr_mode = intr_mode;
-
-       /* Take device offline; this will perform cleanup */
-       lpfc_offline(phba);
-       lpfc_sli_brdrestart(phba);
-
-       /* Log the current active interrupt mode */
-       lpfc_log_intr_mode(phba, phba->intr_mode);
-
-       return PCI_ERS_RESULT_RECOVERED;
+                               "1428 Invalid PCI device group: 0x%x\n",
+                               phba->pci_dev_grp);
+               break;
+       }
+       return rc;
 }
 
 /**
- * lpfc_io_resume - Resume PCI I/O operation
+ * lpfc_io_resume - lpfc method for resuming PCI I/O operation
  * @pdev: pointer to PCI device
  *
- * This routine is registered to the PCI subsystem for error handling. It is
- * called when kernel error recovery tells the lpfc driver that it is ok to
- * resume normal PCI operation after PCI bus error recovery. After this call,
- * traffic can start to flow from this device again.
- */
-static void lpfc_io_resume(struct pci_dev *pdev)
+ * This routine is registered to the PCI subsystem for error handling. It
+ * is called when kernel error recovery tells the lpfc driver that it is
+ * OK to resume normal PCI operation after PCI bus error recovery. When
+ * this routine is invoked, it dispatches the action to the proper SLI-3
+ * or SLI-4 device io_resume routine, which will resume the device operation.
+ **/
+static void
+lpfc_io_resume(struct pci_dev *pdev)
 {
        struct Scsi_Host *shost = pci_get_drvdata(pdev);
        struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
 
-       lpfc_online(phba);
+       switch (phba->pci_dev_grp) {
+       case LPFC_PCI_DEV_LP:
+               lpfc_io_resume_s3(pdev);
+               break;
+       case LPFC_PCI_DEV_OC:
+               lpfc_io_resume_s4(pdev);
+               break;
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1429 Invalid PCI device group: 0x%x\n",
+                               phba->pci_dev_grp);
+               break;
+       }
+       return;
 }
 
 static struct pci_device_id lpfc_id_table[] = {
@@ -3469,6 +7939,10 @@ static struct pci_device_id lpfc_id_table[] = {
                PCI_ANY_ID, PCI_ANY_ID, },
        {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PROTEUS_S,
                PCI_ANY_ID, PCI_ANY_ID, },
+       {PCI_VENDOR_ID_SERVERENGINE, PCI_DEVICE_ID_TIGERSHARK,
+               PCI_ANY_ID, PCI_ANY_ID, },
+       {PCI_VENDOR_ID_SERVERENGINE, PCI_DEVICE_ID_TIGERSHARK_S,
+               PCI_ANY_ID, PCI_ANY_ID, },
        { 0 }
 };
 
@@ -3486,7 +7960,7 @@ static struct pci_driver lpfc_driver = {
        .probe          = lpfc_pci_probe_one,
        .remove         = __devexit_p(lpfc_pci_remove_one),
        .suspend        = lpfc_pci_suspend_one,
-       .resume         = lpfc_pci_resume_one,
+       .resume         = lpfc_pci_resume_one,
        .err_handler    = &lpfc_err_handler,
 };
 
index 1aa85709b012d24826652d87e0c5da2121f1dda2..954ba57970a3ab294ef8db13d99ff798b6ca346c 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2008 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2009 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  *                                                                 *
  * included with this package.                                     *
  *******************************************************************/
 
-#define LOG_ELS                       0x1      /* ELS events */
-#define LOG_DISCOVERY                 0x2      /* Link discovery events */
-#define LOG_MBOX                      0x4      /* Mailbox events */
-#define LOG_INIT                      0x8      /* Initialization events */
-#define LOG_LINK_EVENT                0x10     /* Link events */
-#define LOG_IP                        0x20     /* IP traffic history */
-#define LOG_FCP                       0x40     /* FCP traffic history */
-#define LOG_NODE                      0x80     /* Node table events */
-#define LOG_TEMP                      0x100    /* Temperature sensor events */
-#define LOG_BG                       0x200     /* BlockGuard events */
-#define LOG_MISC                      0x400    /* Miscellaneous events */
-#define LOG_SLI                       0x800    /* SLI events */
-#define LOG_FCP_ERROR                 0x1000   /* log errors, not underruns */
-#define LOG_LIBDFC                    0x2000   /* Libdfc events */
-#define LOG_VPORT                     0x4000   /* NPIV events */
-#define LOG_ALL_MSG                   0xffff   /* LOG all messages */
+#define LOG_ELS                0x00000001      /* ELS events */
+#define LOG_DISCOVERY  0x00000002      /* Link discovery events */
+#define LOG_MBOX       0x00000004      /* Mailbox events */
+#define LOG_INIT       0x00000008      /* Initialization events */
+#define LOG_LINK_EVENT 0x00000010      /* Link events */
+#define LOG_IP         0x00000020      /* IP traffic history */
+#define LOG_FCP                0x00000040      /* FCP traffic history */
+#define LOG_NODE       0x00000080      /* Node table events */
+#define LOG_TEMP       0x00000100      /* Temperature sensor events */
+#define LOG_BG         0x00000200      /* BlockGuard events */
+#define LOG_MISC       0x00000400      /* Miscellaneous events */
+#define LOG_SLI                0x00000800      /* SLI events */
+#define LOG_FCP_ERROR  0x00001000      /* log errors, not underruns */
+#define LOG_LIBDFC     0x00002000      /* Libdfc events */
+#define LOG_VPORT      0x00004000      /* NPIV events */
+#define LOF_SECURITY   0x00008000      /* Security events */
+#define LOG_EVENT      0x00010000      /* CT,TEMP,DUMP, logging */
+#define LOG_ALL_MSG    0xffffffff      /* LOG all messages */
 
 #define lpfc_printf_vlog(vport, level, mask, fmt, arg...) \
-       do { \
-       { if (((mask) &(vport)->cfg_log_verbose) || (level[1] <= '3')) \
+do { \
+       { if (((mask) & (vport)->cfg_log_verbose) || (level[1] <= '3')) \
                dev_printk(level, &((vport)->phba->pcidev)->dev, "%d:(%d):" \
                           fmt, (vport)->phba->brd_no, vport->vpi, ##arg); } \
-       } while (0)
+} while (0)
 
 #define lpfc_printf_log(phba, level, mask, fmt, arg...) \
-       do { \
-       { if (((mask) &(phba)->pport->cfg_log_verbose) || (level[1] <= '3')) \
+do { \
+       { uint32_t log_verbose = (phba)->pport ? \
+                                (phba)->pport->cfg_log_verbose : \
+                                (phba)->cfg_log_verbose; \
+         if (((mask) & log_verbose) || (level[1] <= '3')) \
                dev_printk(level, &((phba)->pcidev)->dev, "%d:" \
-                          fmt, phba->brd_no, ##arg); } \
-       } while (0)
+                          fmt, phba->brd_no, ##arg); \
+       } \
+} while (0)
index 134fc7fc2127454ccf98083cdd375d2f39400158..b9b451c090101565d507f34c931009a0c25f15e8 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2008 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2009 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
 
 #include <scsi/scsi.h>
 
+#include "lpfc_hw4.h"
 #include "lpfc_hw.h"
 #include "lpfc_sli.h"
+#include "lpfc_sli4.h"
 #include "lpfc_nl.h"
 #include "lpfc_disc.h"
 #include "lpfc_scsi.h"
 #include "lpfc_crtn.h"
 #include "lpfc_compat.h"
 
+/**
+ * lpfc_dump_static_vport - Dump HBA's static vport information.
+ * @phba: pointer to lpfc hba data structure.
+ * @pmb: pointer to the driver internal queue element for mailbox command.
+ * @offset: offset for dumping vport info.
+ *
+ * The dump mailbox command provides a method for the device driver to obtain
+ * various types of information from the HBA device.
+ *
+ * This routine prepares the mailbox command for dumping list of static
+ * vports to be created.
+ **/
+void
+lpfc_dump_static_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb,
+               uint16_t offset)
+{
+       MAILBOX_t *mb;
+       void *ctx;
+
+       mb = &pmb->u.mb;
+       ctx = pmb->context2;
+
+       /* Setup to dump vport info region */
+       memset(pmb, 0, sizeof(LPFC_MBOXQ_t));
+       mb->mbxCommand = MBX_DUMP_MEMORY;
+       mb->un.varDmp.cv = 1;
+       mb->un.varDmp.type = DMP_NV_PARAMS;
+       mb->un.varDmp.entry_index = offset;
+       mb->un.varDmp.region_id = DMP_REGION_VPORT;
+       mb->un.varDmp.word_cnt = DMP_RSP_SIZE/sizeof(uint32_t);
+       mb->un.varDmp.co = 0;
+       mb->un.varDmp.resp_offset = 0;
+       pmb->context2 = ctx;
+       mb->mbxOwner = OWN_HOST;
+
+       return;
+}
+
 /**
  * lpfc_dump_mem - Prepare a mailbox command for retrieving HBA's VPD memory
  * @phba: pointer to lpfc hba data structure.
@@ -58,7 +98,7 @@ lpfc_dump_mem(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, uint16_t offset)
        MAILBOX_t *mb;
        void *ctx;
 
-       mb = &pmb->mb;
+       mb = &pmb->u.mb;
        ctx = pmb->context2;
 
        /* Setup to dump VPD region */
@@ -90,7 +130,7 @@ lpfc_dump_wakeup_param(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
        MAILBOX_t *mb;
        void *ctx;
 
-       mb = &pmb->mb;
+       mb = &pmb->u.mb;
        /* Save context so that we can restore after memset */
        ctx = pmb->context2;
 
@@ -125,7 +165,7 @@ lpfc_read_nv(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
 {
        MAILBOX_t *mb;
 
-       mb = &pmb->mb;
+       mb = &pmb->u.mb;
        memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
        mb->mbxCommand = MBX_READ_NV;
        mb->mbxOwner = OWN_HOST;
@@ -151,7 +191,7 @@ lpfc_config_async(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb,
 {
        MAILBOX_t *mb;
 
-       mb = &pmb->mb;
+       mb = &pmb->u.mb;
        memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
        mb->mbxCommand = MBX_ASYNCEVT_ENABLE;
        mb->un.varCfgAsyncEvent.ring = ring;
@@ -177,7 +217,7 @@ lpfc_heart_beat(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
 {
        MAILBOX_t *mb;
 
-       mb = &pmb->mb;
+       mb = &pmb->u.mb;
        memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
        mb->mbxCommand = MBX_HEARTBEAT;
        mb->mbxOwner = OWN_HOST;
@@ -211,7 +251,7 @@ lpfc_read_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, struct lpfc_dmabuf *mp)
        struct lpfc_sli *psli;
 
        psli = &phba->sli;
-       mb = &pmb->mb;
+       mb = &pmb->u.mb;
        memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
 
        INIT_LIST_HEAD(&mp->list);
@@ -248,7 +288,7 @@ lpfc_clear_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
 {
        MAILBOX_t *mb;
 
-       mb = &pmb->mb;
+       mb = &pmb->u.mb;
        memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
 
        mb->un.varClearLA.eventTag = phba->fc_eventTag;
@@ -275,7 +315,7 @@ void
 lpfc_config_link(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
 {
        struct lpfc_vport  *vport = phba->pport;
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
        memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
 
        /* NEW_FEATURE
@@ -321,7 +361,7 @@ lpfc_config_link(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
 int
 lpfc_config_msi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
 {
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
        uint32_t attentionConditions[2];
 
        /* Sanity check */
@@ -405,7 +445,7 @@ lpfc_init_link(struct lpfc_hba * phba,
        struct lpfc_sli *psli;
        MAILBOX_t *mb;
 
-       mb = &pmb->mb;
+       mb = &pmb->u.mb;
        memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
 
        psli = &phba->sli;
@@ -492,7 +532,7 @@ lpfc_read_sparam(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb, int vpi)
        struct lpfc_sli *psli;
 
        psli = &phba->sli;
-       mb = &pmb->mb;
+       mb = &pmb->u.mb;
        memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
 
        mb->mbxOwner = OWN_HOST;
@@ -515,7 +555,7 @@ lpfc_read_sparam(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb, int vpi)
        mb->un.varRdSparm.un.sp64.tus.f.bdeSize = sizeof (struct serv_parm);
        mb->un.varRdSparm.un.sp64.addrHigh = putPaddrHigh(mp->phys);
        mb->un.varRdSparm.un.sp64.addrLow = putPaddrLow(mp->phys);
-       mb->un.varRdSparm.vpi = vpi;
+       mb->un.varRdSparm.vpi = vpi + phba->vpi_base;
 
        /* save address for completion */
        pmb->context1 = mp;
@@ -544,10 +584,12 @@ lpfc_unreg_did(struct lpfc_hba * phba, uint16_t vpi, uint32_t did,
 {
        MAILBOX_t *mb;
 
-       mb = &pmb->mb;
+       mb = &pmb->u.mb;
        memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
 
        mb->un.varUnregDID.did = did;
+       if (vpi != 0xffff)
+               vpi += phba->vpi_base;
        mb->un.varUnregDID.vpi = vpi;
 
        mb->mbxCommand = MBX_UNREG_D_ID;
@@ -573,7 +615,7 @@ lpfc_read_config(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
 {
        MAILBOX_t *mb;
 
-       mb = &pmb->mb;
+       mb = &pmb->u.mb;
        memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
 
        mb->mbxCommand = MBX_READ_CONFIG;
@@ -598,7 +640,7 @@ lpfc_read_lnk_stat(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
 {
        MAILBOX_t *mb;
 
-       mb = &pmb->mb;
+       mb = &pmb->u.mb;
        memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
 
        mb->mbxCommand = MBX_READ_LNK_STAT;
@@ -607,7 +649,7 @@ lpfc_read_lnk_stat(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
 }
 
 /**
- * lpfc_reg_login - Prepare a mailbox command for registering remote login
+ * lpfc_reg_rpi - Prepare a mailbox command for registering remote login
  * @phba: pointer to lpfc hba data structure.
  * @vpi: virtual N_Port identifier.
  * @did: remote port identifier.
@@ -631,17 +673,23 @@ lpfc_read_lnk_stat(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
  *    1 - DMA memory allocation failed
  **/
 int
-lpfc_reg_login(struct lpfc_hba *phba, uint16_t vpi, uint32_t did,
+lpfc_reg_rpi(struct lpfc_hba *phba, uint16_t vpi, uint32_t did,
               uint8_t *param, LPFC_MBOXQ_t *pmb, uint32_t flag)
 {
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
        uint8_t *sparam;
        struct lpfc_dmabuf *mp;
 
        memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
 
        mb->un.varRegLogin.rpi = 0;
-       mb->un.varRegLogin.vpi = vpi;
+       if (phba->sli_rev == LPFC_SLI_REV4) {
+               mb->un.varRegLogin.rpi = lpfc_sli4_alloc_rpi(phba);
+               if (mb->un.varRegLogin.rpi == LPFC_RPI_ALLOC_ERROR)
+                       return 1;
+       }
+
+       mb->un.varRegLogin.vpi = vpi + phba->vpi_base;
        mb->un.varRegLogin.did = did;
        mb->un.varWords[30] = flag;     /* Set flag to issue action on cmpl */
 
@@ -697,15 +745,16 @@ lpfc_unreg_login(struct lpfc_hba *phba, uint16_t vpi, uint32_t rpi,
 {
        MAILBOX_t *mb;
 
-       mb = &pmb->mb;
+       mb = &pmb->u.mb;
        memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
 
        mb->un.varUnregLogin.rpi = (uint16_t) rpi;
        mb->un.varUnregLogin.rsvd1 = 0;
-       mb->un.varUnregLogin.vpi = vpi;
+       mb->un.varUnregLogin.vpi = vpi + phba->vpi_base;
 
        mb->mbxCommand = MBX_UNREG_LOGIN;
        mb->mbxOwner = OWN_HOST;
+
        return;
 }
 
@@ -725,15 +774,15 @@ lpfc_unreg_login(struct lpfc_hba *phba, uint16_t vpi, uint32_t rpi,
  * This routine prepares the mailbox command for registering a virtual N_Port.
  **/
 void
-lpfc_reg_vpi(struct lpfc_hba *phba, uint16_t vpi, uint32_t sid,
-            LPFC_MBOXQ_t *pmb)
+lpfc_reg_vpi(struct lpfc_vport *vport, LPFC_MBOXQ_t *pmb)
 {
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
 
        memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
 
-       mb->un.varRegVpi.vpi = vpi;
-       mb->un.varRegVpi.sid = sid;
+       mb->un.varRegVpi.vpi = vport->vpi + vport->phba->vpi_base;
+       mb->un.varRegVpi.sid = vport->fc_myDID;
+       mb->un.varRegVpi.vfi = vport->vfi + vport->phba->vfi_base;
 
        mb->mbxCommand = MBX_REG_VPI;
        mb->mbxOwner = OWN_HOST;
@@ -760,10 +809,10 @@ lpfc_reg_vpi(struct lpfc_hba *phba, uint16_t vpi, uint32_t sid,
 void
 lpfc_unreg_vpi(struct lpfc_hba *phba, uint16_t vpi, LPFC_MBOXQ_t *pmb)
 {
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
        memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
 
-       mb->un.varUnregVpi.vpi = vpi;
+       mb->un.varUnregVpi.vpi = vpi + phba->vpi_base;
 
        mb->mbxCommand = MBX_UNREG_VPI;
        mb->mbxOwner = OWN_HOST;
@@ -852,7 +901,7 @@ lpfc_config_pcb_setup(struct lpfc_hba * phba)
 void
 lpfc_read_rev(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
 {
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
        memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
        mb->un.varRdRev.cv = 1;
        mb->un.varRdRev.v3req = 1; /* Request SLI3 info */
@@ -945,7 +994,7 @@ lpfc_config_hbq(struct lpfc_hba *phba, uint32_t id,
                uint32_t hbq_entry_index, LPFC_MBOXQ_t *pmb)
 {
        int i;
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
        struct config_hbq_var *hbqmb = &mb->un.varCfgHbq;
 
        memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
@@ -1020,7 +1069,7 @@ void
 lpfc_config_ring(struct lpfc_hba * phba, int ring, LPFC_MBOXQ_t * pmb)
 {
        int i;
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
        struct lpfc_sli *psli;
        struct lpfc_sli_ring *pring;
 
@@ -1075,7 +1124,7 @@ void
 lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
 {
        MAILBOX_t __iomem *mb_slim = (MAILBOX_t __iomem *) phba->MBslimaddr;
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
        dma_addr_t pdma_addr;
        uint32_t bar_low, bar_high;
        size_t offset;
@@ -1099,21 +1148,22 @@ lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
 
        /* If HBA supports SLI=3 ask for it */
 
-       if (phba->sli_rev == 3 && phba->vpd.sli3Feat.cerbm) {
+       if (phba->sli_rev == LPFC_SLI_REV3 && phba->vpd.sli3Feat.cerbm) {
                if (phba->cfg_enable_bg)
                        mb->un.varCfgPort.cbg = 1; /* configure BlockGuard */
+               mb->un.varCfgPort.cdss = 1; /* Configure Security */
                mb->un.varCfgPort.cerbm = 1; /* Request HBQs */
                mb->un.varCfgPort.ccrp = 1; /* Command Ring Polling */
                mb->un.varCfgPort.cinb = 1; /* Interrupt Notification Block */
                mb->un.varCfgPort.max_hbq = lpfc_sli_hbq_count();
                if (phba->max_vpi && phba->cfg_enable_npiv &&
                    phba->vpd.sli3Feat.cmv) {
-                       mb->un.varCfgPort.max_vpi = phba->max_vpi;
+                       mb->un.varCfgPort.max_vpi = LPFC_MAX_VPI;
                        mb->un.varCfgPort.cmv = 1;
                } else
                        mb->un.varCfgPort.max_vpi = phba->max_vpi = 0;
        } else
-               phba->sli_rev = 2;
+               phba->sli_rev = LPFC_SLI_REV2;
        mb->un.varCfgPort.sli_mode = phba->sli_rev;
 
        /* Now setup pcb */
@@ -1245,7 +1295,7 @@ lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
 void
 lpfc_kill_board(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
 {
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
 
        memset(pmb, 0, sizeof(LPFC_MBOXQ_t));
        mb->mbxCommand = MBX_KILL_BOARD;
@@ -1304,29 +1354,98 @@ lpfc_mbox_get(struct lpfc_hba * phba)
        return mbq;
 }
 
+/**
+ * __lpfc_mbox_cmpl_put - Put mailbox cmd into mailbox cmd complete list
+ * @phba: pointer to lpfc hba data structure.
+ * @mbq: pointer to the driver internal queue element for mailbox command.
+ *
+ * This routine put the completed mailbox command into the mailbox command
+ * complete list. This is the unlocked version of the routine. The mailbox
+ * complete list is used by the driver worker thread to process mailbox
+ * complete callback functions outside the driver interrupt handler.
+ **/
+void
+__lpfc_mbox_cmpl_put(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbq)
+{
+       list_add_tail(&mbq->list, &phba->sli.mboxq_cmpl);
+}
+
 /**
  * lpfc_mbox_cmpl_put - Put mailbox command into mailbox command complete list
  * @phba: pointer to lpfc hba data structure.
  * @mbq: pointer to the driver internal queue element for mailbox command.
  *
  * This routine put the completed mailbox command into the mailbox command
- * complete list. This routine is called from driver interrupt handler
- * context.The mailbox complete list is used by the driver worker thread
- * to process mailbox complete callback functions outside the driver interrupt
- * handler.
+ * complete list. This is the locked version of the routine. The mailbox
+ * complete list is used by the driver worker thread to process mailbox
+ * complete callback functions outside the driver interrupt handler.
  **/
 void
-lpfc_mbox_cmpl_put(struct lpfc_hba * phba, LPFC_MBOXQ_t * mbq)
+lpfc_mbox_cmpl_put(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbq)
 {
        unsigned long iflag;
 
        /* This function expects to be called from interrupt context */
        spin_lock_irqsave(&phba->hbalock, iflag);
-       list_add_tail(&mbq->list, &phba->sli.mboxq_cmpl);
+       __lpfc_mbox_cmpl_put(phba, mbq);
        spin_unlock_irqrestore(&phba->hbalock, iflag);
        return;
 }
 
+/**
+ * lpfc_mbox_cmd_check - Check the validality of a mailbox command
+ * @phba: pointer to lpfc hba data structure.
+ * @mboxq: pointer to the driver internal queue element for mailbox command.
+ *
+ * This routine is to check whether a mailbox command is valid to be issued.
+ * This check will be performed by both the mailbox issue API when a client
+ * is to issue a mailbox command to the mailbox transport.
+ *
+ * Return 0 - pass the check, -ENODEV - fail the check
+ **/
+int
+lpfc_mbox_cmd_check(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+       /* Mailbox command that have a completion handler must also have a
+        * vport specified.
+        */
+       if (mboxq->mbox_cmpl && mboxq->mbox_cmpl != lpfc_sli_def_mbox_cmpl &&
+           mboxq->mbox_cmpl != lpfc_sli_wake_mbox_wait) {
+               if (!mboxq->vport) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_VPORT,
+                                       "1814 Mbox x%x failed, no vport\n",
+                                       mboxq->u.mb.mbxCommand);
+                       dump_stack();
+                       return -ENODEV;
+               }
+       }
+       return 0;
+}
+
+/**
+ * lpfc_mbox_dev_check - Check the device state for issuing a mailbox command
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is to check whether the HBA device is ready for posting a
+ * mailbox command. It is used by the mailbox transport API at the time the
+ * to post a mailbox command to the device.
+ *
+ * Return 0 - pass the check, -ENODEV - fail the check
+ **/
+int
+lpfc_mbox_dev_check(struct lpfc_hba *phba)
+{
+       /* If the PCI channel is in offline state, do not issue mbox */
+       if (unlikely(pci_channel_offline(phba->pcidev)))
+               return -ENODEV;
+
+       /* If the HBA is in error state, do not issue mbox */
+       if (phba->link_state == LPFC_HBA_ERROR)
+               return -ENODEV;
+
+       return 0;
+}
+
 /**
  * lpfc_mbox_tmo_val - Retrieve mailbox command timeout value
  * @phba: pointer to lpfc hba data structure.
@@ -1350,6 +1469,475 @@ lpfc_mbox_tmo_val(struct lpfc_hba *phba, int cmd)
        case MBX_WRITE_WWN:     /* 0x98 */
        case MBX_LOAD_EXP_ROM:  /* 0x9C */
                return LPFC_MBOX_TMO_FLASH_CMD;
+       case MBX_SLI4_CONFIG:   /* 0x9b */
+               return LPFC_MBOX_SLI4_CONFIG_TMO;
        }
        return LPFC_MBOX_TMO;
 }
+
+/**
+ * lpfc_sli4_mbx_sge_set - Set a sge entry in non-embedded mailbox command
+ * @mbox: pointer to lpfc mbox command.
+ * @sgentry: sge entry index.
+ * @phyaddr: physical address for the sge
+ * @length: Length of the sge.
+ *
+ * This routine sets up an entry in the non-embedded mailbox command at the sge
+ * index location.
+ **/
+void
+lpfc_sli4_mbx_sge_set(struct lpfcMboxq *mbox, uint32_t sgentry,
+                     dma_addr_t phyaddr, uint32_t length)
+{
+       struct lpfc_mbx_nembed_cmd *nembed_sge;
+
+       nembed_sge = (struct lpfc_mbx_nembed_cmd *)
+                               &mbox->u.mqe.un.nembed_cmd;
+       nembed_sge->sge[sgentry].pa_lo = putPaddrLow(phyaddr);
+       nembed_sge->sge[sgentry].pa_hi = putPaddrHigh(phyaddr);
+       nembed_sge->sge[sgentry].length = length;
+}
+
+/**
+ * lpfc_sli4_mbx_sge_get - Get a sge entry from non-embedded mailbox command
+ * @mbox: pointer to lpfc mbox command.
+ * @sgentry: sge entry index.
+ *
+ * This routine gets an entry from the non-embedded mailbox command at the sge
+ * index location.
+ **/
+void
+lpfc_sli4_mbx_sge_get(struct lpfcMboxq *mbox, uint32_t sgentry,
+                     struct lpfc_mbx_sge *sge)
+{
+       struct lpfc_mbx_nembed_cmd *nembed_sge;
+
+       nembed_sge = (struct lpfc_mbx_nembed_cmd *)
+                               &mbox->u.mqe.un.nembed_cmd;
+       sge->pa_lo = nembed_sge->sge[sgentry].pa_lo;
+       sge->pa_hi = nembed_sge->sge[sgentry].pa_hi;
+       sge->length = nembed_sge->sge[sgentry].length;
+}
+
+/**
+ * lpfc_sli4_mbox_cmd_free - Free a sli4 mailbox command
+ * @phba: pointer to lpfc hba data structure.
+ * @mbox: pointer to lpfc mbox command.
+ *
+ * This routine frees SLI4 specific mailbox command for sending IOCTL command.
+ **/
+void
+lpfc_sli4_mbox_cmd_free(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
+{
+       struct lpfc_mbx_sli4_config *sli4_cfg;
+       struct lpfc_mbx_sge sge;
+       dma_addr_t phyaddr;
+       uint32_t sgecount, sgentry;
+
+       sli4_cfg = &mbox->u.mqe.un.sli4_config;
+
+       /* For embedded mbox command, just free the mbox command */
+       if (bf_get(lpfc_mbox_hdr_emb, &sli4_cfg->header.cfg_mhdr)) {
+               mempool_free(mbox, phba->mbox_mem_pool);
+               return;
+       }
+
+       /* For non-embedded mbox command, we need to free the pages first */
+       sgecount = bf_get(lpfc_mbox_hdr_sge_cnt, &sli4_cfg->header.cfg_mhdr);
+       /* There is nothing we can do if there is no sge address array */
+       if (unlikely(!mbox->sge_array)) {
+               mempool_free(mbox, phba->mbox_mem_pool);
+               return;
+       }
+       /* Each non-embedded DMA memory was allocated in the length of a page */
+       for (sgentry = 0; sgentry < sgecount; sgentry++) {
+               lpfc_sli4_mbx_sge_get(mbox, sgentry, &sge);
+               phyaddr = getPaddr(sge.pa_hi, sge.pa_lo);
+               dma_free_coherent(&phba->pcidev->dev, PAGE_SIZE,
+                                 mbox->sge_array->addr[sgentry], phyaddr);
+       }
+       /* Free the sge address array memory */
+       kfree(mbox->sge_array);
+       /* Finally, free the mailbox command itself */
+       mempool_free(mbox, phba->mbox_mem_pool);
+}
+
+/**
+ * lpfc_sli4_config - Initialize the  SLI4 Config Mailbox command
+ * @phba: pointer to lpfc hba data structure.
+ * @mbox: pointer to lpfc mbox command.
+ * @subsystem: The sli4 config sub mailbox subsystem.
+ * @opcode: The sli4 config sub mailbox command opcode.
+ * @length: Length of the sli4 config mailbox command.
+ *
+ * This routine sets up the header fields of SLI4 specific mailbox command
+ * for sending IOCTL command.
+ *
+ * Return: the actual length of the mbox command allocated (mostly useful
+ *         for none embedded mailbox command).
+ **/
+int
+lpfc_sli4_config(struct lpfc_hba *phba, struct lpfcMboxq *mbox,
+                uint8_t subsystem, uint8_t opcode, uint32_t length, bool emb)
+{
+       struct lpfc_mbx_sli4_config *sli4_config;
+       union lpfc_sli4_cfg_shdr *cfg_shdr = NULL;
+       uint32_t alloc_len;
+       uint32_t resid_len;
+       uint32_t pagen, pcount;
+       void *viraddr;
+       dma_addr_t phyaddr;
+
+       /* Set up SLI4 mailbox command header fields */
+       memset(mbox, 0, sizeof(*mbox));
+       bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_SLI4_CONFIG);
+
+       /* Set up SLI4 ioctl command header fields */
+       sli4_config = &mbox->u.mqe.un.sli4_config;
+
+       /* Setup for the embedded mbox command */
+       if (emb) {
+               /* Set up main header fields */
+               bf_set(lpfc_mbox_hdr_emb, &sli4_config->header.cfg_mhdr, 1);
+               sli4_config->header.cfg_mhdr.payload_length =
+                                       LPFC_MBX_CMD_HDR_LENGTH + length;
+               /* Set up sub-header fields following main header */
+               bf_set(lpfc_mbox_hdr_opcode,
+                       &sli4_config->header.cfg_shdr.request, opcode);
+               bf_set(lpfc_mbox_hdr_subsystem,
+                       &sli4_config->header.cfg_shdr.request, subsystem);
+               sli4_config->header.cfg_shdr.request.request_length = length;
+               return length;
+       }
+
+       /* Setup for the none-embedded mbox command */
+       pcount = (PAGE_ALIGN(length))/PAGE_SIZE;
+       pcount = (pcount > LPFC_SLI4_MBX_SGE_MAX_PAGES) ?
+                               LPFC_SLI4_MBX_SGE_MAX_PAGES : pcount;
+       /* Allocate record for keeping SGE virtual addresses */
+       mbox->sge_array = kmalloc(sizeof(struct lpfc_mbx_nembed_sge_virt),
+                                 GFP_KERNEL);
+       if (!mbox->sge_array)
+               return 0;
+
+       for (pagen = 0, alloc_len = 0; pagen < pcount; pagen++) {
+               /* The DMA memory is always allocated in the length of a
+                * page even though the last SGE might not fill up to a
+                * page, this is used as a priori size of PAGE_SIZE for
+                * the later DMA memory free.
+                */
+               viraddr = dma_alloc_coherent(&phba->pcidev->dev, PAGE_SIZE,
+                                            &phyaddr, GFP_KERNEL);
+               /* In case of malloc fails, proceed with whatever we have */
+               if (!viraddr)
+                       break;
+               mbox->sge_array->addr[pagen] = viraddr;
+               /* Keep the first page for later sub-header construction */
+               if (pagen == 0)
+                       cfg_shdr = (union lpfc_sli4_cfg_shdr *)viraddr;
+               resid_len = length - alloc_len;
+               if (resid_len > PAGE_SIZE) {
+                       lpfc_sli4_mbx_sge_set(mbox, pagen, phyaddr,
+                                             PAGE_SIZE);
+                       alloc_len += PAGE_SIZE;
+               } else {
+                       lpfc_sli4_mbx_sge_set(mbox, pagen, phyaddr,
+                                             resid_len);
+                       alloc_len = length;
+               }
+       }
+
+       /* Set up main header fields in mailbox command */
+       sli4_config->header.cfg_mhdr.payload_length = alloc_len;
+       bf_set(lpfc_mbox_hdr_sge_cnt, &sli4_config->header.cfg_mhdr, pagen);
+
+       /* Set up sub-header fields into the first page */
+       if (pagen > 0) {
+               bf_set(lpfc_mbox_hdr_opcode, &cfg_shdr->request, opcode);
+               bf_set(lpfc_mbox_hdr_subsystem, &cfg_shdr->request, subsystem);
+               cfg_shdr->request.request_length =
+                               alloc_len - sizeof(union  lpfc_sli4_cfg_shdr);
+       }
+       /* The sub-header is in DMA memory, which needs endian converstion */
+       lpfc_sli_pcimem_bcopy(cfg_shdr, cfg_shdr,
+                             sizeof(union  lpfc_sli4_cfg_shdr));
+
+       return alloc_len;
+}
+
+/**
+ * lpfc_sli4_mbox_opcode_get - Get the opcode from a sli4 mailbox command
+ * @phba: pointer to lpfc hba data structure.
+ * @mbox: pointer to lpfc mbox command.
+ *
+ * This routine gets the opcode from a SLI4 specific mailbox command for
+ * sending IOCTL command. If the mailbox command is not MBX_SLI4_CONFIG
+ * (0x9B) or if the IOCTL sub-header is not present, opcode 0x0 shall be
+ * returned.
+ **/
+uint8_t
+lpfc_sli4_mbox_opcode_get(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
+{
+       struct lpfc_mbx_sli4_config *sli4_cfg;
+       union lpfc_sli4_cfg_shdr *cfg_shdr;
+
+       if (mbox->u.mb.mbxCommand != MBX_SLI4_CONFIG)
+               return 0;
+       sli4_cfg = &mbox->u.mqe.un.sli4_config;
+
+       /* For embedded mbox command, get opcode from embedded sub-header*/
+       if (bf_get(lpfc_mbox_hdr_emb, &sli4_cfg->header.cfg_mhdr)) {
+               cfg_shdr = &mbox->u.mqe.un.sli4_config.header.cfg_shdr;
+               return bf_get(lpfc_mbox_hdr_opcode, &cfg_shdr->request);
+       }
+
+       /* For non-embedded mbox command, get opcode from first dma page */
+       if (unlikely(!mbox->sge_array))
+               return 0;
+       cfg_shdr = (union lpfc_sli4_cfg_shdr *)mbox->sge_array->addr[0];
+       return bf_get(lpfc_mbox_hdr_opcode, &cfg_shdr->request);
+}
+
+/**
+ * lpfc_request_features: Configure SLI4 REQUEST_FEATURES mailbox
+ * @mboxq: pointer to lpfc mbox command.
+ *
+ * This routine sets up the mailbox for an SLI4 REQUEST_FEATURES
+ * mailbox command.
+ **/
+void
+lpfc_request_features(struct lpfc_hba *phba, struct lpfcMboxq *mboxq)
+{
+       /* Set up SLI4 mailbox command header fields */
+       memset(mboxq, 0, sizeof(LPFC_MBOXQ_t));
+       bf_set(lpfc_mqe_command, &mboxq->u.mqe, MBX_SLI4_REQ_FTRS);
+
+       /* Set up host requested features. */
+       bf_set(lpfc_mbx_rq_ftr_rq_fcpi, &mboxq->u.mqe.un.req_ftrs, 1);
+
+       /* Virtual fabrics and FIPs are not supported yet. */
+       bf_set(lpfc_mbx_rq_ftr_rq_ifip, &mboxq->u.mqe.un.req_ftrs, 0);
+
+       /* Enable DIF (block guard) only if configured to do so. */
+       if (phba->cfg_enable_bg)
+               bf_set(lpfc_mbx_rq_ftr_rq_dif, &mboxq->u.mqe.un.req_ftrs, 1);
+
+       /* Enable NPIV only if configured to do so. */
+       if (phba->max_vpi && phba->cfg_enable_npiv)
+               bf_set(lpfc_mbx_rq_ftr_rq_npiv, &mboxq->u.mqe.un.req_ftrs, 1);
+
+       return;
+}
+
+/**
+ * lpfc_init_vfi - Initialize the INIT_VFI mailbox command
+ * @mbox: pointer to lpfc mbox command to initialize.
+ * @vport: Vport associated with the VF.
+ *
+ * This routine initializes @mbox to all zeros and then fills in the mailbox
+ * fields from @vport. INIT_VFI configures virtual fabrics identified by VFI
+ * in the context of an FCF. The driver issues this command to setup a VFI
+ * before issuing a FLOGI to login to the VSAN. The driver should also issue a
+ * REG_VFI after a successful VSAN login.
+ **/
+void
+lpfc_init_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport)
+{
+       struct lpfc_mbx_init_vfi *init_vfi;
+
+       memset(mbox, 0, sizeof(*mbox));
+       init_vfi = &mbox->u.mqe.un.init_vfi;
+       bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_INIT_VFI);
+       bf_set(lpfc_init_vfi_vr, init_vfi, 1);
+       bf_set(lpfc_init_vfi_vt, init_vfi, 1);
+       bf_set(lpfc_init_vfi_vfi, init_vfi, vport->vfi + vport->phba->vfi_base);
+       bf_set(lpfc_init_vfi_fcfi, init_vfi, vport->phba->fcf.fcfi);
+}
+
+/**
+ * lpfc_reg_vfi - Initialize the REG_VFI mailbox command
+ * @mbox: pointer to lpfc mbox command to initialize.
+ * @vport: vport associated with the VF.
+ * @phys: BDE DMA bus address used to send the service parameters to the HBA.
+ *
+ * This routine initializes @mbox to all zeros and then fills in the mailbox
+ * fields from @vport, and uses @buf as a DMAable buffer to send the vport's
+ * fc service parameters to the HBA for this VFI. REG_VFI configures virtual
+ * fabrics identified by VFI in the context of an FCF.
+ **/
+void
+lpfc_reg_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport, dma_addr_t phys)
+{
+       struct lpfc_mbx_reg_vfi *reg_vfi;
+
+       memset(mbox, 0, sizeof(*mbox));
+       reg_vfi = &mbox->u.mqe.un.reg_vfi;
+       bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_REG_VFI);
+       bf_set(lpfc_reg_vfi_vp, reg_vfi, 1);
+       bf_set(lpfc_reg_vfi_vfi, reg_vfi, vport->vfi + vport->phba->vfi_base);
+       bf_set(lpfc_reg_vfi_fcfi, reg_vfi, vport->phba->fcf.fcfi);
+       bf_set(lpfc_reg_vfi_vpi, reg_vfi, vport->vpi + vport->phba->vpi_base);
+       reg_vfi->bde.addrHigh = putPaddrHigh(phys);
+       reg_vfi->bde.addrLow = putPaddrLow(phys);
+       reg_vfi->bde.tus.f.bdeSize = sizeof(vport->fc_sparam);
+       reg_vfi->bde.tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+       bf_set(lpfc_reg_vfi_nport_id, reg_vfi, vport->fc_myDID);
+}
+
+/**
+ * lpfc_init_vpi - Initialize the INIT_VPI mailbox command
+ * @mbox: pointer to lpfc mbox command to initialize.
+ * @vpi: VPI to be initialized.
+ *
+ * The INIT_VPI mailbox command supports virtual N_Ports. The driver uses the
+ * command to activate a virtual N_Port. The HBA assigns a MAC address to use
+ * with the virtual N Port.  The SLI Host issues this command before issuing a
+ * FDISC to connect to the Fabric. The SLI Host should issue a REG_VPI after a
+ * successful virtual NPort login.
+ **/
+void
+lpfc_init_vpi(struct lpfcMboxq *mbox, uint16_t vpi)
+{
+       memset(mbox, 0, sizeof(*mbox));
+       bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_INIT_VPI);
+       bf_set(lpfc_init_vpi_vpi, &mbox->u.mqe.un.init_vpi, vpi);
+}
+
+/**
+ * lpfc_unreg_vfi - Initialize the UNREG_VFI mailbox command
+ * @mbox: pointer to lpfc mbox command to initialize.
+ * @vfi: VFI to be unregistered.
+ *
+ * The UNREG_VFI mailbox command causes the SLI Host to put a virtual fabric
+ * (logical NPort) into the inactive state. The SLI Host must have logged out
+ * and unregistered all remote N_Ports to abort any activity on the virtual
+ * fabric. The SLI Port posts the mailbox response after marking the virtual
+ * fabric inactive.
+ **/
+void
+lpfc_unreg_vfi(struct lpfcMboxq *mbox, uint16_t vfi)
+{
+       memset(mbox, 0, sizeof(*mbox));
+       bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_UNREG_VFI);
+       bf_set(lpfc_unreg_vfi_vfi, &mbox->u.mqe.un.unreg_vfi, vfi);
+}
+
+/**
+ * lpfc_dump_fcoe_param - Dump config region 23 to get FCoe parameters.
+ * @phba: pointer to the hba structure containing.
+ * @mbox: pointer to lpfc mbox command to initialize.
+ *
+ * This function create a SLI4 dump mailbox command to dump FCoE
+ * parameters stored in region 23.
+ **/
+int
+lpfc_dump_fcoe_param(struct lpfc_hba *phba,
+               struct lpfcMboxq *mbox)
+{
+       struct lpfc_dmabuf *mp = NULL;
+       MAILBOX_t *mb;
+
+       memset(mbox, 0, sizeof(*mbox));
+       mb = &mbox->u.mb;
+
+       mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+       if (mp)
+               mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys);
+
+       if (!mp || !mp->virt) {
+               kfree(mp);
+               /* dump_fcoe_param failed to allocate memory */
+               lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
+                       "2569 lpfc_dump_fcoe_param: memory"
+                       " allocation failed \n");
+               return 1;
+       }
+
+       memset(mp->virt, 0, LPFC_BPL_SIZE);
+       INIT_LIST_HEAD(&mp->list);
+
+       /* save address for completion */
+       mbox->context1 = (uint8_t *) mp;
+
+       mb->mbxCommand = MBX_DUMP_MEMORY;
+       mb->un.varDmp.type = DMP_NV_PARAMS;
+       mb->un.varDmp.region_id = DMP_REGION_FCOEPARAM;
+       mb->un.varDmp.sli4_length = DMP_FCOEPARAM_RGN_SIZE;
+       mb->un.varWords[3] = putPaddrLow(mp->phys);
+       mb->un.varWords[4] = putPaddrHigh(mp->phys);
+       return 0;
+}
+
+/**
+ * lpfc_reg_fcfi - Initialize the REG_FCFI mailbox command
+ * @phba: pointer to the hba structure containing the FCF index and RQ ID.
+ * @mbox: pointer to lpfc mbox command to initialize.
+ *
+ * The REG_FCFI mailbox command supports Fibre Channel Forwarders (FCFs). The
+ * SLI Host uses the command to activate an FCF after it has acquired FCF
+ * information via a READ_FCF mailbox command. This mailbox command also is used
+ * to indicate where received unsolicited frames from this FCF will be sent. By
+ * default this routine will set up the FCF to forward all unsolicited frames
+ * the the RQ ID passed in the @phba. This can be overridden by the caller for
+ * more complicated setups.
+ **/
+void
+lpfc_reg_fcfi(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
+{
+       struct lpfc_mbx_reg_fcfi *reg_fcfi;
+
+       memset(mbox, 0, sizeof(*mbox));
+       reg_fcfi = &mbox->u.mqe.un.reg_fcfi;
+       bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_REG_FCFI);
+       bf_set(lpfc_reg_fcfi_rq_id0, reg_fcfi, phba->sli4_hba.hdr_rq->queue_id);
+       bf_set(lpfc_reg_fcfi_rq_id1, reg_fcfi, REG_FCF_INVALID_QID);
+       bf_set(lpfc_reg_fcfi_rq_id2, reg_fcfi, REG_FCF_INVALID_QID);
+       bf_set(lpfc_reg_fcfi_rq_id3, reg_fcfi, REG_FCF_INVALID_QID);
+       bf_set(lpfc_reg_fcfi_info_index, reg_fcfi, phba->fcf.fcf_indx);
+       /* reg_fcf addr mode is bit wise inverted value of fcf addr_mode */
+       bf_set(lpfc_reg_fcfi_mam, reg_fcfi,
+               (~phba->fcf.addr_mode) & 0x3);
+       if (phba->fcf.fcf_flag & FCF_VALID_VLAN) {
+               bf_set(lpfc_reg_fcfi_vv, reg_fcfi, 1);
+               bf_set(lpfc_reg_fcfi_vlan_tag, reg_fcfi, phba->fcf.vlan_id);
+       }
+}
+
+/**
+ * lpfc_unreg_fcfi - Initialize the UNREG_FCFI mailbox command
+ * @mbox: pointer to lpfc mbox command to initialize.
+ * @fcfi: FCFI to be unregistered.
+ *
+ * The UNREG_FCFI mailbox command supports Fibre Channel Forwarders (FCFs).
+ * The SLI Host uses the command to inactivate an FCFI.
+ **/
+void
+lpfc_unreg_fcfi(struct lpfcMboxq *mbox, uint16_t fcfi)
+{
+       memset(mbox, 0, sizeof(*mbox));
+       bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_UNREG_FCFI);
+       bf_set(lpfc_unreg_fcfi, &mbox->u.mqe.un.unreg_fcfi, fcfi);
+}
+
+/**
+ * lpfc_resume_rpi - Initialize the RESUME_RPI mailbox command
+ * @mbox: pointer to lpfc mbox command to initialize.
+ * @ndlp: The nodelist structure that describes the RPI to resume.
+ *
+ * The RESUME_RPI mailbox command is used to restart I/O to an RPI after a
+ * link event.
+ **/
+void
+lpfc_resume_rpi(struct lpfcMboxq *mbox, struct lpfc_nodelist *ndlp)
+{
+       struct lpfc_mbx_resume_rpi *resume_rpi;
+
+       memset(mbox, 0, sizeof(*mbox));
+       resume_rpi = &mbox->u.mqe.un.resume_rpi;
+       bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_RESUME_RPI);
+       bf_set(lpfc_resume_rpi_rpi, resume_rpi, ndlp->nlp_rpi);
+       bf_set(lpfc_resume_rpi_vpi, resume_rpi,
+              ndlp->vport->vpi + ndlp->vport->phba->vpi_base);
+       bf_set(lpfc_resume_rpi_vfi, resume_rpi,
+              ndlp->vport->vfi + ndlp->vport->phba->vfi_base);
+}
index 35a976733398827968083062e2377543d0c1c8a3..e198c917c13ecc55d91839d8ca64a8051ee7f097 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2008 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2009 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
 
 #include <scsi/scsi.h>
 
+#include "lpfc_hw4.h"
 #include "lpfc_hw.h"
 #include "lpfc_sli.h"
+#include "lpfc_sli4.h"
 #include "lpfc_nl.h"
 #include "lpfc_disc.h"
 #include "lpfc_scsi.h"
@@ -45,7 +47,7 @@
  * @phba: HBA to allocate pools for
  *
  * Description: Creates and allocates PCI pools lpfc_scsi_dma_buf_pool,
- * lpfc_mbuf_pool, lpfc_hbq_pool.  Creates and allocates kmalloc-backed mempools
+ * lpfc_mbuf_pool, lpfc_hrb_pool.  Creates and allocates kmalloc-backed mempools
  * for LPFC_MBOXQ_t and lpfc_nodelist.  Also allocates the VPI bitmask.
  *
  * Notes: Not interrupt-safe.  Must be called with no locks held.  If any
  *   -ENOMEM on failure (if any memory allocations fail)
  **/
 int
-lpfc_mem_alloc(struct lpfc_hba * phba)
+lpfc_mem_alloc(struct lpfc_hba *phba, int align)
 {
        struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool;
        int longs;
        int i;
 
-       phba->lpfc_scsi_dma_buf_pool = pci_pool_create("lpfc_scsi_dma_buf_pool",
-                               phba->pcidev, phba->cfg_sg_dma_buf_size, 8, 0);
+       if (phba->sli_rev == LPFC_SLI_REV4)
+               phba->lpfc_scsi_dma_buf_pool =
+                       pci_pool_create("lpfc_scsi_dma_buf_pool",
+                               phba->pcidev,
+                               phba->cfg_sg_dma_buf_size,
+                               phba->cfg_sg_dma_buf_size,
+                               0);
+       else
+               phba->lpfc_scsi_dma_buf_pool =
+                       pci_pool_create("lpfc_scsi_dma_buf_pool",
+                               phba->pcidev, phba->cfg_sg_dma_buf_size,
+                               align, 0);
        if (!phba->lpfc_scsi_dma_buf_pool)
                goto fail;
 
        phba->lpfc_mbuf_pool = pci_pool_create("lpfc_mbuf_pool", phba->pcidev,
-                                                       LPFC_BPL_SIZE, 8,0);
+                                                       LPFC_BPL_SIZE,
+                                                       align, 0);
        if (!phba->lpfc_mbuf_pool)
                goto fail_free_dma_buf_pool;
 
@@ -97,23 +110,31 @@ lpfc_mem_alloc(struct lpfc_hba * phba)
                                                sizeof(struct lpfc_nodelist));
        if (!phba->nlp_mem_pool)
                goto fail_free_mbox_pool;
-
-       phba->lpfc_hbq_pool = pci_pool_create("lpfc_hbq_pool",phba->pcidev,
-                                             LPFC_BPL_SIZE, 8, 0);
-       if (!phba->lpfc_hbq_pool)
+       phba->lpfc_hrb_pool = pci_pool_create("lpfc_hrb_pool",
+                                             phba->pcidev,
+                                             LPFC_HDR_BUF_SIZE, align, 0);
+       if (!phba->lpfc_hrb_pool)
                goto fail_free_nlp_mem_pool;
+       phba->lpfc_drb_pool = pci_pool_create("lpfc_drb_pool",
+                                             phba->pcidev,
+                                             LPFC_DATA_BUF_SIZE, align, 0);
+       if (!phba->lpfc_drb_pool)
+               goto fail_free_hbq_pool;
 
        /* vpi zero is reserved for the physical port so add 1 to max */
        longs = ((phba->max_vpi + 1) + BITS_PER_LONG - 1) / BITS_PER_LONG;
        phba->vpi_bmask = kzalloc(longs * sizeof(unsigned long), GFP_KERNEL);
        if (!phba->vpi_bmask)
-               goto fail_free_hbq_pool;
+               goto fail_free_dbq_pool;
 
        return 0;
 
+ fail_free_dbq_pool:
+       pci_pool_destroy(phba->lpfc_drb_pool);
+       phba->lpfc_drb_pool = NULL;
  fail_free_hbq_pool:
-       lpfc_sli_hbqbuf_free_all(phba);
-       pci_pool_destroy(phba->lpfc_hbq_pool);
+       pci_pool_destroy(phba->lpfc_hrb_pool);
+       phba->lpfc_hrb_pool = NULL;
  fail_free_nlp_mem_pool:
        mempool_destroy(phba->nlp_mem_pool);
        phba->nlp_mem_pool = NULL;
@@ -136,27 +157,73 @@ lpfc_mem_alloc(struct lpfc_hba * phba)
 }
 
 /**
- * lpfc_mem_free - Frees all PCI and memory allocated by lpfc_mem_alloc
+ * lpfc_mem_free - Frees memory allocated by lpfc_mem_alloc
  * @phba: HBA to free memory for
  *
- * Description: Frees PCI pools lpfc_scsi_dma_buf_pool, lpfc_mbuf_pool,
- * lpfc_hbq_pool.  Frees kmalloc-backed mempools for LPFC_MBOXQ_t and
- * lpfc_nodelist.  Also frees the VPI bitmask
+ * Description: Free the memory allocated by lpfc_mem_alloc routine. This
+ * routine is a the counterpart of lpfc_mem_alloc.
  *
  * Returns: None
  **/
 void
-lpfc_mem_free(struct lpfc_hba * phba)
+lpfc_mem_free(struct lpfc_hba *phba)
 {
-       struct lpfc_sli *psli = &phba->sli;
-       struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool;
-       LPFC_MBOXQ_t *mbox, *next_mbox;
-       struct lpfc_dmabuf   *mp;
        int i;
+       struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool;
 
+       /* Free VPI bitmask memory */
        kfree(phba->vpi_bmask);
+
+       /* Free HBQ pools */
        lpfc_sli_hbqbuf_free_all(phba);
+       pci_pool_destroy(phba->lpfc_drb_pool);
+       phba->lpfc_drb_pool = NULL;
+       pci_pool_destroy(phba->lpfc_hrb_pool);
+       phba->lpfc_hrb_pool = NULL;
+
+       /* Free NLP memory pool */
+       mempool_destroy(phba->nlp_mem_pool);
+       phba->nlp_mem_pool = NULL;
+
+       /* Free mbox memory pool */
+       mempool_destroy(phba->mbox_mem_pool);
+       phba->mbox_mem_pool = NULL;
+
+       /* Free MBUF memory pool */
+       for (i = 0; i < pool->current_count; i++)
+               pci_pool_free(phba->lpfc_mbuf_pool, pool->elements[i].virt,
+                             pool->elements[i].phys);
+       kfree(pool->elements);
+
+       pci_pool_destroy(phba->lpfc_mbuf_pool);
+       phba->lpfc_mbuf_pool = NULL;
 
+       /* Free DMA buffer memory pool */
+       pci_pool_destroy(phba->lpfc_scsi_dma_buf_pool);
+       phba->lpfc_scsi_dma_buf_pool = NULL;
+
+       return;
+}
+
+/**
+ * lpfc_mem_free_all - Frees all PCI and driver memory
+ * @phba: HBA to free memory for
+ *
+ * Description: Free memory from PCI and driver memory pools and also those
+ * used : lpfc_scsi_dma_buf_pool, lpfc_mbuf_pool, lpfc_hrb_pool. Frees
+ * kmalloc-backed mempools for LPFC_MBOXQ_t and lpfc_nodelist. Also frees
+ * the VPI bitmask.
+ *
+ * Returns: None
+ **/
+void
+lpfc_mem_free_all(struct lpfc_hba *phba)
+{
+       struct lpfc_sli *psli = &phba->sli;
+       LPFC_MBOXQ_t *mbox, *next_mbox;
+       struct lpfc_dmabuf   *mp;
+
+       /* Free memory used in mailbox queue back to mailbox memory pool */
        list_for_each_entry_safe(mbox, next_mbox, &psli->mboxq, list) {
                mp = (struct lpfc_dmabuf *) (mbox->context1);
                if (mp) {
@@ -166,6 +233,7 @@ lpfc_mem_free(struct lpfc_hba * phba)
                list_del(&mbox->list);
                mempool_free(mbox, phba->mbox_mem_pool);
        }
+       /* Free memory used in mailbox cmpl list back to mailbox memory pool */
        list_for_each_entry_safe(mbox, next_mbox, &psli->mboxq_cmpl, list) {
                mp = (struct lpfc_dmabuf *) (mbox->context1);
                if (mp) {
@@ -175,8 +243,10 @@ lpfc_mem_free(struct lpfc_hba * phba)
                list_del(&mbox->list);
                mempool_free(mbox, phba->mbox_mem_pool);
        }
-
+       /* Free the active mailbox command back to the mailbox memory pool */
+       spin_lock_irq(&phba->hbalock);
        psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+       spin_unlock_irq(&phba->hbalock);
        if (psli->mbox_active) {
                mbox = psli->mbox_active;
                mp = (struct lpfc_dmabuf *) (mbox->context1);
@@ -188,27 +258,14 @@ lpfc_mem_free(struct lpfc_hba * phba)
                psli->mbox_active = NULL;
        }
 
-       for (i = 0; i < pool->current_count; i++)
-               pci_pool_free(phba->lpfc_mbuf_pool, pool->elements[i].virt,
-                                                pool->elements[i].phys);
-       kfree(pool->elements);
-
-       pci_pool_destroy(phba->lpfc_hbq_pool);
-       mempool_destroy(phba->nlp_mem_pool);
-       mempool_destroy(phba->mbox_mem_pool);
-
-       pci_pool_destroy(phba->lpfc_scsi_dma_buf_pool);
-       pci_pool_destroy(phba->lpfc_mbuf_pool);
-
-       phba->lpfc_hbq_pool = NULL;
-       phba->nlp_mem_pool = NULL;
-       phba->mbox_mem_pool = NULL;
-       phba->lpfc_scsi_dma_buf_pool = NULL;
-       phba->lpfc_mbuf_pool = NULL;
+       /* Free and destroy all the allocated memory pools */
+       lpfc_mem_free(phba);
 
        /* Free the iocb lookup array */
        kfree(psli->iocbq_lookup);
        psli->iocbq_lookup = NULL;
+
+       return;
 }
 
 /**
@@ -305,7 +362,7 @@ lpfc_mbuf_free(struct lpfc_hba * phba, void *virt, dma_addr_t dma)
  * lpfc_els_hbq_alloc - Allocate an HBQ buffer
  * @phba: HBA to allocate HBQ buffer for
  *
- * Description: Allocates a DMA-mapped HBQ buffer from the lpfc_hbq_pool PCI
+ * Description: Allocates a DMA-mapped HBQ buffer from the lpfc_hrb_pool PCI
  * pool along a non-DMA-mapped container for it.
  *
  * Notes: Not interrupt-safe.  Must be called with no locks held.
@@ -323,7 +380,7 @@ lpfc_els_hbq_alloc(struct lpfc_hba *phba)
        if (!hbqbp)
                return NULL;
 
-       hbqbp->dbuf.virt = pci_pool_alloc(phba->lpfc_hbq_pool, GFP_KERNEL,
+       hbqbp->dbuf.virt = pci_pool_alloc(phba->lpfc_hrb_pool, GFP_KERNEL,
                                          &hbqbp->dbuf.phys);
        if (!hbqbp->dbuf.virt) {
                kfree(hbqbp);
@@ -334,7 +391,7 @@ lpfc_els_hbq_alloc(struct lpfc_hba *phba)
 }
 
 /**
- * lpfc_mem_hbq_free - Frees an HBQ buffer allocated with lpfc_els_hbq_alloc
+ * lpfc_els_hbq_free - Frees an HBQ buffer allocated with lpfc_els_hbq_alloc
  * @phba: HBA buffer was allocated for
  * @hbqbp: HBQ container returned by lpfc_els_hbq_alloc
  *
@@ -348,11 +405,72 @@ lpfc_els_hbq_alloc(struct lpfc_hba *phba)
 void
 lpfc_els_hbq_free(struct lpfc_hba *phba, struct hbq_dmabuf *hbqbp)
 {
-       pci_pool_free(phba->lpfc_hbq_pool, hbqbp->dbuf.virt, hbqbp->dbuf.phys);
+       pci_pool_free(phba->lpfc_hrb_pool, hbqbp->dbuf.virt, hbqbp->dbuf.phys);
        kfree(hbqbp);
        return;
 }
 
+/**
+ * lpfc_sli4_rb_alloc - Allocate an SLI4 Receive buffer
+ * @phba: HBA to allocate a receive buffer for
+ *
+ * Description: Allocates a DMA-mapped receive buffer from the lpfc_hrb_pool PCI
+ * pool along a non-DMA-mapped container for it.
+ *
+ * Notes: Not interrupt-safe.  Must be called with no locks held.
+ *
+ * Returns:
+ *   pointer to HBQ on success
+ *   NULL on failure
+ **/
+struct hbq_dmabuf *
+lpfc_sli4_rb_alloc(struct lpfc_hba *phba)
+{
+       struct hbq_dmabuf *dma_buf;
+
+       dma_buf = kmalloc(sizeof(struct hbq_dmabuf), GFP_KERNEL);
+       if (!dma_buf)
+               return NULL;
+
+       dma_buf->hbuf.virt = pci_pool_alloc(phba->lpfc_hrb_pool, GFP_KERNEL,
+                                           &dma_buf->hbuf.phys);
+       if (!dma_buf->hbuf.virt) {
+               kfree(dma_buf);
+               return NULL;
+       }
+       dma_buf->dbuf.virt = pci_pool_alloc(phba->lpfc_drb_pool, GFP_KERNEL,
+                                           &dma_buf->dbuf.phys);
+       if (!dma_buf->dbuf.virt) {
+               pci_pool_free(phba->lpfc_hrb_pool, dma_buf->hbuf.virt,
+                             dma_buf->hbuf.phys);
+               kfree(dma_buf);
+               return NULL;
+       }
+       dma_buf->size = LPFC_BPL_SIZE;
+       return dma_buf;
+}
+
+/**
+ * lpfc_sli4_rb_free - Frees a receive buffer
+ * @phba: HBA buffer was allocated for
+ * @dmab: DMA Buffer container returned by lpfc_sli4_hbq_alloc
+ *
+ * Description: Frees both the container and the DMA-mapped buffers returned by
+ * lpfc_sli4_rb_alloc.
+ *
+ * Notes: Can be called with or without locks held.
+ *
+ * Returns: None
+ **/
+void
+lpfc_sli4_rb_free(struct lpfc_hba *phba, struct hbq_dmabuf *dmab)
+{
+       pci_pool_free(phba->lpfc_hrb_pool, dmab->hbuf.virt, dmab->hbuf.phys);
+       pci_pool_free(phba->lpfc_drb_pool, dmab->dbuf.virt, dmab->dbuf.phys);
+       kfree(dmab);
+       return;
+}
+
 /**
  * lpfc_in_buf_free - Free a DMA buffer
  * @phba: HBA buffer is associated with
index 08cdc77af41c12f42f34ab2da799f1f678811d7b..09f659f77bb3f2b399850215319eb9121489d181 100644 (file)
@@ -1,7 +1,7 @@
  /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2008 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2009 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport_fc.h>
 
+#include "lpfc_hw4.h"
 #include "lpfc_hw.h"
 #include "lpfc_sli.h"
+#include "lpfc_sli4.h"
 #include "lpfc_nl.h"
 #include "lpfc_disc.h"
 #include "lpfc_scsi.h"
@@ -361,7 +363,7 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
        if (!mbox)
                goto out;
 
-       rc = lpfc_reg_login(phba, vport->vpi, icmd->un.rcvels.remoteID,
+       rc = lpfc_reg_rpi(phba, vport->vpi, icmd->un.rcvels.remoteID,
                            (uint8_t *) sp, mbox, 0);
        if (rc) {
                mempool_free(mbox, phba->mbox_mem_pool);
@@ -495,11 +497,19 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
                lpfc_els_rsp_acc(vport, ELS_CMD_PRLO, cmdiocb, ndlp, NULL);
        else
                lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
+       if ((ndlp->nlp_type & NLP_FABRIC) &&
+               vport->port_type == LPFC_NPIV_PORT) {
+               lpfc_linkdown_port(vport);
+               mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1);
+               spin_lock_irq(shost->host_lock);
+               ndlp->nlp_flag |= NLP_DELAY_TMO;
+               spin_unlock_irq(shost->host_lock);
 
-       if ((!(ndlp->nlp_type & NLP_FABRIC) &&
-            ((ndlp->nlp_type & NLP_FCP_TARGET) ||
-             !(ndlp->nlp_type & NLP_FCP_INITIATOR))) ||
-           (ndlp->nlp_state == NLP_STE_ADISC_ISSUE)) {
+               ndlp->nlp_last_elscmd = ELS_CMD_FDISC;
+       } else if ((!(ndlp->nlp_type & NLP_FABRIC) &&
+               ((ndlp->nlp_type & NLP_FCP_TARGET) ||
+               !(ndlp->nlp_type & NLP_FCP_INITIATOR))) ||
+               (ndlp->nlp_state == NLP_STE_ADISC_ISSUE)) {
                /* Only try to re-login if this is NOT a Fabric Node */
                mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1);
                spin_lock_irq(shost->host_lock);
@@ -567,7 +577,7 @@ lpfc_disc_set_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
 {
        struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
 
-       if (!ndlp->nlp_rpi) {
+       if (!(ndlp->nlp_flag & NLP_RPI_VALID)) {
                ndlp->nlp_flag &= ~NLP_NPR_ADISC;
                return 0;
        }
@@ -857,7 +867,7 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport,
 
        lpfc_unreg_rpi(vport, ndlp);
 
-       if (lpfc_reg_login(phba, vport->vpi, irsp->un.elsreq64.remoteID,
+       if (lpfc_reg_rpi(phba, vport->vpi, irsp->un.elsreq64.remoteID,
                           (uint8_t *) sp, mbox, 0) == 0) {
                switch (ndlp->nlp_DID) {
                case NameServer_DID:
@@ -1068,6 +1078,7 @@ lpfc_cmpl_adisc_adisc_issue(struct lpfc_vport *vport,
        struct lpfc_iocbq *cmdiocb, *rspiocb;
        IOCB_t *irsp;
        ADISC *ap;
+       int rc;
 
        cmdiocb = (struct lpfc_iocbq *) arg;
        rspiocb = cmdiocb->context_un.rsp_iocb;
@@ -1093,6 +1104,15 @@ lpfc_cmpl_adisc_adisc_issue(struct lpfc_vport *vport,
                return ndlp->nlp_state;
        }
 
+       if (phba->sli_rev == LPFC_SLI_REV4) {
+               rc = lpfc_sli4_resume_rpi(ndlp);
+               if (rc) {
+                       /* Stay in state and retry. */
+                       ndlp->nlp_prev_state = NLP_STE_ADISC_ISSUE;
+                       return ndlp->nlp_state;
+               }
+       }
+
        if (ndlp->nlp_type & NLP_FCP_TARGET) {
                ndlp->nlp_prev_state = NLP_STE_ADISC_ISSUE;
                lpfc_nlp_set_state(vport, ndlp, NLP_STE_MAPPED_NODE);
@@ -1100,6 +1120,7 @@ lpfc_cmpl_adisc_adisc_issue(struct lpfc_vport *vport,
                ndlp->nlp_prev_state = NLP_STE_ADISC_ISSUE;
                lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
        }
+
        return ndlp->nlp_state;
 }
 
@@ -1190,7 +1211,7 @@ lpfc_rcv_logo_reglogin_issue(struct lpfc_vport *vport,
 
        /* cleanup any ndlp on mbox q waiting for reglogin cmpl */
        if ((mb = phba->sli.mbox_active)) {
-               if ((mb->mb.mbxCommand == MBX_REG_LOGIN64) &&
+               if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) &&
                   (ndlp == (struct lpfc_nodelist *) mb->context2)) {
                        lpfc_nlp_put(ndlp);
                        mb->context2 = NULL;
@@ -1200,7 +1221,7 @@ lpfc_rcv_logo_reglogin_issue(struct lpfc_vport *vport,
 
        spin_lock_irq(&phba->hbalock);
        list_for_each_entry_safe(mb, nextmb, &phba->sli.mboxq, list) {
-               if ((mb->mb.mbxCommand == MBX_REG_LOGIN64) &&
+               if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) &&
                   (ndlp == (struct lpfc_nodelist *) mb->context2)) {
                        mp = (struct lpfc_dmabuf *) (mb->context1);
                        if (mp) {
@@ -1251,7 +1272,7 @@ lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_vport *vport,
 {
        struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
        LPFC_MBOXQ_t *pmb = (LPFC_MBOXQ_t *) arg;
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
        uint32_t did  = mb->un.varWords[1];
 
        if (mb->mbxStatus) {
@@ -1283,6 +1304,7 @@ lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_vport *vport,
        }
 
        ndlp->nlp_rpi = mb->un.varWords[0];
+       ndlp->nlp_flag |= NLP_RPI_VALID;
 
        /* Only if we are not a fabric nport do we issue PRLI */
        if (!(ndlp->nlp_type & NLP_FABRIC)) {
@@ -1878,11 +1900,12 @@ lpfc_cmpl_reglogin_npr_node(struct lpfc_vport *vport,
                            void *arg, uint32_t evt)
 {
        LPFC_MBOXQ_t *pmb = (LPFC_MBOXQ_t *) arg;
-       MAILBOX_t    *mb = &pmb->mb;
+       MAILBOX_t    *mb = &pmb->u.mb;
 
-       if (!mb->mbxStatus)
+       if (!mb->mbxStatus) {
                ndlp->nlp_rpi = mb->un.varWords[0];
-       else {
+               ndlp->nlp_flag |= NLP_RPI_VALID;
+       } else {
                if (ndlp->nlp_flag & NLP_NODEV_REMOVE) {
                        lpfc_drop_node(vport, ndlp);
                        return NLP_STE_FREED_NODE;
index 8032c5adb6a9b2400c318912a7aa376df512b096..e9fa6762044ae9ef398b27b9e44c51a364338528 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2008 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2009 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
 #include <scsi/scsi_transport_fc.h>
 
 #include "lpfc_version.h"
+#include "lpfc_hw4.h"
 #include "lpfc_hw.h"
 #include "lpfc_sli.h"
+#include "lpfc_sli4.h"
 #include "lpfc_nl.h"
 #include "lpfc_disc.h"
 #include "lpfc_scsi.h"
@@ -57,6 +59,8 @@ static char *dif_op_str[] = {
        "SCSI_PROT_READ_CONVERT",
        "SCSI_PROT_WRITE_CONVERT"
 };
+static void
+lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb);
 
 static void
 lpfc_debug_save_data(struct scsi_cmnd *cmnd)
@@ -325,7 +329,7 @@ lpfc_ramp_down_queue_handler(struct lpfc_hba *phba)
 
        vports = lpfc_create_vport_work_array(phba);
        if (vports != NULL)
-               for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+               for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
                        shost = lpfc_shost_from_vport(vports[i]);
                        shost_for_each_device(sdev, shost) {
                                new_queue_depth =
@@ -379,7 +383,7 @@ lpfc_ramp_up_queue_handler(struct lpfc_hba *phba)
 
        vports = lpfc_create_vport_work_array(phba);
        if (vports != NULL)
-               for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+               for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
                        shost = lpfc_shost_from_vport(vports[i]);
                        shost_for_each_device(sdev, shost) {
                                if (vports[i]->cfg_lun_queue_depth <=
@@ -427,7 +431,7 @@ lpfc_scsi_dev_block(struct lpfc_hba *phba)
 
        vports = lpfc_create_vport_work_array(phba);
        if (vports != NULL)
-               for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+               for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
                        shost = lpfc_shost_from_vport(vports[i]);
                        shost_for_each_device(sdev, shost) {
                                rport = starget_to_rport(scsi_target(sdev));
@@ -438,22 +442,23 @@ lpfc_scsi_dev_block(struct lpfc_hba *phba)
 }
 
 /**
- * lpfc_new_scsi_buf - Scsi buffer allocator
+ * lpfc_new_scsi_buf_s3 - Scsi buffer allocator for HBA with SLI3 IF spec
  * @vport: The virtual port for which this call being executed.
+ * @num_to_allocate: The requested number of buffers to allocate.
  *
- * 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 initial BPL.  In addition to
- * allocating memory, the FCP CMND and FCP RSP BDEs are setup in the BPL
- * and the BPL BDE is setup in the IOCB.
+ * This routine allocates a scsi buffer for device with SLI-3 interface spec,
+ * the scsi buffer 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 initial BPL. In addition to allocating memory, the FCP CMND and
+ * FCP RSP BDEs are setup in the BPL and the BPL BDE is setup in the IOCB.
  *
  * Return codes:
- *   NULL - Error
- *   Pointer to lpfc_scsi_buf data structure - Success
+ *   int - number of scsi buffers that were allocated.
+ *   0 = failure, less than num_to_alloc is a partial failure.
  **/
-static struct lpfc_scsi_buf *
-lpfc_new_scsi_buf(struct lpfc_vport *vport)
+static int
+lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc)
 {
        struct lpfc_hba *phba = vport->phba;
        struct lpfc_scsi_buf *psb;
@@ -463,107 +468,401 @@ lpfc_new_scsi_buf(struct lpfc_vport *vport)
        dma_addr_t pdma_phys_fcp_rsp;
        dma_addr_t pdma_phys_bpl;
        uint16_t iotag;
+       int bcnt;
 
-       psb = kzalloc(sizeof(struct lpfc_scsi_buf), GFP_KERNEL);
-       if (!psb)
-               return NULL;
+       for (bcnt = 0; bcnt < num_to_alloc; bcnt++) {
+               psb = kzalloc(sizeof(struct lpfc_scsi_buf), GFP_KERNEL);
+               if (!psb)
+                       break;
+
+               /*
+                * 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);
+                       break;
+               }
+
+               /* Initialize virtual ptrs to dma_buf region. */
+               memset(psb->data, 0, phba->cfg_sg_dma_buf_size);
+
+               /* Allocate iotag for psb->cur_iocbq. */
+               iotag = lpfc_sli_next_iotag(phba, &psb->cur_iocbq);
+               if (iotag == 0) {
+                       pci_pool_free(phba->lpfc_scsi_dma_buf_pool,
+                                       psb->data, psb->dma_handle);
+                       kfree(psb);
+                       break;
+               }
+               psb->cur_iocbq.iocb_flag |= LPFC_IO_FCP;
+
+               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_fcp_cmd = psb->dma_handle;
+               pdma_phys_fcp_rsp = psb->dma_handle + sizeof(struct fcp_cmnd);
+               pdma_phys_bpl = psb->dma_handle + sizeof(struct fcp_cmnd) +
+                       sizeof(struct fcp_rsp);
+
+               /*
+                * 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[0].addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys_fcp_cmd));
+               bpl[0].addrLow = le32_to_cpu(putPaddrLow(pdma_phys_fcp_cmd));
+               bpl[0].tus.f.bdeSize = sizeof(struct fcp_cmnd);
+               bpl[0].tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+               bpl[0].tus.w = le32_to_cpu(bpl[0].tus.w);
+
+               /* Setup the physical region for the FCP RSP */
+               bpl[1].addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys_fcp_rsp));
+               bpl[1].addrLow = le32_to_cpu(putPaddrLow(pdma_phys_fcp_rsp));
+               bpl[1].tus.f.bdeSize = sizeof(struct fcp_rsp);
+               bpl[1].tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+               bpl[1].tus.w = le32_to_cpu(bpl[1].tus.w);
+
+               /*
+                * Since the IOCB for the FCP I/O is built into this
+                * lpfc_scsi_buf, initialize it with all known data now.
+                */
+               iocb = &psb->cur_iocbq.iocb;
+               iocb->un.fcpi64.bdl.ulpIoTag32 = 0;
+               if ((phba->sli_rev == 3) &&
+                               !(phba->sli3_options & LPFC_SLI3_BG_ENABLED)) {
+                       /* fill in immediate fcp command BDE */
+                       iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDE_IMMED;
+                       iocb->un.fcpi64.bdl.bdeSize = sizeof(struct fcp_cmnd);
+                       iocb->un.fcpi64.bdl.addrLow = offsetof(IOCB_t,
+                                       unsli3.fcp_ext.icd);
+                       iocb->un.fcpi64.bdl.addrHigh = 0;
+                       iocb->ulpBdeCount = 0;
+                       iocb->ulpLe = 0;
+                       /* fill in responce BDE */
+                       iocb->unsli3.fcp_ext.rbde.tus.f.bdeFlags =
+                                                       BUFF_TYPE_BDE_64;
+                       iocb->unsli3.fcp_ext.rbde.tus.f.bdeSize =
+                               sizeof(struct fcp_rsp);
+                       iocb->unsli3.fcp_ext.rbde.addrLow =
+                               putPaddrLow(pdma_phys_fcp_rsp);
+                       iocb->unsli3.fcp_ext.rbde.addrHigh =
+                               putPaddrHigh(pdma_phys_fcp_rsp);
+               } else {
+                       iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BLP_64;
+                       iocb->un.fcpi64.bdl.bdeSize =
+                                       (2 * sizeof(struct ulp_bde64));
+                       iocb->un.fcpi64.bdl.addrLow =
+                                       putPaddrLow(pdma_phys_bpl);
+                       iocb->un.fcpi64.bdl.addrHigh =
+                                       putPaddrHigh(pdma_phys_bpl);
+                       iocb->ulpBdeCount = 1;
+                       iocb->ulpLe = 1;
+               }
+               iocb->ulpClass = CLASS3;
+               psb->status = IOSTAT_SUCCESS;
+               /* Put it back into the SCSI buffer list */
+               lpfc_release_scsi_buf_s4(phba, psb);
 
-       /*
-        * 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);
+       return bcnt;
+}
 
-       /* Allocate iotag for psb->cur_iocbq. */
-       iotag = lpfc_sli_next_iotag(phba, &psb->cur_iocbq);
-       if (iotag == 0) {
-               pci_pool_free(phba->lpfc_scsi_dma_buf_pool,
-                             psb->data, psb->dma_handle);
-               kfree (psb);
-               return NULL;
+/**
+ * lpfc_sli4_fcp_xri_aborted - Fast-path process of fcp xri abort
+ * @phba: pointer to lpfc hba data structure.
+ * @axri: pointer to the fcp xri abort wcqe structure.
+ *
+ * This routine is invoked by the worker thread to process a SLI4 fast-path
+ * FCP aborted xri.
+ **/
+void
+lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba,
+                         struct sli4_wcqe_xri_aborted *axri)
+{
+       uint16_t xri = bf_get(lpfc_wcqe_xa_xri, axri);
+       struct lpfc_scsi_buf *psb, *next_psb;
+       unsigned long iflag = 0;
+
+       spin_lock_irqsave(&phba->sli4_hba.abts_scsi_buf_list_lock, iflag);
+       list_for_each_entry_safe(psb, next_psb,
+               &phba->sli4_hba.lpfc_abts_scsi_buf_list, list) {
+               if (psb->cur_iocbq.sli4_xritag == xri) {
+                       list_del(&psb->list);
+                       psb->status = IOSTAT_SUCCESS;
+                       spin_unlock_irqrestore(
+                               &phba->sli4_hba.abts_scsi_buf_list_lock,
+                               iflag);
+                       lpfc_release_scsi_buf_s4(phba, psb);
+                       return;
+               }
+       }
+       spin_unlock_irqrestore(&phba->sli4_hba.abts_scsi_buf_list_lock,
+                               iflag);
+}
+
+/**
+ * lpfc_sli4_repost_scsi_sgl_list - Repsot the Scsi buffers sgl pages as block
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine walks the list of scsi buffers that have been allocated and
+ * repost them to the HBA by using SGL block post. This is needed after a
+ * pci_function_reset/warm_start or start. The lpfc_hba_down_post_s4 routine
+ * is responsible for moving all scsi buffers on the lpfc_abts_scsi_sgl_list
+ * to the lpfc_scsi_buf_list. If the repost fails, reject all scsi buffers.
+ *
+ * Returns: 0 = success, non-zero failure.
+ **/
+int
+lpfc_sli4_repost_scsi_sgl_list(struct lpfc_hba *phba)
+{
+       struct lpfc_scsi_buf *psb;
+       int index, status, bcnt = 0, rcnt = 0, rc = 0;
+       LIST_HEAD(sblist);
+
+       for (index = 0; index < phba->sli4_hba.scsi_xri_cnt; index++) {
+               psb = phba->sli4_hba.lpfc_scsi_psb_array[index];
+               if (psb) {
+                       /* Remove from SCSI buffer list */
+                       list_del(&psb->list);
+                       /* Add it to a local SCSI buffer list */
+                       list_add_tail(&psb->list, &sblist);
+                       if (++rcnt == LPFC_NEMBED_MBOX_SGL_CNT) {
+                               bcnt = rcnt;
+                               rcnt = 0;
+                       }
+               } else
+                       /* A hole present in the XRI array, need to skip */
+                       bcnt = rcnt;
+
+               if (index == phba->sli4_hba.scsi_xri_cnt - 1)
+                       /* End of XRI array for SCSI buffer, complete */
+                       bcnt = rcnt;
+
+               /* Continue until collect up to a nembed page worth of sgls */
+               if (bcnt == 0)
+                       continue;
+               /* Now, post the SCSI buffer list sgls as a block */
+               status = lpfc_sli4_post_scsi_sgl_block(phba, &sblist, bcnt);
+               /* Reset SCSI buffer count for next round of posting */
+               bcnt = 0;
+               while (!list_empty(&sblist)) {
+                       list_remove_head(&sblist, psb, struct lpfc_scsi_buf,
+                                        list);
+                       if (status) {
+                               /* Put this back on the abort scsi list */
+                               psb->status = IOSTAT_LOCAL_REJECT;
+                               psb->result = IOERR_ABORT_REQUESTED;
+                               rc++;
+                       } else
+                               psb->status = IOSTAT_SUCCESS;
+                       /* Put it back into the SCSI buffer list */
+                       lpfc_release_scsi_buf_s4(phba, psb);
+               }
        }
-       psb->cur_iocbq.iocb_flag |= LPFC_IO_FCP;
+       return rc;
+}
 
-       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);
+/**
+ * lpfc_new_scsi_buf_s4 - Scsi buffer allocator for HBA with SLI4 IF spec
+ * @vport: The virtual port for which this call being executed.
+ * @num_to_allocate: The requested number of buffers to allocate.
+ *
+ * This routine allocates a scsi buffer for device with SLI-4 interface spec,
+ * the scsi buffer contains all the necessary information needed to initiate
+ * a SCSI I/O.
+ *
+ * Return codes:
+ *   int - number of scsi buffers that were allocated.
+ *   0 = failure, less than num_to_alloc is a partial failure.
+ **/
+static int
+lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc)
+{
+       struct lpfc_hba *phba = vport->phba;
+       struct lpfc_scsi_buf *psb;
+       struct sli4_sge *sgl;
+       IOCB_t *iocb;
+       dma_addr_t pdma_phys_fcp_cmd;
+       dma_addr_t pdma_phys_fcp_rsp;
+       dma_addr_t pdma_phys_bpl, pdma_phys_bpl1;
+       uint16_t iotag, last_xritag = NO_XRI;
+       int status = 0, index;
+       int bcnt;
+       int non_sequential_xri = 0;
+       int rc = 0;
+       LIST_HEAD(sblist);
+
+       for (bcnt = 0; bcnt < num_to_alloc; bcnt++) {
+               psb = kzalloc(sizeof(struct lpfc_scsi_buf), GFP_KERNEL);
+               if (!psb)
+                       break;
 
-       /* Initialize local short-hand pointers. */
-       bpl = psb->fcp_bpl;
-       pdma_phys_fcp_cmd = psb->dma_handle;
-       pdma_phys_fcp_rsp = psb->dma_handle + sizeof(struct fcp_cmnd);
-       pdma_phys_bpl = psb->dma_handle + sizeof(struct fcp_cmnd) +
-                       sizeof(struct fcp_rsp);
+               /*
+                * 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);
+                       break;
+               }
 
-       /*
-        * 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[0].addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys_fcp_cmd));
-       bpl[0].addrLow = le32_to_cpu(putPaddrLow(pdma_phys_fcp_cmd));
-       bpl[0].tus.f.bdeSize = sizeof(struct fcp_cmnd);
-       bpl[0].tus.f.bdeFlags = BUFF_TYPE_BDE_64;
-       bpl[0].tus.w = le32_to_cpu(bpl[0].tus.w);
-
-       /* Setup the physical region for the FCP RSP */
-       bpl[1].addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys_fcp_rsp));
-       bpl[1].addrLow = le32_to_cpu(putPaddrLow(pdma_phys_fcp_rsp));
-       bpl[1].tus.f.bdeSize = sizeof(struct fcp_rsp);
-       bpl[1].tus.f.bdeFlags = BUFF_TYPE_BDE_64;
-       bpl[1].tus.w = le32_to_cpu(bpl[1].tus.w);
+               /* Initialize virtual ptrs to dma_buf region. */
+               memset(psb->data, 0, phba->cfg_sg_dma_buf_size);
 
-       /*
-        * Since the IOCB for the FCP I/O is built into this lpfc_scsi_buf,
-        * initialize it with all known data now.
-        */
-       iocb = &psb->cur_iocbq.iocb;
-       iocb->un.fcpi64.bdl.ulpIoTag32 = 0;
-       if ((phba->sli_rev == 3) &&
-           !(phba->sli3_options & LPFC_SLI3_BG_ENABLED)) {
-               /* fill in immediate fcp command BDE */
-               iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDE_IMMED;
+               /* Allocate iotag for psb->cur_iocbq. */
+               iotag = lpfc_sli_next_iotag(phba, &psb->cur_iocbq);
+               if (iotag == 0) {
+                       kfree(psb);
+                       break;
+               }
+
+               psb->cur_iocbq.sli4_xritag = lpfc_sli4_next_xritag(phba);
+               if (psb->cur_iocbq.sli4_xritag == NO_XRI) {
+                       pci_pool_free(phba->lpfc_scsi_dma_buf_pool,
+                             psb->data, psb->dma_handle);
+                       kfree(psb);
+                       break;
+               }
+               if (last_xritag != NO_XRI
+                       && psb->cur_iocbq.sli4_xritag != (last_xritag+1)) {
+                       non_sequential_xri = 1;
+               } else
+                       list_add_tail(&psb->list, &sblist);
+               last_xritag = psb->cur_iocbq.sli4_xritag;
+
+               index = phba->sli4_hba.scsi_xri_cnt++;
+               psb->cur_iocbq.iocb_flag |= LPFC_IO_FCP;
+
+               psb->fcp_bpl = psb->data;
+               psb->fcp_cmnd = (psb->data + phba->cfg_sg_dma_buf_size)
+                       - (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp));
+               psb->fcp_rsp = (struct fcp_rsp *)((uint8_t *)psb->fcp_cmnd +
+                                       sizeof(struct fcp_cmnd));
+
+               /* Initialize local short-hand pointers. */
+               sgl = (struct sli4_sge *)psb->fcp_bpl;
+               pdma_phys_bpl = psb->dma_handle;
+               pdma_phys_fcp_cmd =
+                       (psb->dma_handle + phba->cfg_sg_dma_buf_size)
+                        - (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp));
+               pdma_phys_fcp_rsp = pdma_phys_fcp_cmd + sizeof(struct fcp_cmnd);
+
+               /*
+                * 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.
+                */
+               sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_cmd));
+               sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_cmd));
+               bf_set(lpfc_sli4_sge_len, sgl, sizeof(struct fcp_cmnd));
+               bf_set(lpfc_sli4_sge_last, sgl, 0);
+               sgl->word2 = cpu_to_le32(sgl->word2);
+               sgl->word3 = cpu_to_le32(sgl->word3);
+               sgl++;
+
+               /* Setup the physical region for the FCP RSP */
+               sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_rsp));
+               sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_rsp));
+               bf_set(lpfc_sli4_sge_len, sgl, sizeof(struct fcp_rsp));
+               bf_set(lpfc_sli4_sge_last, sgl, 1);
+               sgl->word2 = cpu_to_le32(sgl->word2);
+               sgl->word3 = cpu_to_le32(sgl->word3);
+
+               /*
+                * Since the IOCB for the FCP I/O is built into this
+                * lpfc_scsi_buf, initialize it with all known data now.
+                */
+               iocb = &psb->cur_iocbq.iocb;
+               iocb->un.fcpi64.bdl.ulpIoTag32 = 0;
+               iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDE_64;
+               /* setting the BLP size to 2 * sizeof BDE may not be correct.
+                * We are setting the bpl to point to out sgl. An sgl's
+                * entries are 16 bytes, a bpl entries are 12 bytes.
+                */
                iocb->un.fcpi64.bdl.bdeSize = sizeof(struct fcp_cmnd);
-               iocb->un.fcpi64.bdl.addrLow = offsetof(IOCB_t,
-                                                      unsli3.fcp_ext.icd);
-               iocb->un.fcpi64.bdl.addrHigh = 0;
-               iocb->ulpBdeCount = 0;
-               iocb->ulpLe = 0;
-               /* fill in responce BDE */
-               iocb->unsli3.fcp_ext.rbde.tus.f.bdeFlags = BUFF_TYPE_BDE_64;
-               iocb->unsli3.fcp_ext.rbde.tus.f.bdeSize =
-                                               sizeof(struct fcp_rsp);
-               iocb->unsli3.fcp_ext.rbde.addrLow =
-                                               putPaddrLow(pdma_phys_fcp_rsp);
-               iocb->unsli3.fcp_ext.rbde.addrHigh =
-                                               putPaddrHigh(pdma_phys_fcp_rsp);
-       } else {
-               iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BLP_64;
-               iocb->un.fcpi64.bdl.bdeSize = (2 * sizeof(struct ulp_bde64));
-               iocb->un.fcpi64.bdl.addrLow = putPaddrLow(pdma_phys_bpl);
-               iocb->un.fcpi64.bdl.addrHigh = putPaddrHigh(pdma_phys_bpl);
+               iocb->un.fcpi64.bdl.addrLow = putPaddrLow(pdma_phys_fcp_cmd);
+               iocb->un.fcpi64.bdl.addrHigh = putPaddrHigh(pdma_phys_fcp_cmd);
                iocb->ulpBdeCount = 1;
                iocb->ulpLe = 1;
+               iocb->ulpClass = CLASS3;
+               if (phba->cfg_sg_dma_buf_size > SGL_PAGE_SIZE)
+                       pdma_phys_bpl1 = pdma_phys_bpl + SGL_PAGE_SIZE;
+               else
+                       pdma_phys_bpl1 = 0;
+               psb->dma_phys_bpl = pdma_phys_bpl;
+               phba->sli4_hba.lpfc_scsi_psb_array[index] = psb;
+               if (non_sequential_xri) {
+                       status = lpfc_sli4_post_sgl(phba, pdma_phys_bpl,
+                                               pdma_phys_bpl1,
+                                               psb->cur_iocbq.sli4_xritag);
+                       if (status) {
+                               /* Put this back on the abort scsi list */
+                               psb->status = IOSTAT_LOCAL_REJECT;
+                               psb->result = IOERR_ABORT_REQUESTED;
+                               rc++;
+                       } else
+                               psb->status = IOSTAT_SUCCESS;
+                       /* Put it back into the SCSI buffer list */
+                       lpfc_release_scsi_buf_s4(phba, psb);
+                       break;
+               }
+       }
+       if (bcnt) {
+               status = lpfc_sli4_post_scsi_sgl_block(phba, &sblist, bcnt);
+               /* Reset SCSI buffer count for next round of posting */
+               while (!list_empty(&sblist)) {
+                       list_remove_head(&sblist, psb, struct lpfc_scsi_buf,
+                                list);
+                       if (status) {
+                               /* Put this back on the abort scsi list */
+                               psb->status = IOSTAT_LOCAL_REJECT;
+                               psb->result = IOERR_ABORT_REQUESTED;
+                               rc++;
+                       } else
+                               psb->status = IOSTAT_SUCCESS;
+                       /* Put it back into the SCSI buffer list */
+                       lpfc_release_scsi_buf_s4(phba, psb);
+               }
        }
-       iocb->ulpClass = CLASS3;
 
-       return psb;
+       return bcnt + non_sequential_xri - rc;
 }
 
 /**
- * lpfc_get_scsi_buf - Get a scsi buffer from lpfc_scsi_buf_list list of Hba
- * @phba: The Hba for which this call is being executed.
+ * lpfc_new_scsi_buf - Wrapper funciton for scsi buffer allocator
+ * @vport: The virtual port for which this call being executed.
+ * @num_to_allocate: The requested number of buffers to allocate.
+ *
+ * This routine wraps the actual SCSI buffer allocator function pointer from
+ * the lpfc_hba struct.
+ *
+ * Return codes:
+ *   int - number of scsi buffers that were allocated.
+ *   0 = failure, less than num_to_alloc is a partial failure.
+ **/
+static inline int
+lpfc_new_scsi_buf(struct lpfc_vport *vport, int num_to_alloc)
+{
+       return vport->phba->lpfc_new_scsi_buf(vport, num_to_alloc);
+}
+
+/**
+ * lpfc_get_scsi_buf - Get a scsi buffer from lpfc_scsi_buf_list of the HBA
+ * @phba: The HBA for which this call is being executed.
  *
  * This routine removes a scsi buffer from head of @phba lpfc_scsi_buf_list list
  * and returns to caller.
@@ -591,7 +890,7 @@ lpfc_get_scsi_buf(struct lpfc_hba * phba)
 }
 
 /**
- * lpfc_release_scsi_buf - Return a scsi buffer back to hba's lpfc_scsi_buf_list
+ * lpfc_release_scsi_buf - Return a scsi buffer back to hba scsi buf list
  * @phba: The Hba for which this call is being executed.
  * @psb: The scsi buffer which is being released.
  *
@@ -599,7 +898,7 @@ lpfc_get_scsi_buf(struct lpfc_hba * phba)
  * lpfc_scsi_buf_list list.
  **/
 static void
-lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
+lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
 {
        unsigned long iflag = 0;
 
@@ -610,21 +909,69 @@ lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
 }
 
 /**
- * lpfc_scsi_prep_dma_buf - Routine to do DMA mapping for scsi buffer
+ * lpfc_release_scsi_buf_s4: Return a scsi buffer back to hba scsi buf list.
+ * @phba: The Hba for which this call is being executed.
+ * @psb: The scsi buffer which is being released.
+ *
+ * This routine releases @psb scsi buffer by adding it to tail of @phba
+ * lpfc_scsi_buf_list list. For SLI4 XRI's are tied to the scsi buffer
+ * and cannot be reused for at least RA_TOV amount of time if it was
+ * aborted.
+ **/
+static void
+lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
+{
+       unsigned long iflag = 0;
+
+       if (psb->status == IOSTAT_LOCAL_REJECT
+               && psb->result == IOERR_ABORT_REQUESTED) {
+               spin_lock_irqsave(&phba->sli4_hba.abts_scsi_buf_list_lock,
+                                       iflag);
+               psb->pCmd = NULL;
+               list_add_tail(&psb->list,
+                       &phba->sli4_hba.lpfc_abts_scsi_buf_list);
+               spin_unlock_irqrestore(&phba->sli4_hba.abts_scsi_buf_list_lock,
+                                       iflag);
+       } else {
+
+               spin_lock_irqsave(&phba->scsi_buf_list_lock, iflag);
+               psb->pCmd = NULL;
+               list_add_tail(&psb->list, &phba->lpfc_scsi_buf_list);
+               spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag);
+       }
+}
+
+/**
+ * lpfc_release_scsi_buf: Return a scsi buffer back to hba scsi buf list.
+ * @phba: The Hba for which this call is being executed.
+ * @psb: The scsi buffer which is being released.
+ *
+ * This routine releases @psb scsi buffer by adding it to tail of @phba
+ * lpfc_scsi_buf_list list.
+ **/
+static void
+lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
+{
+
+       phba->lpfc_release_scsi_buf(phba, psb);
+}
+
+/**
+ * lpfc_scsi_prep_dma_buf_s3 - DMA mapping for scsi buffer to SLI3 IF spec
  * @phba: The Hba for which this call is being executed.
  * @lpfc_cmd: The scsi buffer which is going to be mapped.
  *
  * This routine does the pci dma mapping for scatter-gather list of scsi cmnd
- * field of @lpfc_cmd. This routine scans through sg elements and format the
- * bdea. This routine also initializes all IOCB fields which are dependent on
- * scsi command request buffer.
+ * field of @lpfc_cmd for device with SLI-3 interface spec. This routine scans
+ * through sg elements and format the bdea. This routine also initializes all
+ * IOCB fields which are dependent on scsi command request buffer.
  *
  * Return codes:
  *   1 - Error
  *   0 - Success
  **/
 static int
-lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
+lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
 {
        struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
        struct scatterlist *sgel = NULL;
@@ -1411,6 +1758,133 @@ out:
        return ret;
 }
 
+/**
+ * lpfc_scsi_prep_dma_buf_s4 - DMA mapping for scsi buffer to SLI4 IF spec
+ * @phba: The Hba for which this call is being executed.
+ * @lpfc_cmd: The scsi buffer which is going to be mapped.
+ *
+ * This routine does the pci dma mapping for scatter-gather list of scsi cmnd
+ * field of @lpfc_cmd for device with SLI-4 interface spec.
+ *
+ * Return codes:
+ *     1 - Error
+ *     0 - Success
+ **/
+static int
+lpfc_scsi_prep_dma_buf_s4(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 sli4_sge *sgl = (struct sli4_sge *)lpfc_cmd->fcp_bpl;
+       IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
+       dma_addr_t physaddr;
+       uint32_t num_bde = 0;
+       uint32_t dma_len;
+       uint32_t dma_offset = 0;
+       int nseg;
+
+       /*
+        * 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_sg_count(scsi_cmnd)) {
+               /*
+                * 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.
+                */
+
+               nseg = scsi_dma_map(scsi_cmnd);
+               if (unlikely(!nseg))
+                       return 1;
+               sgl += 1;
+               /* clear the last flag in the fcp_rsp map entry */
+               sgl->word2 = le32_to_cpu(sgl->word2);
+               bf_set(lpfc_sli4_sge_last, sgl, 0);
+               sgl->word2 = cpu_to_le32(sgl->word2);
+               sgl += 1;
+
+               lpfc_cmd->seg_cnt = nseg;
+               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\n",
+                              __func__, phba->cfg_sg_seg_cnt,
+                              lpfc_cmd->seg_cnt);
+                       scsi_dma_unmap(scsi_cmnd);
+                       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 sge's.
+                * When using SLI-3 the driver will try to fit all the BDEs into
+                * the IOCB. If it can't then the BDEs get added to a BPL as it
+                * does for SLI-2 mode.
+                */
+               scsi_for_each_sg(scsi_cmnd, sgel, nseg, num_bde) {
+                       physaddr = sg_dma_address(sgel);
+                       dma_len = sg_dma_len(sgel);
+                       bf_set(lpfc_sli4_sge_len, sgl, sg_dma_len(sgel));
+                       sgl->addr_lo = cpu_to_le32(putPaddrLow(physaddr));
+                       sgl->addr_hi = cpu_to_le32(putPaddrHigh(physaddr));
+                       if ((num_bde + 1) == nseg)
+                               bf_set(lpfc_sli4_sge_last, sgl, 1);
+                       else
+                               bf_set(lpfc_sli4_sge_last, sgl, 0);
+                       bf_set(lpfc_sli4_sge_offset, sgl, dma_offset);
+                       sgl->word2 = cpu_to_le32(sgl->word2);
+                       sgl->word3 = cpu_to_le32(sgl->word3);
+                       dma_offset += dma_len;
+                       sgl++;
+               }
+       } else {
+               sgl += 1;
+               /* clear the last flag in the fcp_rsp map entry */
+               sgl->word2 = le32_to_cpu(sgl->word2);
+               bf_set(lpfc_sli4_sge_last, sgl, 1);
+               sgl->word2 = cpu_to_le32(sgl->word2);
+       }
+
+       /*
+        * Finish initializing those IOCB fields that are dependent on the
+        * scsi_cmnd request_buffer.  Note that for SLI-2 the bdeSize is
+        * explicitly reinitialized.
+        * all iocb memory resources are reused.
+        */
+       fcp_cmnd->fcpDl = cpu_to_be32(scsi_bufflen(scsi_cmnd));
+
+       /*
+        * Due to difference in data length between DIF/non-DIF paths,
+        * we need to set word 4 of IOCB here
+        */
+       iocb_cmd->un.fcpi.fcpi_parm = scsi_bufflen(scsi_cmnd);
+       return 0;
+}
+
+/**
+ * lpfc_scsi_prep_dma_buf - Wrapper function for DMA mapping of scsi buffer
+ * @phba: The Hba for which this call is being executed.
+ * @lpfc_cmd: The scsi buffer which is going to be mapped.
+ *
+ * This routine wraps the actual DMA mapping function pointer from the
+ * lpfc_hba struct.
+ *
+ * Return codes:
+ *     1 - Error
+ *     0 - Success
+ **/
+static inline int
+lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
+{
+       return phba->lpfc_scsi_prep_dma_buf(phba, lpfc_cmd);
+}
+
 /**
  * lpfc_send_scsi_error_event - Posts an event when there is SCSI error
  * @phba: Pointer to hba context object.
@@ -1504,15 +1978,15 @@ lpfc_send_scsi_error_event(struct lpfc_hba *phba, struct lpfc_vport *vport,
 }
 
 /**
- * lpfc_scsi_unprep_dma_buf - Routine to un-map DMA mapping of scatter gather
- * @phba: The Hba for which this call is being executed.
+ * lpfc_scsi_unprep_dma_buf_s3 - Un-map DMA mapping of SG-list for SLI3 dev
+ * @phba: The HBA for which this call is being executed.
  * @psb: The scsi buffer which is going to be un-mapped.
  *
  * This routine does DMA un-mapping of scatter gather list of scsi command
- * field of @lpfc_cmd.
+ * field of @lpfc_cmd for device with SLI-3 interface spec.
  **/
 static void
-lpfc_scsi_unprep_dma_buf(struct lpfc_hba * phba, struct lpfc_scsi_buf * psb)
+lpfc_scsi_unprep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
 {
        /*
         * There are only two special cases to consider.  (1) the scsi command
@@ -1528,6 +2002,36 @@ lpfc_scsi_unprep_dma_buf(struct lpfc_hba * phba, struct lpfc_scsi_buf * psb)
                                psb->pCmd->sc_data_direction);
 }
 
+/**
+ * lpfc_scsi_unprep_dma_buf_s4 - Un-map DMA mapping of SG-list for SLI4 dev
+ * @phba: The Hba for which this call is being executed.
+ * @psb: The scsi buffer which is going to be un-mapped.
+ *
+ * This routine does DMA un-mapping of scatter gather list of scsi command
+ * field of @lpfc_cmd for device with SLI-4 interface spec. If we have to
+ * remove the sgl for this scsi buffer then we will do it here. For now
+ * we should be able to just call the sli3 unprep routine.
+ **/
+static void
+lpfc_scsi_unprep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
+{
+       lpfc_scsi_unprep_dma_buf_s3(phba, psb);
+}
+
+/**
+ * lpfc_scsi_unprep_dma_buf - Wrapper function for unmap DMA mapping of SG-list
+ * @phba: The Hba for which this call is being executed.
+ * @psb: The scsi buffer which is going to be un-mapped.
+ *
+ * This routine does DMA un-mapping of scatter gather list of scsi command
+ * field of @lpfc_cmd for device with SLI-4 interface spec.
+ **/
+static void
+lpfc_scsi_unprep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
+{
+       phba->lpfc_scsi_unprep_dma_buf(phba, psb);
+}
+
 /**
  * lpfc_handler_fcp_err - FCP response handler
  * @vport: The virtual port for which this call is being executed.
@@ -1676,7 +2180,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
  * lpfc_scsi_cmd_iocb_cmpl - Scsi cmnd IOCB completion routine
  * @phba: The Hba for which this call is being executed.
  * @pIocbIn: The command IOCBQ for the scsi cmnd.
- * @pIocbOut: The response IOCBQ for the scsi cmnd .
+ * @pIocbOut: The response IOCBQ for the scsi cmnd.
  *
  * This routine assigns scsi command result by looking into response IOCB
  * status field appropriately. This routine handles QUEUE FULL condition as
@@ -1957,16 +2461,16 @@ lpfc_fcpcmd_to_iocb(uint8_t *data, struct fcp_cmnd *fcp_cmnd)
 }
 
 /**
- * lpfc_scsi_prep_cmnd -  Routine to convert scsi cmnd to FCP information unit
+ * lpfc_scsi_prep_cmnd_s3 - Convert scsi cmnd to FCP infor unit for SLI3 dev
  * @vport: The virtual port for which this call is being executed.
  * @lpfc_cmd: The scsi command which needs to send.
  * @pnode: Pointer to lpfc_nodelist.
  *
  * This routine initializes fcp_cmnd and iocb data structure from scsi command
- * to transfer.
+ * to transfer for device with SLI3 interface spec.
  **/
 static void
-lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
+lpfc_scsi_prep_cmnd_s3(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
                    struct lpfc_nodelist *pnode)
 {
        struct lpfc_hba *phba = vport->phba;
@@ -2013,8 +2517,11 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
        if (scsi_sg_count(scsi_cmnd)) {
                if (datadir == DMA_TO_DEVICE) {
                        iocb_cmd->ulpCommand = CMD_FCP_IWRITE64_CR;
-                       iocb_cmd->un.fcpi.fcpi_parm = 0;
-                       iocb_cmd->ulpPU = 0;
+                       if (phba->sli_rev < LPFC_SLI_REV4) {
+                               iocb_cmd->un.fcpi.fcpi_parm = 0;
+                               iocb_cmd->ulpPU = 0;
+                       } else
+                               iocb_cmd->ulpPU = PARM_READ_CHECK;
                        fcp_cmnd->fcpCntl3 = WRITE_DATA;
                        phba->fc4OutputRequests++;
                } else {
@@ -2051,20 +2558,60 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
 }
 
 /**
- * lpfc_scsi_prep_task_mgmt_cmnd - Convert scsi TM cmnd to FCP information unit
+ * lpfc_scsi_prep_cmnd_s4 - Convert scsi cmnd to FCP infor unit for SLI4 dev
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: The scsi command which needs to send.
+ * @pnode: Pointer to lpfc_nodelist.
+ *
+ * This routine initializes fcp_cmnd and iocb data structure from scsi command
+ * to transfer for device with SLI4 interface spec.
+ **/
+static void
+lpfc_scsi_prep_cmnd_s4(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
+                      struct lpfc_nodelist *pnode)
+{
+       /*
+        * The prep cmnd routines do not touch the sgl or its
+        * entries. We may not have to do anything different.
+        * I will leave this function in place until we can
+        * run some IO through the driver and determine if changes
+        * are needed.
+        */
+       return lpfc_scsi_prep_cmnd_s3(vport, lpfc_cmd, pnode);
+}
+
+/**
+ * lpfc_scsi_prep_cmnd - Wrapper func for convert scsi cmnd to FCP info unit
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: The scsi command which needs to send.
+ * @pnode: Pointer to lpfc_nodelist.
+ *
+ * This routine wraps the actual convert SCSI cmnd function pointer from
+ * the lpfc_hba struct.
+ **/
+static inline void
+lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
+                   struct lpfc_nodelist *pnode)
+{
+       vport->phba->lpfc_scsi_prep_cmnd(vport, lpfc_cmd, pnode);
+}
+
+/**
+ * lpfc_scsi_prep_task_mgmt_cmnd_s3 - Convert SLI3 scsi TM cmd to FCP info unit
  * @vport: The virtual port for which this call is being executed.
  * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure.
  * @lun: Logical unit number.
  * @task_mgmt_cmd: SCSI task management command.
  *
- * This routine creates FCP information unit corresponding to @task_mgmt_cmd.
+ * This routine creates FCP information unit corresponding to @task_mgmt_cmd
+ * for device with SLI-3 interface spec.
  *
  * Return codes:
  *   0 - Error
  *   1 - Success
  **/
 static int
-lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
+lpfc_scsi_prep_task_mgmt_cmd_s3(struct lpfc_vport *vport,
                             struct lpfc_scsi_buf *lpfc_cmd,
                             unsigned int lun,
                             uint8_t task_mgmt_cmd)
@@ -2113,6 +2660,107 @@ lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
        return 1;
 }
 
+/**
+ * lpfc_scsi_prep_task_mgmt_cmnd_s4 - Convert SLI4 scsi TM cmd to FCP info unit
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure.
+ * @lun: Logical unit number.
+ * @task_mgmt_cmd: SCSI task management command.
+ *
+ * This routine creates FCP information unit corresponding to @task_mgmt_cmd
+ * for device with SLI-4 interface spec.
+ *
+ * Return codes:
+ *     0 - Error
+ *     1 - Success
+ **/
+static int
+lpfc_scsi_prep_task_mgmt_cmd_s4(struct lpfc_vport *vport,
+                               struct lpfc_scsi_buf *lpfc_cmd,
+                               unsigned int lun,
+                               uint8_t task_mgmt_cmd)
+{
+       /*
+        * The prep cmnd routines do not touch the sgl or its
+        * entries. We may not have to do anything different.
+        * I will leave this function in place until we can
+        * run some IO through the driver and determine if changes
+        * are needed.
+        */
+       return lpfc_scsi_prep_task_mgmt_cmd_s3(vport, lpfc_cmd, lun,
+                                               task_mgmt_cmd);
+}
+
+/**
+ * lpfc_scsi_prep_task_mgmt_cmnd - Wrapper func convert scsi TM cmd to FCP info
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure.
+ * @lun: Logical unit number.
+ * @task_mgmt_cmd: SCSI task management command.
+ *
+ * This routine wraps the actual convert SCSI TM to FCP information unit
+ * function pointer from the lpfc_hba struct.
+ *
+ * Return codes:
+ *     0 - Error
+ *     1 - Success
+ **/
+static inline int
+lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
+                            struct lpfc_scsi_buf *lpfc_cmd,
+                            unsigned int lun,
+                            uint8_t task_mgmt_cmd)
+{
+       struct lpfc_hba *phba = vport->phba;
+
+       return phba->lpfc_scsi_prep_task_mgmt_cmd(vport, lpfc_cmd, lun,
+                                                 task_mgmt_cmd);
+}
+
+/**
+ * lpfc_scsi_api_table_setup - Set up scsi api fucntion jump table
+ * @phba: The hba struct for which this call is being executed.
+ * @dev_grp: The HBA PCI-Device group number.
+ *
+ * This routine sets up the SCSI interface API function jump table in @phba
+ * struct.
+ * Returns: 0 - success, -ENODEV - failure.
+ **/
+int
+lpfc_scsi_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
+{
+
+       switch (dev_grp) {
+       case LPFC_PCI_DEV_LP:
+               phba->lpfc_new_scsi_buf = lpfc_new_scsi_buf_s3;
+               phba->lpfc_scsi_prep_dma_buf = lpfc_scsi_prep_dma_buf_s3;
+               phba->lpfc_scsi_prep_cmnd = lpfc_scsi_prep_cmnd_s3;
+               phba->lpfc_scsi_unprep_dma_buf = lpfc_scsi_unprep_dma_buf_s3;
+               phba->lpfc_scsi_prep_task_mgmt_cmd =
+                                       lpfc_scsi_prep_task_mgmt_cmd_s3;
+               phba->lpfc_release_scsi_buf = lpfc_release_scsi_buf_s3;
+               break;
+       case LPFC_PCI_DEV_OC:
+               phba->lpfc_new_scsi_buf = lpfc_new_scsi_buf_s4;
+               phba->lpfc_scsi_prep_dma_buf = lpfc_scsi_prep_dma_buf_s4;
+               phba->lpfc_scsi_prep_cmnd = lpfc_scsi_prep_cmnd_s4;
+               phba->lpfc_scsi_unprep_dma_buf = lpfc_scsi_unprep_dma_buf_s4;
+               phba->lpfc_scsi_prep_task_mgmt_cmd =
+                                       lpfc_scsi_prep_task_mgmt_cmd_s4;
+               phba->lpfc_release_scsi_buf = lpfc_release_scsi_buf_s4;
+               break;
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1418 Invalid HBA PCI-device group: 0x%x\n",
+                               dev_grp);
+               return -ENODEV;
+               break;
+       }
+       phba->lpfc_get_scsi_buf = lpfc_get_scsi_buf;
+       phba->lpfc_rampdown_queue_depth = lpfc_rampdown_queue_depth;
+       return 0;
+}
+
 /**
  * lpfc_taskmgmt_def_cmpl - IOCB completion routine for task management command
  * @phba: The Hba for which this call is being executed.
@@ -2178,9 +2826,8 @@ lpfc_scsi_tgt_reset(struct lpfc_scsi_buf *lpfc_cmd, struct lpfc_vport *vport,
        lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
                         "0702 Issue Target Reset to TGT %d Data: x%x x%x\n",
                         tgt_id, rdata->pnode->nlp_rpi, rdata->pnode->nlp_flag);
-       status = lpfc_sli_issue_iocb_wait(phba,
-                                      &phba->sli.ring[phba->sli.fcp_ring],
-                                      iocbq, iocbqrsp, lpfc_cmd->timeout);
+       status = lpfc_sli_issue_iocb_wait(phba, LPFC_FCP_RING,
+                                         iocbq, iocbqrsp, lpfc_cmd->timeout);
        if (status != IOCB_SUCCESS) {
                if (status == IOCB_TIMEDOUT) {
                        iocbq->iocb_cmpl = lpfc_tskmgmt_def_cmpl;
@@ -2305,7 +2952,6 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
        struct Scsi_Host  *shost = cmnd->device->host;
        struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
        struct lpfc_hba   *phba = vport->phba;
-       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;
@@ -2427,7 +3073,7 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
        lpfc_scsi_prep_cmnd(vport, lpfc_cmd, ndlp);
 
        atomic_inc(&ndlp->cmd_pending);
-       err = lpfc_sli_issue_iocb(phba, &phba->sli.ring[psli->fcp_ring],
+       err = lpfc_sli_issue_iocb(phba, LPFC_FCP_RING,
                                  &lpfc_cmd->cur_iocbq, SLI_IOCB_RET_IOCB);
        if (err) {
                atomic_dec(&ndlp->cmd_pending);
@@ -2490,7 +3136,6 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
        struct Scsi_Host  *shost = cmnd->device->host;
        struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
        struct lpfc_hba   *phba = vport->phba;
-       struct lpfc_sli_ring *pring = &phba->sli.ring[phba->sli.fcp_ring];
        struct lpfc_iocbq *iocb;
        struct lpfc_iocbq *abtsiocb;
        struct lpfc_scsi_buf *lpfc_cmd;
@@ -2531,7 +3176,10 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
        icmd = &abtsiocb->iocb;
        icmd->un.acxri.abortType = ABORT_TYPE_ABTS;
        icmd->un.acxri.abortContextTag = cmd->ulpContext;
-       icmd->un.acxri.abortIoTag = cmd->ulpIoTag;
+       if (phba->sli_rev == LPFC_SLI_REV4)
+               icmd->un.acxri.abortIoTag = iocb->sli4_xritag;
+       else
+               icmd->un.acxri.abortIoTag = cmd->ulpIoTag;
 
        icmd->ulpLe = 1;
        icmd->ulpClass = cmd->ulpClass;
@@ -2542,7 +3190,8 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
 
        abtsiocb->iocb_cmpl = lpfc_sli_abort_fcp_cmpl;
        abtsiocb->vport = vport;
-       if (lpfc_sli_issue_iocb(phba, pring, abtsiocb, 0) == IOCB_ERROR) {
+       if (lpfc_sli_issue_iocb(phba, LPFC_FCP_RING, abtsiocb, 0) ==
+           IOCB_ERROR) {
                lpfc_sli_release_iocbq(phba, abtsiocb);
                ret = FAILED;
                goto out;
@@ -2668,8 +3317,7 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
                         "0703 Issue target reset to TGT %d LUN %d "
                         "rpi x%x nlp_flag x%x\n", cmnd->device->id,
                         cmnd->device->lun, pnode->nlp_rpi, pnode->nlp_flag);
-       status = lpfc_sli_issue_iocb_wait(phba,
-                                         &phba->sli.ring[phba->sli.fcp_ring],
+       status = lpfc_sli_issue_iocb_wait(phba, LPFC_FCP_RING,
                                          iocbq, iocbqrsp, lpfc_cmd->timeout);
        if (status == IOCB_TIMEDOUT) {
                iocbq->iocb_cmpl = lpfc_tskmgmt_def_cmpl;
@@ -2825,11 +3473,10 @@ lpfc_slave_alloc(struct scsi_device *sdev)
 {
        struct lpfc_vport *vport = (struct lpfc_vport *) sdev->host->hostdata;
        struct lpfc_hba   *phba = vport->phba;
-       struct lpfc_scsi_buf *scsi_buf = NULL;
        struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
-       uint32_t total = 0, i;
+       uint32_t total = 0;
        uint32_t num_to_alloc = 0;
-       unsigned long flags;
+       int num_allocated = 0;
 
        if (!rport || fc_remote_port_chkready(rport))
                return -ENXIO;
@@ -2863,20 +3510,13 @@ lpfc_slave_alloc(struct scsi_device *sdev)
                                 (phba->cfg_hba_queue_depth - total));
                num_to_alloc = phba->cfg_hba_queue_depth - total;
        }
-
-       for (i = 0; i < num_to_alloc; i++) {
-               scsi_buf = lpfc_new_scsi_buf(vport);
-               if (!scsi_buf) {
-                       lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
-                                        "0706 Failed to allocate "
-                                        "command buffer\n");
-                       break;
-               }
-
-               spin_lock_irqsave(&phba->scsi_buf_list_lock, flags);
-               phba->total_scsi_bufs++;
-               list_add_tail(&scsi_buf->list, &phba->lpfc_scsi_buf_list);
-               spin_unlock_irqrestore(&phba->scsi_buf_list_lock, flags);
+       num_allocated = lpfc_new_scsi_buf(vport, num_to_alloc);
+       if (num_to_alloc != num_allocated) {
+                       lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
+                                "0708 Allocation request of %d "
+                                "command buffers did not succeed.  "
+                                "Allocated %d buffers.\n",
+                                num_to_alloc, num_allocated);
        }
        return 0;
 }
index c7c440d5fa29668010bc53df87e0c2901328bdb7..65dfc8bd5b49ae385e6d15c51909c245aed71da0 100644 (file)
@@ -140,6 +140,8 @@ struct lpfc_scsi_buf {
        struct fcp_rsp *fcp_rsp;
        struct ulp_bde64 *fcp_bpl;
 
+       dma_addr_t dma_phys_bpl;
+
        /* cur_iocbq has phys of the dma-able buffer.
         * Iotag is in here
         */
index eb5c75c45ba4aa1e4414fb880101e77edd60a511..ff04daf18f48d1ba4df538638b746c2d87a1236d 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2008 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2009 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport_fc.h>
+#include <scsi/fc/fc_fs.h>
 
+#include "lpfc_hw4.h"
 #include "lpfc_hw.h"
 #include "lpfc_sli.h"
+#include "lpfc_sli4.h"
 #include "lpfc_nl.h"
 #include "lpfc_disc.h"
 #include "lpfc_scsi.h"
 #include "lpfc_logmsg.h"
 #include "lpfc_compat.h"
 #include "lpfc_debugfs.h"
-
-/*
- * Define macro to log: Mailbox command x%x cannot issue Data
- * This allows multiple uses of lpfc_msgBlk0311
- * w/o perturbing log msg utility.
- */
-#define LOG_MBOX_CANNOT_ISSUE_DATA(phba, pmbox, psli, flag) \
-                       lpfc_printf_log(phba, \
-                               KERN_INFO, \
-                               LOG_MBOX | LOG_SLI, \
-                               "(%d):0311 Mailbox command x%x cannot " \
-                               "issue Data: x%x x%x x%x\n", \
-                               pmbox->vport ? pmbox->vport->vpi : 0, \
-                               pmbox->mb.mbxCommand,           \
-                               phba->pport->port_state,        \
-                               psli->sli_flag, \
-                               flag)
-
+#include "lpfc_vport.h"
 
 /* There are only four IOCB completion types. */
 typedef enum _lpfc_iocb_type {
@@ -67,6 +53,350 @@ typedef enum _lpfc_iocb_type {
        LPFC_ABORT_IOCB
 } lpfc_iocb_type;
 
+
+/* Provide function prototypes local to this module. */
+static int lpfc_sli_issue_mbox_s4(struct lpfc_hba *, LPFC_MBOXQ_t *,
+                                 uint32_t);
+static int lpfc_sli4_read_rev(struct lpfc_hba *, LPFC_MBOXQ_t *,
+                           uint8_t *, uint32_t *);
+
+static IOCB_t *
+lpfc_get_iocb_from_iocbq(struct lpfc_iocbq *iocbq)
+{
+       return &iocbq->iocb;
+}
+
+/**
+ * lpfc_sli4_wq_put - Put a Work Queue Entry on an Work Queue
+ * @q: The Work Queue to operate on.
+ * @wqe: The work Queue Entry to put on the Work queue.
+ *
+ * This routine will copy the contents of @wqe to the next available entry on
+ * the @q. This function will then ring the Work Queue Doorbell to signal the
+ * HBA to start processing the Work Queue Entry. This function returns 0 if
+ * successful. If no entries are available on @q then this function will return
+ * -ENOMEM.
+ * The caller is expected to hold the hbalock when calling this routine.
+ **/
+static uint32_t
+lpfc_sli4_wq_put(struct lpfc_queue *q, union lpfc_wqe *wqe)
+{
+       union lpfc_wqe *temp_wqe = q->qe[q->host_index].wqe;
+       struct lpfc_register doorbell;
+       uint32_t host_index;
+
+       /* If the host has not yet processed the next entry then we are done */
+       if (((q->host_index + 1) % q->entry_count) == q->hba_index)
+               return -ENOMEM;
+       /* set consumption flag every once in a while */
+       if (!((q->host_index + 1) % LPFC_RELEASE_NOTIFICATION_INTERVAL))
+               bf_set(lpfc_wqe_gen_wqec, &wqe->generic, 1);
+
+       lpfc_sli_pcimem_bcopy(wqe, temp_wqe, q->entry_size);
+
+       /* Update the host index before invoking device */
+       host_index = q->host_index;
+       q->host_index = ((q->host_index + 1) % q->entry_count);
+
+       /* Ring Doorbell */
+       doorbell.word0 = 0;
+       bf_set(lpfc_wq_doorbell_num_posted, &doorbell, 1);
+       bf_set(lpfc_wq_doorbell_index, &doorbell, host_index);
+       bf_set(lpfc_wq_doorbell_id, &doorbell, q->queue_id);
+       writel(doorbell.word0, q->phba->sli4_hba.WQDBregaddr);
+       readl(q->phba->sli4_hba.WQDBregaddr); /* Flush */
+
+       return 0;
+}
+
+/**
+ * lpfc_sli4_wq_release - Updates internal hba index for WQ
+ * @q: The Work Queue to operate on.
+ * @index: The index to advance the hba index to.
+ *
+ * This routine will update the HBA index of a queue to reflect consumption of
+ * Work Queue Entries by the HBA. When the HBA indicates that it has consumed
+ * an entry the host calls this function to update the queue's internal
+ * pointers. This routine returns the number of entries that were consumed by
+ * the HBA.
+ **/
+static uint32_t
+lpfc_sli4_wq_release(struct lpfc_queue *q, uint32_t index)
+{
+       uint32_t released = 0;
+
+       if (q->hba_index == index)
+               return 0;
+       do {
+               q->hba_index = ((q->hba_index + 1) % q->entry_count);
+               released++;
+       } while (q->hba_index != index);
+       return released;
+}
+
+/**
+ * lpfc_sli4_mq_put - Put a Mailbox Queue Entry on an Mailbox Queue
+ * @q: The Mailbox Queue to operate on.
+ * @wqe: The Mailbox Queue Entry to put on the Work queue.
+ *
+ * This routine will copy the contents of @mqe to the next available entry on
+ * the @q. This function will then ring the Work Queue Doorbell to signal the
+ * HBA to start processing the Work Queue Entry. This function returns 0 if
+ * successful. If no entries are available on @q then this function will return
+ * -ENOMEM.
+ * The caller is expected to hold the hbalock when calling this routine.
+ **/
+static uint32_t
+lpfc_sli4_mq_put(struct lpfc_queue *q, struct lpfc_mqe *mqe)
+{
+       struct lpfc_mqe *temp_mqe = q->qe[q->host_index].mqe;
+       struct lpfc_register doorbell;
+       uint32_t host_index;
+
+       /* If the host has not yet processed the next entry then we are done */
+       if (((q->host_index + 1) % q->entry_count) == q->hba_index)
+               return -ENOMEM;
+       lpfc_sli_pcimem_bcopy(mqe, temp_mqe, q->entry_size);
+       /* Save off the mailbox pointer for completion */
+       q->phba->mbox = (MAILBOX_t *)temp_mqe;
+
+       /* Update the host index before invoking device */
+       host_index = q->host_index;
+       q->host_index = ((q->host_index + 1) % q->entry_count);
+
+       /* Ring Doorbell */
+       doorbell.word0 = 0;
+       bf_set(lpfc_mq_doorbell_num_posted, &doorbell, 1);
+       bf_set(lpfc_mq_doorbell_id, &doorbell, q->queue_id);
+       writel(doorbell.word0, q->phba->sli4_hba.MQDBregaddr);
+       readl(q->phba->sli4_hba.MQDBregaddr); /* Flush */
+       return 0;
+}
+
+/**
+ * lpfc_sli4_mq_release - Updates internal hba index for MQ
+ * @q: The Mailbox Queue to operate on.
+ *
+ * This routine will update the HBA index of a queue to reflect consumption of
+ * a Mailbox Queue Entry by the HBA. When the HBA indicates that it has consumed
+ * an entry the host calls this function to update the queue's internal
+ * pointers. This routine returns the number of entries that were consumed by
+ * the HBA.
+ **/
+static uint32_t
+lpfc_sli4_mq_release(struct lpfc_queue *q)
+{
+       /* Clear the mailbox pointer for completion */
+       q->phba->mbox = NULL;
+       q->hba_index = ((q->hba_index + 1) % q->entry_count);
+       return 1;
+}
+
+/**
+ * lpfc_sli4_eq_get - Gets the next valid EQE from a EQ
+ * @q: The Event Queue to get the first valid EQE from
+ *
+ * This routine will get the first valid Event Queue Entry from @q, update
+ * the queue's internal hba index, and return the EQE. If no valid EQEs are in
+ * the Queue (no more work to do), or the Queue is full of EQEs that have been
+ * processed, but not popped back to the HBA then this routine will return NULL.
+ **/
+static struct lpfc_eqe *
+lpfc_sli4_eq_get(struct lpfc_queue *q)
+{
+       struct lpfc_eqe *eqe = q->qe[q->hba_index].eqe;
+
+       /* If the next EQE is not valid then we are done */
+       if (!bf_get(lpfc_eqe_valid, eqe))
+               return NULL;
+       /* If the host has not yet processed the next entry then we are done */
+       if (((q->hba_index + 1) % q->entry_count) == q->host_index)
+               return NULL;
+
+       q->hba_index = ((q->hba_index + 1) % q->entry_count);
+       return eqe;
+}
+
+/**
+ * lpfc_sli4_eq_release - Indicates the host has finished processing an EQ
+ * @q: The Event Queue that the host has completed processing for.
+ * @arm: Indicates whether the host wants to arms this CQ.
+ *
+ * This routine will mark all Event Queue Entries on @q, from the last
+ * known completed entry to the last entry that was processed, as completed
+ * by clearing the valid bit for each completion queue entry. Then it will
+ * notify the HBA, by ringing the doorbell, that the EQEs have been processed.
+ * The internal host index in the @q will be updated by this routine to indicate
+ * that the host has finished processing the entries. The @arm parameter
+ * indicates that the queue should be rearmed when ringing the doorbell.
+ *
+ * This function will return the number of EQEs that were popped.
+ **/
+uint32_t
+lpfc_sli4_eq_release(struct lpfc_queue *q, bool arm)
+{
+       uint32_t released = 0;
+       struct lpfc_eqe *temp_eqe;
+       struct lpfc_register doorbell;
+
+       /* while there are valid entries */
+       while (q->hba_index != q->host_index) {
+               temp_eqe = q->qe[q->host_index].eqe;
+               bf_set(lpfc_eqe_valid, temp_eqe, 0);
+               released++;
+               q->host_index = ((q->host_index + 1) % q->entry_count);
+       }
+       if (unlikely(released == 0 && !arm))
+               return 0;
+
+       /* ring doorbell for number popped */
+       doorbell.word0 = 0;
+       if (arm) {
+               bf_set(lpfc_eqcq_doorbell_arm, &doorbell, 1);
+               bf_set(lpfc_eqcq_doorbell_eqci, &doorbell, 1);
+       }
+       bf_set(lpfc_eqcq_doorbell_num_released, &doorbell, released);
+       bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_EVENT);
+       bf_set(lpfc_eqcq_doorbell_eqid, &doorbell, q->queue_id);
+       writel(doorbell.word0, q->phba->sli4_hba.EQCQDBregaddr);
+       return released;
+}
+
+/**
+ * lpfc_sli4_cq_get - Gets the next valid CQE from a CQ
+ * @q: The Completion Queue to get the first valid CQE from
+ *
+ * This routine will get the first valid Completion Queue Entry from @q, update
+ * the queue's internal hba index, and return the CQE. If no valid CQEs are in
+ * the Queue (no more work to do), or the Queue is full of CQEs that have been
+ * processed, but not popped back to the HBA then this routine will return NULL.
+ **/
+static struct lpfc_cqe *
+lpfc_sli4_cq_get(struct lpfc_queue *q)
+{
+       struct lpfc_cqe *cqe;
+
+       /* If the next CQE is not valid then we are done */
+       if (!bf_get(lpfc_cqe_valid, q->qe[q->hba_index].cqe))
+               return NULL;
+       /* If the host has not yet processed the next entry then we are done */
+       if (((q->hba_index + 1) % q->entry_count) == q->host_index)
+               return NULL;
+
+       cqe = q->qe[q->hba_index].cqe;
+       q->hba_index = ((q->hba_index + 1) % q->entry_count);
+       return cqe;
+}
+
+/**
+ * lpfc_sli4_cq_release - Indicates the host has finished processing a CQ
+ * @q: The Completion Queue that the host has completed processing for.
+ * @arm: Indicates whether the host wants to arms this CQ.
+ *
+ * This routine will mark all Completion queue entries on @q, from the last
+ * known completed entry to the last entry that was processed, as completed
+ * by clearing the valid bit for each completion queue entry. Then it will
+ * notify the HBA, by ringing the doorbell, that the CQEs have been processed.
+ * The internal host index in the @q will be updated by this routine to indicate
+ * that the host has finished processing the entries. The @arm parameter
+ * indicates that the queue should be rearmed when ringing the doorbell.
+ *
+ * This function will return the number of CQEs that were released.
+ **/
+uint32_t
+lpfc_sli4_cq_release(struct lpfc_queue *q, bool arm)
+{
+       uint32_t released = 0;
+       struct lpfc_cqe *temp_qe;
+       struct lpfc_register doorbell;
+
+       /* while there are valid entries */
+       while (q->hba_index != q->host_index) {
+               temp_qe = q->qe[q->host_index].cqe;
+               bf_set(lpfc_cqe_valid, temp_qe, 0);
+               released++;
+               q->host_index = ((q->host_index + 1) % q->entry_count);
+       }
+       if (unlikely(released == 0 && !arm))
+               return 0;
+
+       /* ring doorbell for number popped */
+       doorbell.word0 = 0;
+       if (arm)
+               bf_set(lpfc_eqcq_doorbell_arm, &doorbell, 1);
+       bf_set(lpfc_eqcq_doorbell_num_released, &doorbell, released);
+       bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_COMPLETION);
+       bf_set(lpfc_eqcq_doorbell_cqid, &doorbell, q->queue_id);
+       writel(doorbell.word0, q->phba->sli4_hba.EQCQDBregaddr);
+       return released;
+}
+
+/**
+ * lpfc_sli4_rq_put - Put a Receive Buffer Queue Entry on a Receive Queue
+ * @q: The Header Receive Queue to operate on.
+ * @wqe: The Receive Queue Entry to put on the Receive queue.
+ *
+ * This routine will copy the contents of @wqe to the next available entry on
+ * the @q. This function will then ring the Receive Queue Doorbell to signal the
+ * HBA to start processing the Receive Queue Entry. This function returns the
+ * index that the rqe was copied to if successful. If no entries are available
+ * on @q then this function will return -ENOMEM.
+ * The caller is expected to hold the hbalock when calling this routine.
+ **/
+static int
+lpfc_sli4_rq_put(struct lpfc_queue *hq, struct lpfc_queue *dq,
+                struct lpfc_rqe *hrqe, struct lpfc_rqe *drqe)
+{
+       struct lpfc_rqe *temp_hrqe = hq->qe[hq->host_index].rqe;
+       struct lpfc_rqe *temp_drqe = dq->qe[dq->host_index].rqe;
+       struct lpfc_register doorbell;
+       int put_index = hq->host_index;
+
+       if (hq->type != LPFC_HRQ || dq->type != LPFC_DRQ)
+               return -EINVAL;
+       if (hq->host_index != dq->host_index)
+               return -EINVAL;
+       /* If the host has not yet processed the next entry then we are done */
+       if (((hq->host_index + 1) % hq->entry_count) == hq->hba_index)
+               return -EBUSY;
+       lpfc_sli_pcimem_bcopy(hrqe, temp_hrqe, hq->entry_size);
+       lpfc_sli_pcimem_bcopy(drqe, temp_drqe, dq->entry_size);
+
+       /* Update the host index to point to the next slot */
+       hq->host_index = ((hq->host_index + 1) % hq->entry_count);
+       dq->host_index = ((dq->host_index + 1) % dq->entry_count);
+
+       /* Ring The Header Receive Queue Doorbell */
+       if (!(hq->host_index % LPFC_RQ_POST_BATCH)) {
+               doorbell.word0 = 0;
+               bf_set(lpfc_rq_doorbell_num_posted, &doorbell,
+                      LPFC_RQ_POST_BATCH);
+               bf_set(lpfc_rq_doorbell_id, &doorbell, hq->queue_id);
+               writel(doorbell.word0, hq->phba->sli4_hba.RQDBregaddr);
+       }
+       return put_index;
+}
+
+/**
+ * lpfc_sli4_rq_release - Updates internal hba index for RQ
+ * @q: The Header Receive Queue to operate on.
+ *
+ * This routine will update the HBA index of a queue to reflect consumption of
+ * one Receive Queue Entry by the HBA. When the HBA indicates that it has
+ * consumed an entry the host calls this function to update the queue's
+ * internal pointers. This routine returns the number of entries that were
+ * consumed by the HBA.
+ **/
+static uint32_t
+lpfc_sli4_rq_release(struct lpfc_queue *hq, struct lpfc_queue *dq)
+{
+       if ((hq->type != LPFC_HRQ) || (dq->type != LPFC_DRQ))
+               return 0;
+       hq->hba_index = ((hq->hba_index + 1) % hq->entry_count);
+       dq->hba_index = ((dq->hba_index + 1) % dq->entry_count);
+       return 1;
+}
+
 /**
  * lpfc_cmd_iocb - Get next command iocb entry in the ring
  * @phba: Pointer to HBA context object.
@@ -120,6 +450,76 @@ __lpfc_sli_get_iocbq(struct lpfc_hba *phba)
        return iocbq;
 }
 
+/**
+ * __lpfc_clear_active_sglq - Remove the active sglq for this XRI.
+ * @phba: Pointer to HBA context object.
+ * @xritag: XRI value.
+ *
+ * This function clears the sglq pointer from the array of acive
+ * sglq's. The xritag that is passed in is used to index into the
+ * array. Before the xritag can be used it needs to be adjusted
+ * by subtracting the xribase.
+ *
+ * Returns sglq ponter = success, NULL = Failure.
+ **/
+static struct lpfc_sglq *
+__lpfc_clear_active_sglq(struct lpfc_hba *phba, uint16_t xritag)
+{
+       uint16_t adj_xri;
+       struct lpfc_sglq *sglq;
+       adj_xri = xritag - phba->sli4_hba.max_cfg_param.xri_base;
+       if (adj_xri > phba->sli4_hba.max_cfg_param.max_xri)
+               return NULL;
+       sglq = phba->sli4_hba.lpfc_sglq_active_list[adj_xri];
+       phba->sli4_hba.lpfc_sglq_active_list[adj_xri] = NULL;
+       return sglq;
+}
+
+/**
+ * __lpfc_get_active_sglq - Get the active sglq for this XRI.
+ * @phba: Pointer to HBA context object.
+ * @xritag: XRI value.
+ *
+ * This function returns the sglq pointer from the array of acive
+ * sglq's. The xritag that is passed in is used to index into the
+ * array. Before the xritag can be used it needs to be adjusted
+ * by subtracting the xribase.
+ *
+ * Returns sglq ponter = success, NULL = Failure.
+ **/
+static struct lpfc_sglq *
+__lpfc_get_active_sglq(struct lpfc_hba *phba, uint16_t xritag)
+{
+       uint16_t adj_xri;
+       struct lpfc_sglq *sglq;
+       adj_xri = xritag - phba->sli4_hba.max_cfg_param.xri_base;
+       if (adj_xri > phba->sli4_hba.max_cfg_param.max_xri)
+               return NULL;
+       sglq =  phba->sli4_hba.lpfc_sglq_active_list[adj_xri];
+       return sglq;
+}
+
+/**
+ * __lpfc_sli_get_sglq - Allocates an iocb object from sgl pool
+ * @phba: Pointer to HBA context object.
+ *
+ * This function is called with hbalock held. This function
+ * Gets a new driver sglq object from the sglq list. If the
+ * list is not empty then it is successful, it returns pointer to the newly
+ * allocated sglq object else it returns NULL.
+ **/
+static struct lpfc_sglq *
+__lpfc_sli_get_sglq(struct lpfc_hba *phba)
+{
+       struct list_head *lpfc_sgl_list = &phba->sli4_hba.lpfc_sgl_list;
+       struct lpfc_sglq *sglq = NULL;
+       uint16_t adj_xri;
+       list_remove_head(lpfc_sgl_list, sglq, struct lpfc_sglq, list);
+       adj_xri = sglq->sli4_xritag - phba->sli4_hba.max_cfg_param.xri_base;
+       phba->sli4_hba.lpfc_sglq_active_list[adj_xri] = sglq;
+       return sglq;
+}
+
 /**
  * lpfc_sli_get_iocbq - Allocates an iocb object from iocb pool
  * @phba: Pointer to HBA context object.
@@ -142,7 +542,7 @@ lpfc_sli_get_iocbq(struct lpfc_hba *phba)
 }
 
 /**
- * __lpfc_sli_release_iocbq - Release iocb to the iocb pool
+ * __lpfc_sli_release_iocbq_s4 - Release iocb to the iocb pool
  * @phba: Pointer to HBA context object.
  * @iocbq: Pointer to driver iocb object.
  *
@@ -150,9 +550,62 @@ lpfc_sli_get_iocbq(struct lpfc_hba *phba)
  * iocb object to the iocb pool. The iotag in the iocb object
  * does not change for each use of the iocb object. This function
  * clears all other fields of the iocb object when it is freed.
+ * The sqlq structure that holds the xritag and phys and virtual
+ * mappings for the scatter gather list is retrieved from the
+ * active array of sglq. The get of the sglq pointer also clears
+ * the entry in the array. If the status of the IO indiactes that
+ * this IO was aborted then the sglq entry it put on the
+ * lpfc_abts_els_sgl_list until the CQ_ABORTED_XRI is received. If the
+ * IO has good status or fails for any other reason then the sglq
+ * entry is added to the free list (lpfc_sgl_list).
  **/
 static void
-__lpfc_sli_release_iocbq(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
+__lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
+{
+       struct lpfc_sglq *sglq;
+       size_t start_clean = offsetof(struct lpfc_iocbq, iocb);
+       unsigned long iflag;
+
+       if (iocbq->sli4_xritag == NO_XRI)
+               sglq = NULL;
+       else
+               sglq = __lpfc_clear_active_sglq(phba, iocbq->sli4_xritag);
+       if (sglq)  {
+               if (iocbq->iocb_flag & LPFC_DRIVER_ABORTED
+                       || ((iocbq->iocb.ulpStatus == IOSTAT_LOCAL_REJECT)
+                       && (iocbq->iocb.un.ulpWord[4]
+                               == IOERR_SLI_ABORTED))) {
+                       spin_lock_irqsave(&phba->sli4_hba.abts_sgl_list_lock,
+                                       iflag);
+                       list_add(&sglq->list,
+                               &phba->sli4_hba.lpfc_abts_els_sgl_list);
+                       spin_unlock_irqrestore(
+                               &phba->sli4_hba.abts_sgl_list_lock, iflag);
+               } else
+                       list_add(&sglq->list, &phba->sli4_hba.lpfc_sgl_list);
+       }
+
+
+       /*
+        * Clean all volatile data fields, preserve iotag and node struct.
+        */
+       memset((char *)iocbq + start_clean, 0, sizeof(*iocbq) - start_clean);
+       iocbq->sli4_xritag = NO_XRI;
+       list_add_tail(&iocbq->list, &phba->lpfc_iocb_list);
+}
+
+/**
+ * __lpfc_sli_release_iocbq_s3 - Release iocb to the iocb pool
+ * @phba: Pointer to HBA context object.
+ * @iocbq: Pointer to driver iocb object.
+ *
+ * This function is called with hbalock held to release driver
+ * iocb object to the iocb pool. The iotag in the iocb object
+ * does not change for each use of the iocb object. This function
+ * clears all other fields of the iocb object when it is freed.
+ **/
+static void
+__lpfc_sli_release_iocbq_s3(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
 {
        size_t start_clean = offsetof(struct lpfc_iocbq, iocb);
 
@@ -160,9 +613,26 @@ __lpfc_sli_release_iocbq(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
         * Clean all volatile data fields, preserve iotag and node struct.
         */
        memset((char*)iocbq + start_clean, 0, sizeof(*iocbq) - start_clean);
+       iocbq->sli4_xritag = NO_XRI;
        list_add_tail(&iocbq->list, &phba->lpfc_iocb_list);
 }
 
+/**
+ * __lpfc_sli_release_iocbq - Release iocb to the iocb pool
+ * @phba: Pointer to HBA context object.
+ * @iocbq: Pointer to driver iocb object.
+ *
+ * This function is called with hbalock held to release driver
+ * iocb object to the iocb pool. The iotag in the iocb object
+ * does not change for each use of the iocb object. This function
+ * clears all other fields of the iocb object when it is freed.
+ **/
+static void
+__lpfc_sli_release_iocbq(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
+{
+       phba->__lpfc_sli_release_iocbq(phba, iocbq);
+}
+
 /**
  * lpfc_sli_release_iocbq - Release iocb to the iocb pool
  * @phba: Pointer to HBA context object.
@@ -281,6 +751,14 @@ lpfc_sli_iocb_cmd_type(uint8_t iocb_cmnd)
        case CMD_GEN_REQUEST64_CR:
        case CMD_GEN_REQUEST64_CX:
        case CMD_XMIT_ELS_RSP64_CX:
+       case DSSCMD_IWRITE64_CR:
+       case DSSCMD_IWRITE64_CX:
+       case DSSCMD_IREAD64_CR:
+       case DSSCMD_IREAD64_CX:
+       case DSSCMD_INVALIDATE_DEK:
+       case DSSCMD_SET_KEK:
+       case DSSCMD_GET_KEK_ID:
+       case DSSCMD_GEN_XFER:
                type = LPFC_SOL_IOCB;
                break;
        case CMD_ABORT_XRI_CN:
@@ -348,7 +826,7 @@ lpfc_sli_ring_map(struct lpfc_hba *phba)
        pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
        if (!pmb)
                return -ENOMEM;
-       pmbox = &pmb->mb;
+       pmbox = &pmb->u.mb;
        phba->link_state = LPFC_INIT_MBX_CMDS;
        for (i = 0; i < psli->num_rings; i++) {
                lpfc_config_ring(phba, i, pmb);
@@ -779,8 +1257,8 @@ lpfc_sli_hbqbuf_free_all(struct lpfc_hba *phba)
                phba->hbqs[i].buffer_count = 0;
        }
        /* Return all HBQ buffer that are in-fly */
-       list_for_each_entry_safe(dmabuf, next_dmabuf,
-                       &phba->hbqbuf_in_list, list) {
+       list_for_each_entry_safe(dmabuf, next_dmabuf, &phba->rb_pend_list,
+                                list) {
                hbq_buf = container_of(dmabuf, struct hbq_dmabuf, dbuf);
                list_del(&hbq_buf->dbuf.list);
                if (hbq_buf->tag == -1) {
@@ -814,9 +1292,27 @@ lpfc_sli_hbqbuf_free_all(struct lpfc_hba *phba)
  * pointer to the hbq entry if it successfully post the buffer
  * else it will return NULL.
  **/
-static struct lpfc_hbq_entry *
+static int
 lpfc_sli_hbq_to_firmware(struct lpfc_hba *phba, uint32_t hbqno,
                         struct hbq_dmabuf *hbq_buf)
+{
+       return phba->lpfc_sli_hbq_to_firmware(phba, hbqno, hbq_buf);
+}
+
+/**
+ * lpfc_sli_hbq_to_firmware_s3 - Post the hbq buffer to SLI3 firmware
+ * @phba: Pointer to HBA context object.
+ * @hbqno: HBQ number.
+ * @hbq_buf: Pointer to HBQ buffer.
+ *
+ * This function is called with the hbalock held to post a hbq buffer to the
+ * firmware. If the function finds an empty slot in the HBQ, it will post the
+ * buffer and place it on the hbq_buffer_list. The function will return zero if
+ * it successfully post the buffer else it will return an error.
+ **/
+static int
+lpfc_sli_hbq_to_firmware_s3(struct lpfc_hba *phba, uint32_t hbqno,
+                           struct hbq_dmabuf *hbq_buf)
 {
        struct lpfc_hbq_entry *hbqe;
        dma_addr_t physaddr = hbq_buf->dbuf.phys;
@@ -838,8 +1334,40 @@ lpfc_sli_hbq_to_firmware(struct lpfc_hba *phba, uint32_t hbqno,
                                /* flush */
                readl(phba->hbq_put + hbqno);
                list_add_tail(&hbq_buf->dbuf.list, &hbqp->hbq_buffer_list);
-       }
-       return hbqe;
+               return 0;
+       } else
+               return -ENOMEM;
+}
+
+/**
+ * lpfc_sli_hbq_to_firmware_s4 - Post the hbq buffer to SLI4 firmware
+ * @phba: Pointer to HBA context object.
+ * @hbqno: HBQ number.
+ * @hbq_buf: Pointer to HBQ buffer.
+ *
+ * This function is called with the hbalock held to post an RQE to the SLI4
+ * firmware. If able to post the RQE to the RQ it will queue the hbq entry to
+ * the hbq_buffer_list and return zero, otherwise it will return an error.
+ **/
+static int
+lpfc_sli_hbq_to_firmware_s4(struct lpfc_hba *phba, uint32_t hbqno,
+                           struct hbq_dmabuf *hbq_buf)
+{
+       int rc;
+       struct lpfc_rqe hrqe;
+       struct lpfc_rqe drqe;
+
+       hrqe.address_lo = putPaddrLow(hbq_buf->hbuf.phys);
+       hrqe.address_hi = putPaddrHigh(hbq_buf->hbuf.phys);
+       drqe.address_lo = putPaddrLow(hbq_buf->dbuf.phys);
+       drqe.address_hi = putPaddrHigh(hbq_buf->dbuf.phys);
+       rc = lpfc_sli4_rq_put(phba->sli4_hba.hdr_rq, phba->sli4_hba.dat_rq,
+                             &hrqe, &drqe);
+       if (rc < 0)
+               return rc;
+       hbq_buf->tag = rc;
+       list_add_tail(&hbq_buf->dbuf.list, &phba->hbqs[hbqno].hbq_buffer_list);
+       return 0;
 }
 
 /* HBQ for ELS and CT traffic. */
@@ -914,7 +1442,7 @@ lpfc_sli_hbqbuf_fill_hbqs(struct lpfc_hba *phba, uint32_t hbqno, uint32_t count)
                                 dbuf.list);
                hbq_buffer->tag = (phba->hbqs[hbqno].buffer_count |
                                      (hbqno << 16));
-               if (lpfc_sli_hbq_to_firmware(phba, hbqno, hbq_buffer)) {
+               if (!lpfc_sli_hbq_to_firmware(phba, hbqno, hbq_buffer)) {
                        phba->hbqs[hbqno].buffer_count++;
                        posted++;
                } else
@@ -964,6 +1492,25 @@ lpfc_sli_hbqbuf_init_hbqs(struct lpfc_hba *phba, uint32_t qno)
                                         lpfc_hbq_defs[qno]->init_count));
 }
 
+/**
+ * lpfc_sli_hbqbuf_get - Remove the first hbq off of an hbq list
+ * @phba: Pointer to HBA context object.
+ * @hbqno: HBQ number.
+ *
+ * This function removes the first hbq buffer on an hbq list and returns a
+ * pointer to that buffer. If it finds no buffers on the list it returns NULL.
+ **/
+static struct hbq_dmabuf *
+lpfc_sli_hbqbuf_get(struct list_head *rb_list)
+{
+       struct lpfc_dmabuf *d_buf;
+
+       list_remove_head(rb_list, d_buf, struct lpfc_dmabuf, list);
+       if (!d_buf)
+               return NULL;
+       return container_of(d_buf, struct hbq_dmabuf, dbuf);
+}
+
 /**
  * lpfc_sli_hbqbuf_find - Find the hbq buffer associated with a tag
  * @phba: Pointer to HBA context object.
@@ -985,12 +1532,15 @@ lpfc_sli_hbqbuf_find(struct lpfc_hba *phba, uint32_t tag)
        if (hbqno >= LPFC_MAX_HBQS)
                return NULL;
 
+       spin_lock_irq(&phba->hbalock);
        list_for_each_entry(d_buf, &phba->hbqs[hbqno].hbq_buffer_list, list) {
                hbq_buf = container_of(d_buf, struct hbq_dmabuf, dbuf);
                if (hbq_buf->tag == tag) {
+                       spin_unlock_irq(&phba->hbalock);
                        return hbq_buf;
                }
        }
+       spin_unlock_irq(&phba->hbalock);
        lpfc_printf_log(phba, KERN_ERR, LOG_SLI | LOG_VPORT,
                        "1803 Bad hbq tag. Data: x%x x%x\n",
                        tag, phba->hbqs[tag >> 16].buffer_count);
@@ -1013,9 +1563,8 @@ lpfc_sli_free_hbq(struct lpfc_hba *phba, struct hbq_dmabuf *hbq_buffer)
 
        if (hbq_buffer) {
                hbqno = hbq_buffer->tag >> 16;
-               if (!lpfc_sli_hbq_to_firmware(phba, hbqno, hbq_buffer)) {
+               if (lpfc_sli_hbq_to_firmware(phba, hbqno, hbq_buffer))
                        (phba->hbqs[hbqno].hbq_free_buffer)(phba, hbq_buffer);
-               }
        }
 }
 
@@ -1086,6 +1635,15 @@ lpfc_sli_chk_mbx_command(uint8_t mbxCommand)
        case MBX_HEARTBEAT:
        case MBX_PORT_CAPABILITIES:
        case MBX_PORT_IOV_CONTROL:
+       case MBX_SLI4_CONFIG:
+       case MBX_SLI4_REQ_FTRS:
+       case MBX_REG_FCFI:
+       case MBX_UNREG_FCFI:
+       case MBX_REG_VFI:
+       case MBX_UNREG_VFI:
+       case MBX_INIT_VPI:
+       case MBX_INIT_VFI:
+       case MBX_RESUME_RPI:
                ret = mbxCommand;
                break;
        default:
@@ -1106,7 +1664,7 @@ lpfc_sli_chk_mbx_command(uint8_t mbxCommand)
  * will wake up thread waiting on the wait queue pointed by context1
  * of the mailbox.
  **/
-static void
+void
 lpfc_sli_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
 {
        wait_queue_head_t *pdone_q;
@@ -1140,7 +1698,7 @@ void
 lpfc_sli_def_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
 {
        struct lpfc_dmabuf *mp;
-       uint16_t rpi;
+       uint16_t rpi, vpi;
        int rc;
 
        mp = (struct lpfc_dmabuf *) (pmb->context1);
@@ -1150,24 +1708,30 @@ lpfc_sli_def_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
                kfree(mp);
        }
 
+       if ((pmb->u.mb.mbxCommand == MBX_UNREG_LOGIN) &&
+           (phba->sli_rev == LPFC_SLI_REV4))
+               lpfc_sli4_free_rpi(phba, pmb->u.mb.un.varUnregLogin.rpi);
+
        /*
         * If a REG_LOGIN succeeded  after node is destroyed or node
         * is in re-discovery driver need to cleanup the RPI.
         */
        if (!(phba->pport->load_flag & FC_UNLOADING) &&
-           pmb->mb.mbxCommand == MBX_REG_LOGIN64 &&
-           !pmb->mb.mbxStatus) {
-
-               rpi = pmb->mb.un.varWords[0];
-               lpfc_unreg_login(phba, pmb->mb.un.varRegLogin.vpi, rpi, pmb);
+           pmb->u.mb.mbxCommand == MBX_REG_LOGIN64 &&
+           !pmb->u.mb.mbxStatus) {
+               rpi = pmb->u.mb.un.varWords[0];
+               vpi = pmb->u.mb.un.varRegLogin.vpi - phba->vpi_base;
+               lpfc_unreg_login(phba, vpi, rpi, pmb);
                pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
                rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
                if (rc != MBX_NOT_FINISHED)
                        return;
        }
 
-       mempool_free(pmb, phba->mbox_mem_pool);
-       return;
+       if (bf_get(lpfc_mqe_command, &pmb->u.mqe) == MBX_SLI4_CONFIG)
+               lpfc_sli4_mbox_cmd_free(phba, pmb);
+       else
+               mempool_free(pmb, phba->mbox_mem_pool);
 }
 
 /**
@@ -1204,7 +1768,7 @@ lpfc_sli_handle_mb_event(struct lpfc_hba *phba)
                if (pmb == NULL)
                        break;
 
-               pmbox = &pmb->mb;
+               pmbox = &pmb->u.mb;
 
                if (pmbox->mbxCommand != MBX_HEARTBEAT) {
                        if (pmb->vport) {
@@ -1233,9 +1797,10 @@ lpfc_sli_handle_mb_event(struct lpfc_hba *phba)
                        /* Unknow mailbox command compl */
                        lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
                                        "(%d):0323 Unknown Mailbox command "
-                                       "%x Cmpl\n",
+                                       "x%x (x%x) Cmpl\n",
                                        pmb->vport ? pmb->vport->vpi : 0,
-                                       pmbox->mbxCommand);
+                                       pmbox->mbxCommand,
+                                       lpfc_sli4_mbox_opcode_get(phba, pmb));
                        phba->link_state = LPFC_HBA_ERROR;
                        phba->work_hs = HS_FFER3;
                        lpfc_handle_eratt(phba);
@@ -1250,29 +1815,29 @@ lpfc_sli_handle_mb_event(struct lpfc_hba *phba)
                                                LOG_MBOX | LOG_SLI,
                                                "(%d):0305 Mbox cmd cmpl "
                                                "error - RETRYing Data: x%x "
-                                               "x%x x%x x%x\n",
+                                               "(x%x) x%x x%x x%x\n",
                                                pmb->vport ? pmb->vport->vpi :0,
                                                pmbox->mbxCommand,
+                                               lpfc_sli4_mbox_opcode_get(phba,
+                                                                         pmb),
                                                pmbox->mbxStatus,
                                                pmbox->un.varWords[0],
                                                pmb->vport->port_state);
                                pmbox->mbxStatus = 0;
                                pmbox->mbxOwner = OWN_HOST;
-                               spin_lock_irq(&phba->hbalock);
-                               phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
-                               spin_unlock_irq(&phba->hbalock);
                                rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
-                               if (rc == MBX_SUCCESS)
+                               if (rc != MBX_NOT_FINISHED)
                                        continue;
                        }
                }
 
                /* Mailbox cmd <cmd> Cmpl <cmpl> */
                lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
-                               "(%d):0307 Mailbox cmd x%x Cmpl x%p "
+                               "(%d):0307 Mailbox cmd x%x (x%x) Cmpl x%p "
                                "Data: x%x x%x x%x x%x x%x x%x x%x x%x x%x\n",
                                pmb->vport ? pmb->vport->vpi : 0,
                                pmbox->mbxCommand,
+                               lpfc_sli4_mbox_opcode_get(phba, pmb),
                                pmb->mbox_cmpl,
                                *((uint32_t *) pmbox),
                                pmbox->un.varWords[0],
@@ -1317,6 +1882,45 @@ lpfc_sli_get_buff(struct lpfc_hba *phba,
        return &hbq_entry->dbuf;
 }
 
+/**
+ * lpfc_complete_unsol_iocb - Complete an unsolicited sequence
+ * @phba: Pointer to HBA context object.
+ * @pring: Pointer to driver SLI ring object.
+ * @saveq: Pointer to the iocbq struct representing the sequence starting frame.
+ * @fch_r_ctl: the r_ctl for the first frame of the sequence.
+ * @fch_type: the type for the first frame of the sequence.
+ *
+ * This function is called with no lock held. This function uses the r_ctl and
+ * type of the received sequence to find the correct callback function to call
+ * to process the sequence.
+ **/
+static int
+lpfc_complete_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+                        struct lpfc_iocbq *saveq, uint32_t fch_r_ctl,
+                        uint32_t fch_type)
+{
+       int i;
+
+       /* unSolicited Responses */
+       if (pring->prt[0].profile) {
+               if (pring->prt[0].lpfc_sli_rcv_unsol_event)
+                       (pring->prt[0].lpfc_sli_rcv_unsol_event) (phba, pring,
+                                                                       saveq);
+               return 1;
+       }
+       /* We must search, based on rctl / type
+          for the right routine */
+       for (i = 0; i < pring->num_mask; i++) {
+               if ((pring->prt[i].rctl == fch_r_ctl) &&
+                   (pring->prt[i].type == fch_type)) {
+                       if (pring->prt[i].lpfc_sli_rcv_unsol_event)
+                               (pring->prt[i].lpfc_sli_rcv_unsol_event)
+                                               (phba, pring, saveq);
+                       return 1;
+               }
+       }
+       return 0;
+}
 
 /**
  * lpfc_sli_process_unsol_iocb - Unsolicited iocb handler
@@ -1339,7 +1943,7 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
        IOCB_t           * irsp;
        WORD5            * w5p;
        uint32_t           Rctl, Type;
-       uint32_t           match, i;
+       uint32_t           match;
        struct lpfc_iocbq *iocbq;
        struct lpfc_dmabuf *dmzbuf;
 
@@ -1482,35 +2086,12 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
                }
        }
 
-       /* unSolicited Responses */
-       if (pring->prt[0].profile) {
-               if (pring->prt[0].lpfc_sli_rcv_unsol_event)
-                       (pring->prt[0].lpfc_sli_rcv_unsol_event) (phba, pring,
-                                                                       saveq);
-               match = 1;
-       } else {
-               /* We must search, based on rctl / type
-                  for the right routine */
-               for (i = 0; i < pring->num_mask; i++) {
-                       if ((pring->prt[i].rctl == Rctl)
-                           && (pring->prt[i].type == Type)) {
-                               if (pring->prt[i].lpfc_sli_rcv_unsol_event)
-                                       (pring->prt[i].lpfc_sli_rcv_unsol_event)
-                                                       (phba, pring, saveq);
-                               match = 1;
-                               break;
-                       }
-               }
-       }
-       if (match == 0) {
-               /* Unexpected Rctl / Type received */
-               /* Ring <ringno> handler: unexpected
-                  Rctl <Rctl> Type <Type> received */
+       if (!lpfc_complete_unsol_iocb(phba, pring, saveq, Rctl, Type))
                lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
                                "0313 Ring %d handler: unexpected Rctl x%x "
                                "Type x%x received\n",
                                pring->ringno, Rctl, Type);
-       }
+
        return 1;
 }
 
@@ -1551,6 +2132,37 @@ lpfc_sli_iocbq_lookup(struct lpfc_hba *phba,
        return NULL;
 }
 
+/**
+ * lpfc_sli_iocbq_lookup_by_tag - Find command iocb for the iotag
+ * @phba: Pointer to HBA context object.
+ * @pring: Pointer to driver SLI ring object.
+ * @iotag: IOCB tag.
+ *
+ * This function looks up the iocb_lookup table to get the command iocb
+ * corresponding to the given iotag. This function is called with the
+ * hbalock held.
+ * This function returns the command iocb object if it finds the command
+ * iocb else returns NULL.
+ **/
+static struct lpfc_iocbq *
+lpfc_sli_iocbq_lookup_by_tag(struct lpfc_hba *phba,
+                            struct lpfc_sli_ring *pring, uint16_t iotag)
+{
+       struct lpfc_iocbq *cmd_iocb;
+
+       if (iotag != 0 && iotag <= phba->sli.last_iotag) {
+               cmd_iocb = phba->sli.iocbq_lookup[iotag];
+               list_del_init(&cmd_iocb->list);
+               pring->txcmplq_cnt--;
+               return cmd_iocb;
+       }
+
+       lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                       "0372 iotag x%x is out off range: max iotag (x%x)\n",
+                       iotag, phba->sli.last_iotag);
+       return NULL;
+}
+
 /**
  * lpfc_sli_process_sol_iocb - process solicited iocb completion
  * @phba: Pointer to HBA context object.
@@ -1954,7 +2566,7 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
                        if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
                                (irsp->un.ulpWord[4] == IOERR_NO_RESOURCES)) {
                                spin_unlock_irqrestore(&phba->hbalock, iflag);
-                               lpfc_rampdown_queue_depth(phba);
+                               phba->lpfc_rampdown_queue_depth(phba);
                                spin_lock_irqsave(&phba->hbalock, iflag);
                        }
 
@@ -2068,39 +2680,215 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
 }
 
 /**
- * lpfc_sli_handle_slow_ring_event - Handle ring events for non-FCP rings
+ * lpfc_sli_sp_handle_rspiocb - Handle slow-path response iocb
+ * @phba: Pointer to HBA context object.
+ * @pring: Pointer to driver SLI ring object.
+ * @rspiocbp: Pointer to driver response IOCB object.
+ *
+ * This function is called from the worker thread when there is a slow-path
+ * response IOCB to process. This function chains all the response iocbs until
+ * seeing the iocb with the LE bit set. The function will call
+ * lpfc_sli_process_sol_iocb function if the response iocb indicates a
+ * completion of a command iocb. The function will call the
+ * lpfc_sli_process_unsol_iocb function if this is an unsolicited iocb.
+ * The function frees the resources or calls the completion handler if this
+ * iocb is an abort completion. The function returns NULL when the response
+ * iocb has the LE bit set and all the chained iocbs are processed, otherwise
+ * this function shall chain the iocb on to the iocb_continueq and return the
+ * response iocb passed in.
+ **/
+static struct lpfc_iocbq *
+lpfc_sli_sp_handle_rspiocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+                       struct lpfc_iocbq *rspiocbp)
+{
+       struct lpfc_iocbq *saveq;
+       struct lpfc_iocbq *cmdiocbp;
+       struct lpfc_iocbq *next_iocb;
+       IOCB_t *irsp = NULL;
+       uint32_t free_saveq;
+       uint8_t iocb_cmd_type;
+       lpfc_iocb_type type;
+       unsigned long iflag;
+       int rc;
+
+       spin_lock_irqsave(&phba->hbalock, iflag);
+       /* First add the response iocb to the countinueq list */
+       list_add_tail(&rspiocbp->list, &(pring->iocb_continueq));
+       pring->iocb_continueq_cnt++;
+
+       /* Now, determine whetehr the list is completed for processing */
+       irsp = &rspiocbp->iocb;
+       if (irsp->ulpLe) {
+               /*
+                * By default, the driver expects to free all resources
+                * associated with this iocb completion.
+                */
+               free_saveq = 1;
+               saveq = list_get_first(&pring->iocb_continueq,
+                                      struct lpfc_iocbq, list);
+               irsp = &(saveq->iocb);
+               list_del_init(&pring->iocb_continueq);
+               pring->iocb_continueq_cnt = 0;
+
+               pring->stats.iocb_rsp++;
+
+               /*
+                * If resource errors reported from HBA, reduce
+                * queuedepths of the SCSI device.
+                */
+               if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+                   (irsp->un.ulpWord[4] == IOERR_NO_RESOURCES)) {
+                       spin_unlock_irqrestore(&phba->hbalock, iflag);
+                       phba->lpfc_rampdown_queue_depth(phba);
+                       spin_lock_irqsave(&phba->hbalock, iflag);
+               }
+
+               if (irsp->ulpStatus) {
+                       /* Rsp ring <ringno> error: IOCB */
+                       lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+                                       "0328 Rsp Ring %d error: "
+                                       "IOCB Data: "
+                                       "x%x x%x x%x x%x "
+                                       "x%x x%x x%x x%x "
+                                       "x%x x%x x%x x%x "
+                                       "x%x x%x x%x x%x\n",
+                                       pring->ringno,
+                                       irsp->un.ulpWord[0],
+                                       irsp->un.ulpWord[1],
+                                       irsp->un.ulpWord[2],
+                                       irsp->un.ulpWord[3],
+                                       irsp->un.ulpWord[4],
+                                       irsp->un.ulpWord[5],
+                                       *(((uint32_t *) irsp) + 6),
+                                       *(((uint32_t *) irsp) + 7),
+                                       *(((uint32_t *) irsp) + 8),
+                                       *(((uint32_t *) irsp) + 9),
+                                       *(((uint32_t *) irsp) + 10),
+                                       *(((uint32_t *) irsp) + 11),
+                                       *(((uint32_t *) irsp) + 12),
+                                       *(((uint32_t *) irsp) + 13),
+                                       *(((uint32_t *) irsp) + 14),
+                                       *(((uint32_t *) irsp) + 15));
+               }
+
+               /*
+                * Fetch the IOCB command type and call the correct completion
+                * routine. Solicited and Unsolicited IOCBs on the ELS ring
+                * get freed back to the lpfc_iocb_list by the discovery
+                * kernel thread.
+                */
+               iocb_cmd_type = irsp->ulpCommand & CMD_IOCB_MASK;
+               type = lpfc_sli_iocb_cmd_type(iocb_cmd_type);
+               switch (type) {
+               case LPFC_SOL_IOCB:
+                       spin_unlock_irqrestore(&phba->hbalock, iflag);
+                       rc = lpfc_sli_process_sol_iocb(phba, pring, saveq);
+                       spin_lock_irqsave(&phba->hbalock, iflag);
+                       break;
+
+               case LPFC_UNSOL_IOCB:
+                       spin_unlock_irqrestore(&phba->hbalock, iflag);
+                       rc = lpfc_sli_process_unsol_iocb(phba, pring, saveq);
+                       spin_lock_irqsave(&phba->hbalock, iflag);
+                       if (!rc)
+                               free_saveq = 0;
+                       break;
+
+               case LPFC_ABORT_IOCB:
+                       cmdiocbp = NULL;
+                       if (irsp->ulpCommand != CMD_XRI_ABORTED_CX)
+                               cmdiocbp = lpfc_sli_iocbq_lookup(phba, pring,
+                                                                saveq);
+                       if (cmdiocbp) {
+                               /* Call the specified completion routine */
+                               if (cmdiocbp->iocb_cmpl) {
+                                       spin_unlock_irqrestore(&phba->hbalock,
+                                                              iflag);
+                                       (cmdiocbp->iocb_cmpl)(phba, cmdiocbp,
+                                                             saveq);
+                                       spin_lock_irqsave(&phba->hbalock,
+                                                         iflag);
+                               } else
+                                       __lpfc_sli_release_iocbq(phba,
+                                                                cmdiocbp);
+                       }
+                       break;
+
+               case LPFC_UNKNOWN_IOCB:
+                       if (irsp->ulpCommand == CMD_ADAPTER_MSG) {
+                               char adaptermsg[LPFC_MAX_ADPTMSG];
+                               memset(adaptermsg, 0, LPFC_MAX_ADPTMSG);
+                               memcpy(&adaptermsg[0], (uint8_t *)irsp,
+                                      MAX_MSG_DATA);
+                               dev_warn(&((phba->pcidev)->dev),
+                                        "lpfc%d: %s\n",
+                                        phba->brd_no, adaptermsg);
+                       } else {
+                               /* Unknown IOCB command */
+                               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                                               "0335 Unknown IOCB "
+                                               "command Data: x%x "
+                                               "x%x x%x x%x\n",
+                                               irsp->ulpCommand,
+                                               irsp->ulpStatus,
+                                               irsp->ulpIoTag,
+                                               irsp->ulpContext);
+                       }
+                       break;
+               }
+
+               if (free_saveq) {
+                       list_for_each_entry_safe(rspiocbp, next_iocb,
+                                                &saveq->list, list) {
+                               list_del(&rspiocbp->list);
+                               __lpfc_sli_release_iocbq(phba, rspiocbp);
+                       }
+                       __lpfc_sli_release_iocbq(phba, saveq);
+               }
+               rspiocbp = NULL;
+       }
+       spin_unlock_irqrestore(&phba->hbalock, iflag);
+       return rspiocbp;
+}
+
+/**
+ * lpfc_sli_handle_slow_ring_event - Wrapper func for handling slow-path iocbs
  * @phba: Pointer to HBA context object.
  * @pring: Pointer to driver SLI ring object.
  * @mask: Host attention register mask for this ring.
  *
- * This function is called from the worker thread when there is a ring
- * event for non-fcp rings. The caller does not hold any lock .
- * The function processes each response iocb in the response ring until it
- * finds an iocb with LE bit set and chains all the iocbs upto the iocb with
- * LE bit set. The function will call lpfc_sli_process_sol_iocb function if the
- * response iocb indicates a completion of a command iocb. The function
- * will call lpfc_sli_process_unsol_iocb function if this is an unsolicited
- * iocb. The function frees the resources or calls the completion handler if
- * this iocb is an abort completion. The function returns 0 when the allocated
- * iocbs are not freed, otherwise returns 1.
+ * This routine wraps the actual slow_ring event process routine from the
+ * API jump table function pointer from the lpfc_hba struct.
  **/
-int
+void
 lpfc_sli_handle_slow_ring_event(struct lpfc_hba *phba,
                                struct lpfc_sli_ring *pring, uint32_t mask)
+{
+       phba->lpfc_sli_handle_slow_ring_event(phba, pring, mask);
+}
+
+/**
+ * lpfc_sli_handle_slow_ring_event_s3 - Handle SLI3 ring event for non-FCP rings
+ * @phba: Pointer to HBA context object.
+ * @pring: Pointer to driver SLI ring object.
+ * @mask: Host attention register mask for this ring.
+ *
+ * This function is called from the worker thread when there is a ring event
+ * for non-fcp rings. The caller does not hold any lock. The function will
+ * remove each response iocb in the response ring and calls the handle
+ * response iocb routine (lpfc_sli_sp_handle_rspiocb) to process it.
+ **/
+static void
+lpfc_sli_handle_slow_ring_event_s3(struct lpfc_hba *phba,
+                                  struct lpfc_sli_ring *pring, uint32_t mask)
 {
        struct lpfc_pgp *pgp;
        IOCB_t *entry;
        IOCB_t *irsp = NULL;
        struct lpfc_iocbq *rspiocbp = NULL;
-       struct lpfc_iocbq *next_iocb;
-       struct lpfc_iocbq *cmdiocbp;
-       struct lpfc_iocbq *saveq;
-       uint8_t iocb_cmd_type;
-       lpfc_iocb_type type;
-       uint32_t status, free_saveq;
        uint32_t portRspPut, portRspMax;
-       int rc = 1;
        unsigned long iflag;
+       uint32_t status;
 
        pgp = &phba->port_gp[pring->ringno];
        spin_lock_irqsave(&phba->hbalock, iflag);
@@ -2128,7 +2916,7 @@ lpfc_sli_handle_slow_ring_event(struct lpfc_hba *phba,
                phba->work_hs = HS_FFER3;
                lpfc_handle_eratt(phba);
 
-               return 1;
+               return;
        }
 
        rmb();
@@ -2173,138 +2961,10 @@ lpfc_sli_handle_slow_ring_event(struct lpfc_hba *phba,
 
                writel(pring->rspidx, &phba->host_gp[pring->ringno].rspGetInx);
 
-               list_add_tail(&rspiocbp->list, &(pring->iocb_continueq));
-
-               pring->iocb_continueq_cnt++;
-               if (irsp->ulpLe) {
-                       /*
-                        * By default, the driver expects to free all resources
-                        * associated with this iocb completion.
-                        */
-                       free_saveq = 1;
-                       saveq = list_get_first(&pring->iocb_continueq,
-                                              struct lpfc_iocbq, list);
-                       irsp = &(saveq->iocb);
-                       list_del_init(&pring->iocb_continueq);
-                       pring->iocb_continueq_cnt = 0;
-
-                       pring->stats.iocb_rsp++;
-
-                       /*
-                        * If resource errors reported from HBA, reduce
-                        * queuedepths of the SCSI device.
-                        */
-                       if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
-                            (irsp->un.ulpWord[4] == IOERR_NO_RESOURCES)) {
-                               spin_unlock_irqrestore(&phba->hbalock, iflag);
-                               lpfc_rampdown_queue_depth(phba);
-                               spin_lock_irqsave(&phba->hbalock, iflag);
-                       }
-
-                       if (irsp->ulpStatus) {
-                               /* Rsp ring <ringno> error: IOCB */
-                               lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
-                                               "0328 Rsp Ring %d error: "
-                                               "IOCB Data: "
-                                               "x%x x%x x%x x%x "
-                                               "x%x x%x x%x x%x "
-                                               "x%x x%x x%x x%x "
-                                               "x%x x%x x%x x%x\n",
-                                               pring->ringno,
-                                               irsp->un.ulpWord[0],
-                                               irsp->un.ulpWord[1],
-                                               irsp->un.ulpWord[2],
-                                               irsp->un.ulpWord[3],
-                                               irsp->un.ulpWord[4],
-                                               irsp->un.ulpWord[5],
-                                               *(((uint32_t *) irsp) + 6),
-                                               *(((uint32_t *) irsp) + 7),
-                                               *(((uint32_t *) irsp) + 8),
-                                               *(((uint32_t *) irsp) + 9),
-                                               *(((uint32_t *) irsp) + 10),
-                                               *(((uint32_t *) irsp) + 11),
-                                               *(((uint32_t *) irsp) + 12),
-                                               *(((uint32_t *) irsp) + 13),
-                                               *(((uint32_t *) irsp) + 14),
-                                               *(((uint32_t *) irsp) + 15));
-                       }
-
-                       /*
-                        * Fetch the IOCB command type and call the correct
-                        * completion routine.  Solicited and Unsolicited
-                        * IOCBs on the ELS ring get freed back to the
-                        * lpfc_iocb_list by the discovery kernel thread.
-                        */
-                       iocb_cmd_type = irsp->ulpCommand & CMD_IOCB_MASK;
-                       type = lpfc_sli_iocb_cmd_type(iocb_cmd_type);
-                       if (type == LPFC_SOL_IOCB) {
-                               spin_unlock_irqrestore(&phba->hbalock, iflag);
-                               rc = lpfc_sli_process_sol_iocb(phba, pring,
-                                                              saveq);
-                               spin_lock_irqsave(&phba->hbalock, iflag);
-                       } else if (type == LPFC_UNSOL_IOCB) {
-                               spin_unlock_irqrestore(&phba->hbalock, iflag);
-                               rc = lpfc_sli_process_unsol_iocb(phba, pring,
-                                                                saveq);
-                               spin_lock_irqsave(&phba->hbalock, iflag);
-                               if (!rc)
-                                       free_saveq = 0;
-                       } else if (type == LPFC_ABORT_IOCB) {
-                               if ((irsp->ulpCommand != CMD_XRI_ABORTED_CX) &&
-                                   ((cmdiocbp =
-                                     lpfc_sli_iocbq_lookup(phba, pring,
-                                                           saveq)))) {
-                                       /* Call the specified completion
-                                          routine */
-                                       if (cmdiocbp->iocb_cmpl) {
-                                               spin_unlock_irqrestore(
-                                                      &phba->hbalock,
-                                                      iflag);
-                                               (cmdiocbp->iocb_cmpl) (phba,
-                                                            cmdiocbp, saveq);
-                                               spin_lock_irqsave(
-                                                         &phba->hbalock,
-                                                         iflag);
-                                       } else
-                                               __lpfc_sli_release_iocbq(phba,
-                                                                     cmdiocbp);
-                               }
-                       } else if (type == LPFC_UNKNOWN_IOCB) {
-                               if (irsp->ulpCommand == CMD_ADAPTER_MSG) {
-
-                                       char adaptermsg[LPFC_MAX_ADPTMSG];
-
-                                       memset(adaptermsg, 0,
-                                              LPFC_MAX_ADPTMSG);
-                                       memcpy(&adaptermsg[0], (uint8_t *) irsp,
-                                              MAX_MSG_DATA);
-                                       dev_warn(&((phba->pcidev)->dev),
-                                                "lpfc%d: %s\n",
-                                                phba->brd_no, adaptermsg);
-                               } else {
-                                       /* Unknown IOCB command */
-                                       lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
-                                                       "0335 Unknown IOCB "
-                                                       "command Data: x%x "
-                                                       "x%x x%x x%x\n",
-                                                       irsp->ulpCommand,
-                                                       irsp->ulpStatus,
-                                                       irsp->ulpIoTag,
-                                                       irsp->ulpContext);
-                               }
-                       }
-
-                       if (free_saveq) {
-                               list_for_each_entry_safe(rspiocbp, next_iocb,
-                                                        &saveq->list, list) {
-                                       list_del(&rspiocbp->list);
-                                       __lpfc_sli_release_iocbq(phba,
-                                                                rspiocbp);
-                               }
-                               __lpfc_sli_release_iocbq(phba, saveq);
-                       }
-                       rspiocbp = NULL;
-               }
+               spin_unlock_irqrestore(&phba->hbalock, iflag);
+               /* Handle the response IOCB */
+               rspiocbp = lpfc_sli_sp_handle_rspiocb(phba, pring, rspiocbp);
+               spin_lock_irqsave(&phba->hbalock, iflag);
 
                /*
                 * If the port response put pointer has not been updated, sync
@@ -2338,7 +2998,37 @@ lpfc_sli_handle_slow_ring_event(struct lpfc_hba *phba,
        }
 
        spin_unlock_irqrestore(&phba->hbalock, iflag);
-       return rc;
+       return;
+}
+
+/**
+ * lpfc_sli_handle_slow_ring_event_s4 - Handle SLI4 slow-path els events
+ * @phba: Pointer to HBA context object.
+ * @pring: Pointer to driver SLI ring object.
+ * @mask: Host attention register mask for this ring.
+ *
+ * This function is called from the worker thread when there is a pending
+ * ELS response iocb on the driver internal slow-path response iocb worker
+ * queue. The caller does not hold any lock. The function will remove each
+ * response iocb from the response worker queue and calls the handle
+ * response iocb routine (lpfc_sli_sp_handle_rspiocb) to process it.
+ **/
+static void
+lpfc_sli_handle_slow_ring_event_s4(struct lpfc_hba *phba,
+                                  struct lpfc_sli_ring *pring, uint32_t mask)
+{
+       struct lpfc_iocbq *irspiocbq;
+       unsigned long iflag;
+
+       while (!list_empty(&phba->sli4_hba.sp_rspiocb_work_queue)) {
+               /* Get the response iocb from the head of work queue */
+               spin_lock_irqsave(&phba->hbalock, iflag);
+               list_remove_head(&phba->sli4_hba.sp_rspiocb_work_queue,
+                                irspiocbq, struct lpfc_iocbq, list);
+               spin_unlock_irqrestore(&phba->hbalock, iflag);
+               /* Process the response iocb */
+               lpfc_sli_sp_handle_rspiocb(phba, pring, irspiocbq);
+       }
 }
 
 /**
@@ -2420,7 +3110,7 @@ lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba)
 }
 
 /**
- * lpfc_sli_brdready - Check for host status bits
+ * lpfc_sli_brdready_s3 - Check for sli3 host ready status
  * @phba: Pointer to HBA context object.
  * @mask: Bit mask to be checked.
  *
@@ -2432,8 +3122,8 @@ lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba)
  * function returns 1 when HBA fail to restart otherwise returns
  * zero.
  **/
-int
-lpfc_sli_brdready(struct lpfc_hba *phba, uint32_t mask)
+static int
+lpfc_sli_brdready_s3(struct lpfc_hba *phba, uint32_t mask)
 {
        uint32_t status;
        int i = 0;
@@ -2477,6 +3167,56 @@ lpfc_sli_brdready(struct lpfc_hba *phba, uint32_t mask)
        return retval;
 }
 
+/**
+ * lpfc_sli_brdready_s4 - Check for sli4 host ready status
+ * @phba: Pointer to HBA context object.
+ * @mask: Bit mask to be checked.
+ *
+ * This function checks the host status register to check if HBA is
+ * ready. This function will wait in a loop for the HBA to be ready
+ * If the HBA is not ready , the function will will reset the HBA PCI
+ * function again. The function returns 1 when HBA fail to be ready
+ * otherwise returns zero.
+ **/
+static int
+lpfc_sli_brdready_s4(struct lpfc_hba *phba, uint32_t mask)
+{
+       uint32_t status;
+       int retval = 0;
+
+       /* Read the HBA Host Status Register */
+       status = lpfc_sli4_post_status_check(phba);
+
+       if (status) {
+               phba->pport->port_state = LPFC_VPORT_UNKNOWN;
+               lpfc_sli_brdrestart(phba);
+               status = lpfc_sli4_post_status_check(phba);
+       }
+
+       /* Check to see if any errors occurred during init */
+       if (status) {
+               phba->link_state = LPFC_HBA_ERROR;
+               retval = 1;
+       } else
+               phba->sli4_hba.intr_enable = 0;
+
+       return retval;
+}
+
+/**
+ * lpfc_sli_brdready - Wrapper func for checking the hba readyness
+ * @phba: Pointer to HBA context object.
+ * @mask: Bit mask to be checked.
+ *
+ * This routine wraps the actual SLI3 or SLI4 hba readyness check routine
+ * from the API jump table function pointer from the lpfc_hba struct.
+ **/
+int
+lpfc_sli_brdready(struct lpfc_hba *phba, uint32_t mask)
+{
+       return phba->lpfc_sli_brdready(phba, mask);
+}
+
 #define BARRIER_TEST_PATTERN (0xdeadbeef)
 
 /**
@@ -2532,7 +3272,7 @@ void lpfc_reset_barrier(struct lpfc_hba *phba)
                mdelay(1);
 
        if (readl(resp_buf + 1) != ~(BARRIER_TEST_PATTERN)) {
-               if (phba->sli.sli_flag & LPFC_SLI2_ACTIVE ||
+               if (phba->sli.sli_flag & LPFC_SLI_ACTIVE ||
                    phba->pport->stopped)
                        goto restore_hc;
                else
@@ -2613,7 +3353,9 @@ lpfc_sli_brdkill(struct lpfc_hba *phba)
                return 1;
        }
 
-       psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
+       spin_lock_irq(&phba->hbalock);
+       psli->sli_flag &= ~LPFC_SLI_ACTIVE;
+       spin_unlock_irq(&phba->hbalock);
 
        mempool_free(pmb, phba->mbox_mem_pool);
 
@@ -2636,10 +3378,10 @@ lpfc_sli_brdkill(struct lpfc_hba *phba)
        }
        spin_lock_irq(&phba->hbalock);
        psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+       psli->mbox_active = NULL;
        phba->link_flag &= ~LS_IGNORE_ERATT;
        spin_unlock_irq(&phba->hbalock);
 
-       psli->mbox_active = NULL;
        lpfc_hba_down_post(phba);
        phba->link_state = LPFC_HBA_ERROR;
 
@@ -2647,7 +3389,7 @@ lpfc_sli_brdkill(struct lpfc_hba *phba)
 }
 
 /**
- * lpfc_sli_brdreset - Reset the HBA
+ * lpfc_sli_brdreset - Reset a sli-2 or sli-3 HBA
  * @phba: Pointer to HBA context object.
  *
  * This function resets the HBA by writing HC_INITFF to the control
@@ -2683,7 +3425,8 @@ lpfc_sli_brdreset(struct lpfc_hba *phba)
                              (cfg_value &
                               ~(PCI_COMMAND_PARITY | PCI_COMMAND_SERR)));
 
-       psli->sli_flag &= ~(LPFC_SLI2_ACTIVE | LPFC_PROCESS_LA);
+       psli->sli_flag &= ~(LPFC_SLI_ACTIVE | LPFC_PROCESS_LA);
+
        /* Now toggle INITFF bit in the Host Control Register */
        writel(HC_INITFF, phba->HCregaddr);
        mdelay(1);
@@ -2710,27 +3453,86 @@ lpfc_sli_brdreset(struct lpfc_hba *phba)
 }
 
 /**
- * lpfc_sli_brdrestart - Restart the HBA
+ * lpfc_sli4_brdreset - Reset a sli-4 HBA
  * @phba: Pointer to HBA context object.
  *
- * This function is called in the SLI initialization code path to
- * restart the HBA. The caller is not required to hold any lock.
- * This function writes MBX_RESTART mailbox command to the SLIM and
- * resets the HBA. At the end of the function, it calls lpfc_hba_down_post
- * function to free any pending commands. The function enables
- * POST only during the first initialization. The function returns zero.
- * The function does not guarantee completion of MBX_RESTART mailbox
- * command before the return of this function.
+ * This function resets a SLI4 HBA. This function disables PCI layer parity
+ * checking during resets the device. The caller is not required to hold
+ * any locks.
+ *
+ * This function returns 0 always.
  **/
 int
-lpfc_sli_brdrestart(struct lpfc_hba *phba)
+lpfc_sli4_brdreset(struct lpfc_hba *phba)
 {
-       MAILBOX_t *mb;
-       struct lpfc_sli *psli;
-       volatile uint32_t word0;
-       void __iomem *to_slim;
-
-       spin_lock_irq(&phba->hbalock);
+       struct lpfc_sli *psli = &phba->sli;
+       uint16_t cfg_value;
+       uint8_t qindx;
+
+       /* Reset HBA */
+       lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+                       "0295 Reset HBA Data: x%x x%x\n",
+                       phba->pport->port_state, psli->sli_flag);
+
+       /* perform board reset */
+       phba->fc_eventTag = 0;
+       phba->pport->fc_myDID = 0;
+       phba->pport->fc_prevDID = 0;
+
+       /* Turn off parity checking and serr during the physical reset */
+       pci_read_config_word(phba->pcidev, PCI_COMMAND, &cfg_value);
+       pci_write_config_word(phba->pcidev, PCI_COMMAND,
+                             (cfg_value &
+                             ~(PCI_COMMAND_PARITY | PCI_COMMAND_SERR)));
+
+       spin_lock_irq(&phba->hbalock);
+       psli->sli_flag &= ~(LPFC_PROCESS_LA);
+       phba->fcf.fcf_flag = 0;
+       /* Clean up the child queue list for the CQs */
+       list_del_init(&phba->sli4_hba.mbx_wq->list);
+       list_del_init(&phba->sli4_hba.els_wq->list);
+       list_del_init(&phba->sli4_hba.hdr_rq->list);
+       list_del_init(&phba->sli4_hba.dat_rq->list);
+       list_del_init(&phba->sli4_hba.mbx_cq->list);
+       list_del_init(&phba->sli4_hba.els_cq->list);
+       list_del_init(&phba->sli4_hba.rxq_cq->list);
+       for (qindx = 0; qindx < phba->cfg_fcp_wq_count; qindx++)
+               list_del_init(&phba->sli4_hba.fcp_wq[qindx]->list);
+       for (qindx = 0; qindx < phba->cfg_fcp_eq_count; qindx++)
+               list_del_init(&phba->sli4_hba.fcp_cq[qindx]->list);
+       spin_unlock_irq(&phba->hbalock);
+
+       /* Now physically reset the device */
+       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                       "0389 Performing PCI function reset!\n");
+       /* Perform FCoE PCI function reset */
+       lpfc_pci_function_reset(phba);
+
+       return 0;
+}
+
+/**
+ * lpfc_sli_brdrestart_s3 - Restart a sli-3 hba
+ * @phba: Pointer to HBA context object.
+ *
+ * This function is called in the SLI initialization code path to
+ * restart the HBA. The caller is not required to hold any lock.
+ * This function writes MBX_RESTART mailbox command to the SLIM and
+ * resets the HBA. At the end of the function, it calls lpfc_hba_down_post
+ * function to free any pending commands. The function enables
+ * POST only during the first initialization. The function returns zero.
+ * The function does not guarantee completion of MBX_RESTART mailbox
+ * command before the return of this function.
+ **/
+static int
+lpfc_sli_brdrestart_s3(struct lpfc_hba *phba)
+{
+       MAILBOX_t *mb;
+       struct lpfc_sli *psli;
+       volatile uint32_t word0;
+       void __iomem *to_slim;
+
+       spin_lock_irq(&phba->hbalock);
 
        psli = &phba->sli;
 
@@ -2762,7 +3564,7 @@ lpfc_sli_brdrestart(struct lpfc_hba *phba)
        lpfc_sli_brdreset(phba);
        phba->pport->stopped = 0;
        phba->link_state = LPFC_INIT_START;
-
+       phba->hba_flag = 0;
        spin_unlock_irq(&phba->hbalock);
 
        memset(&psli->lnk_stat_offsets, 0, sizeof(psli->lnk_stat_offsets));
@@ -2776,6 +3578,55 @@ lpfc_sli_brdrestart(struct lpfc_hba *phba)
        return 0;
 }
 
+/**
+ * lpfc_sli_brdrestart_s4 - Restart the sli-4 hba
+ * @phba: Pointer to HBA context object.
+ *
+ * This function is called in the SLI initialization code path to restart
+ * a SLI4 HBA. The caller is not required to hold any lock.
+ * At the end of the function, it calls lpfc_hba_down_post function to
+ * free any pending commands.
+ **/
+static int
+lpfc_sli_brdrestart_s4(struct lpfc_hba *phba)
+{
+       struct lpfc_sli *psli = &phba->sli;
+
+
+       /* Restart HBA */
+       lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+                       "0296 Restart HBA Data: x%x x%x\n",
+                       phba->pport->port_state, psli->sli_flag);
+
+       lpfc_sli4_brdreset(phba);
+
+       spin_lock_irq(&phba->hbalock);
+       phba->pport->stopped = 0;
+       phba->link_state = LPFC_INIT_START;
+       phba->hba_flag = 0;
+       spin_unlock_irq(&phba->hbalock);
+
+       memset(&psli->lnk_stat_offsets, 0, sizeof(psli->lnk_stat_offsets));
+       psli->stats_start = get_seconds();
+
+       lpfc_hba_down_post(phba);
+
+       return 0;
+}
+
+/**
+ * lpfc_sli_brdrestart - Wrapper func for restarting hba
+ * @phba: Pointer to HBA context object.
+ *
+ * This routine wraps the actual SLI3 or SLI4 hba restart routine from the
+ * API jump table function pointer from the lpfc_hba struct.
+**/
+int
+lpfc_sli_brdrestart(struct lpfc_hba *phba)
+{
+       return phba->lpfc_sli_brdrestart(phba);
+}
+
 /**
  * lpfc_sli_chipset_init - Wait for the restart of the HBA after a restart
  * @phba: Pointer to HBA context object.
@@ -2940,7 +3791,7 @@ lpfc_sli_hbq_setup(struct lpfc_hba *phba)
        if (!pmb)
                return -ENOMEM;
 
-       pmbox = &pmb->mb;
+       pmbox = &pmb->u.mb;
 
        /* Initialize the struct lpfc_sli_hbq structure for each hbq */
        phba->link_state = LPFC_INIT_MBX_CMDS;
@@ -2983,6 +3834,26 @@ lpfc_sli_hbq_setup(struct lpfc_hba *phba)
        return 0;
 }
 
+/**
+ * lpfc_sli4_rb_setup - Initialize and post RBs to HBA
+ * @phba: Pointer to HBA context object.
+ *
+ * This function is called during the SLI initialization to configure
+ * all the HBQs and post buffers to the HBQ. The caller is not
+ * required to hold any locks. This function will return zero if successful
+ * else it will return negative error code.
+ **/
+static int
+lpfc_sli4_rb_setup(struct lpfc_hba *phba)
+{
+       phba->hbq_in_use = 1;
+       phba->hbqs[0].entry_count = lpfc_hbq_defs[0]->entry_count;
+       phba->hbq_count = 1;
+       /* Initially populate or replenish the HBQs */
+       lpfc_sli_hbqbuf_init_hbqs(phba, 0);
+       return 0;
+}
+
 /**
  * lpfc_sli_config_port - Issue config port mailbox command
  * @phba: Pointer to HBA context object.
@@ -3047,33 +3918,43 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
                        lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
                                "0442 Adapter failed to init, mbxCmd x%x "
                                "CONFIG_PORT, mbxStatus x%x Data: x%x\n",
-                               pmb->mb.mbxCommand, pmb->mb.mbxStatus, 0);
+                               pmb->u.mb.mbxCommand, pmb->u.mb.mbxStatus, 0);
                        spin_lock_irq(&phba->hbalock);
-                       phba->sli.sli_flag &= ~LPFC_SLI2_ACTIVE;
+                       phba->sli.sli_flag &= ~LPFC_SLI_ACTIVE;
                        spin_unlock_irq(&phba->hbalock);
                        rc = -ENXIO;
-               } else
+               } else {
+                       /* Allow asynchronous mailbox command to go through */
+                       spin_lock_irq(&phba->hbalock);
+                       phba->sli.sli_flag &= ~LPFC_SLI_ASYNC_MBX_BLK;
+                       spin_unlock_irq(&phba->hbalock);
                        done = 1;
+               }
        }
        if (!done) {
                rc = -EINVAL;
                goto do_prep_failed;
        }
-       if (pmb->mb.un.varCfgPort.sli_mode == 3) {
-               if (!pmb->mb.un.varCfgPort.cMA) {
+       if (pmb->u.mb.un.varCfgPort.sli_mode == 3) {
+               if (!pmb->u.mb.un.varCfgPort.cMA) {
                        rc = -ENXIO;
                        goto do_prep_failed;
                }
-               if (phba->max_vpi && pmb->mb.un.varCfgPort.gmv) {
+               if (phba->max_vpi && pmb->u.mb.un.varCfgPort.gmv) {
                        phba->sli3_options |= LPFC_SLI3_NPIV_ENABLED;
-                       phba->max_vpi = pmb->mb.un.varCfgPort.max_vpi;
+                       phba->max_vpi = pmb->u.mb.un.varCfgPort.max_vpi;
+                       phba->max_vports = (phba->max_vpi > phba->max_vports) ?
+                               phba->max_vpi : phba->max_vports;
+
                } else
                        phba->max_vpi = 0;
-               if (pmb->mb.un.varCfgPort.gerbm)
+               if (pmb->u.mb.un.varCfgPort.gdss)
+                       phba->sli3_options |= LPFC_SLI3_DSS_ENABLED;
+               if (pmb->u.mb.un.varCfgPort.gerbm)
                        phba->sli3_options |= LPFC_SLI3_HBQ_ENABLED;
-               if (pmb->mb.un.varCfgPort.gcrp)
+               if (pmb->u.mb.un.varCfgPort.gcrp)
                        phba->sli3_options |= LPFC_SLI3_CRP_ENABLED;
-               if (pmb->mb.un.varCfgPort.ginb) {
+               if (pmb->u.mb.un.varCfgPort.ginb) {
                        phba->sli3_options |= LPFC_SLI3_INB_ENABLED;
                        phba->hbq_get = phba->mbox->us.s3_inb_pgp.hbq_get;
                        phba->port_gp = phba->mbox->us.s3_inb_pgp.port;
@@ -3089,7 +3970,7 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
                }
 
                if (phba->cfg_enable_bg) {
-                       if (pmb->mb.un.varCfgPort.gbg)
+                       if (pmb->u.mb.un.varCfgPort.gbg)
                                phba->sli3_options |= LPFC_SLI3_BG_ENABLED;
                        else
                                lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
@@ -3184,8 +4065,9 @@ lpfc_sli_hba_setup(struct lpfc_hba *phba)
                if (rc)
                        goto lpfc_sli_hba_setup_error;
        }
-
+       spin_lock_irq(&phba->hbalock);
        phba->sli.sli_flag |= LPFC_PROCESS_LA;
+       spin_unlock_irq(&phba->hbalock);
 
        rc = lpfc_config_port_post(phba);
        if (rc)
@@ -3200,6 +4082,488 @@ lpfc_sli_hba_setup_error:
        return rc;
 }
 
+/**
+ * lpfc_sli4_read_fcoe_params - Read fcoe params from conf region
+ * @phba: Pointer to HBA context object.
+ * @mboxq: mailbox pointer.
+ * This function issue a dump mailbox command to read config region
+ * 23 and parse the records in the region and populate driver
+ * data structure.
+ **/
+static int
+lpfc_sli4_read_fcoe_params(struct lpfc_hba *phba,
+               LPFC_MBOXQ_t *mboxq)
+{
+       struct lpfc_dmabuf *mp;
+       struct lpfc_mqe *mqe;
+       uint32_t data_length;
+       int rc;
+
+       /* Program the default value of vlan_id and fc_map */
+       phba->valid_vlan = 0;
+       phba->fc_map[0] = LPFC_FCOE_FCF_MAP0;
+       phba->fc_map[1] = LPFC_FCOE_FCF_MAP1;
+       phba->fc_map[2] = LPFC_FCOE_FCF_MAP2;
+
+       mqe = &mboxq->u.mqe;
+       if (lpfc_dump_fcoe_param(phba, mboxq))
+               return -ENOMEM;
+
+       mp = (struct lpfc_dmabuf *) mboxq->context1;
+       rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+
+       lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
+                       "(%d):2571 Mailbox cmd x%x Status x%x "
+                       "Data: x%x x%x x%x x%x x%x x%x x%x x%x x%x "
+                       "x%x x%x x%x x%x x%x x%x x%x x%x x%x "
+                       "CQ: x%x x%x x%x x%x\n",
+                       mboxq->vport ? mboxq->vport->vpi : 0,
+                       bf_get(lpfc_mqe_command, mqe),
+                       bf_get(lpfc_mqe_status, mqe),
+                       mqe->un.mb_words[0], mqe->un.mb_words[1],
+                       mqe->un.mb_words[2], mqe->un.mb_words[3],
+                       mqe->un.mb_words[4], mqe->un.mb_words[5],
+                       mqe->un.mb_words[6], mqe->un.mb_words[7],
+                       mqe->un.mb_words[8], mqe->un.mb_words[9],
+                       mqe->un.mb_words[10], mqe->un.mb_words[11],
+                       mqe->un.mb_words[12], mqe->un.mb_words[13],
+                       mqe->un.mb_words[14], mqe->un.mb_words[15],
+                       mqe->un.mb_words[16], mqe->un.mb_words[50],
+                       mboxq->mcqe.word0,
+                       mboxq->mcqe.mcqe_tag0,  mboxq->mcqe.mcqe_tag1,
+                       mboxq->mcqe.trailer);
+
+       if (rc) {
+               lpfc_mbuf_free(phba, mp->virt, mp->phys);
+               kfree(mp);
+               return -EIO;
+       }
+       data_length = mqe->un.mb_words[5];
+       if (data_length > DMP_FCOEPARAM_RGN_SIZE)
+               return -EIO;
+
+       lpfc_parse_fcoe_conf(phba, mp->virt, data_length);
+       lpfc_mbuf_free(phba, mp->virt, mp->phys);
+       kfree(mp);
+       return 0;
+}
+
+/**
+ * lpfc_sli4_read_rev - Issue READ_REV and collect vpd data
+ * @phba: pointer to lpfc hba data structure.
+ * @mboxq: pointer to the LPFC_MBOXQ_t structure.
+ * @vpd: pointer to the memory to hold resulting port vpd data.
+ * @vpd_size: On input, the number of bytes allocated to @vpd.
+ *           On output, the number of data bytes in @vpd.
+ *
+ * This routine executes a READ_REV SLI4 mailbox command.  In
+ * addition, this routine gets the port vpd data.
+ *
+ * Return codes
+ *     0 - sucessful
+ *     ENOMEM - could not allocated memory.
+ **/
+static int
+lpfc_sli4_read_rev(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
+                   uint8_t *vpd, uint32_t *vpd_size)
+{
+       int rc = 0;
+       uint32_t dma_size;
+       struct lpfc_dmabuf *dmabuf;
+       struct lpfc_mqe *mqe;
+
+       dmabuf = kzalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+       if (!dmabuf)
+               return -ENOMEM;
+
+       /*
+        * Get a DMA buffer for the vpd data resulting from the READ_REV
+        * mailbox command.
+        */
+       dma_size = *vpd_size;
+       dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev,
+                                         dma_size,
+                                         &dmabuf->phys,
+                                         GFP_KERNEL);
+       if (!dmabuf->virt) {
+               kfree(dmabuf);
+               return -ENOMEM;
+       }
+       memset(dmabuf->virt, 0, dma_size);
+
+       /*
+        * The SLI4 implementation of READ_REV conflicts at word1,
+        * bits 31:16 and SLI4 adds vpd functionality not present
+        * in SLI3.  This code corrects the conflicts.
+        */
+       lpfc_read_rev(phba, mboxq);
+       mqe = &mboxq->u.mqe;
+       mqe->un.read_rev.vpd_paddr_high = putPaddrHigh(dmabuf->phys);
+       mqe->un.read_rev.vpd_paddr_low = putPaddrLow(dmabuf->phys);
+       mqe->un.read_rev.word1 &= 0x0000FFFF;
+       bf_set(lpfc_mbx_rd_rev_vpd, &mqe->un.read_rev, 1);
+       bf_set(lpfc_mbx_rd_rev_avail_len, &mqe->un.read_rev, dma_size);
+
+       rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+       if (rc) {
+               dma_free_coherent(&phba->pcidev->dev, dma_size,
+                                 dmabuf->virt, dmabuf->phys);
+               return -EIO;
+       }
+
+       lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
+                       "(%d):0380 Mailbox cmd x%x Status x%x "
+                       "Data: x%x x%x x%x x%x x%x x%x x%x x%x x%x "
+                       "x%x x%x x%x x%x x%x x%x x%x x%x x%x "
+                       "CQ: x%x x%x x%x x%x\n",
+                       mboxq->vport ? mboxq->vport->vpi : 0,
+                       bf_get(lpfc_mqe_command, mqe),
+                       bf_get(lpfc_mqe_status, mqe),
+                       mqe->un.mb_words[0], mqe->un.mb_words[1],
+                       mqe->un.mb_words[2], mqe->un.mb_words[3],
+                       mqe->un.mb_words[4], mqe->un.mb_words[5],
+                       mqe->un.mb_words[6], mqe->un.mb_words[7],
+                       mqe->un.mb_words[8], mqe->un.mb_words[9],
+                       mqe->un.mb_words[10], mqe->un.mb_words[11],
+                       mqe->un.mb_words[12], mqe->un.mb_words[13],
+                       mqe->un.mb_words[14], mqe->un.mb_words[15],
+                       mqe->un.mb_words[16], mqe->un.mb_words[50],
+                       mboxq->mcqe.word0,
+                       mboxq->mcqe.mcqe_tag0,  mboxq->mcqe.mcqe_tag1,
+                       mboxq->mcqe.trailer);
+
+       /*
+        * The available vpd length cannot be bigger than the
+        * DMA buffer passed to the port.  Catch the less than
+        * case and update the caller's size.
+        */
+       if (mqe->un.read_rev.avail_vpd_len < *vpd_size)
+               *vpd_size = mqe->un.read_rev.avail_vpd_len;
+
+       lpfc_sli_pcimem_bcopy(dmabuf->virt, vpd, *vpd_size);
+       dma_free_coherent(&phba->pcidev->dev, dma_size,
+                         dmabuf->virt, dmabuf->phys);
+       kfree(dmabuf);
+       return 0;
+}
+
+/**
+ * lpfc_sli4_arm_cqeq_intr - Arm sli-4 device completion and event queues
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is called to explicitly arm the SLI4 device's completion and
+ * event queues
+ **/
+static void
+lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba)
+{
+       uint8_t fcp_eqidx;
+
+       lpfc_sli4_cq_release(phba->sli4_hba.mbx_cq, LPFC_QUEUE_REARM);
+       lpfc_sli4_cq_release(phba->sli4_hba.els_cq, LPFC_QUEUE_REARM);
+       lpfc_sli4_cq_release(phba->sli4_hba.rxq_cq, LPFC_QUEUE_REARM);
+       for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_eq_count; fcp_eqidx++)
+               lpfc_sli4_cq_release(phba->sli4_hba.fcp_cq[fcp_eqidx],
+                                    LPFC_QUEUE_REARM);
+       lpfc_sli4_eq_release(phba->sli4_hba.sp_eq, LPFC_QUEUE_REARM);
+       for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_eq_count; fcp_eqidx++)
+               lpfc_sli4_eq_release(phba->sli4_hba.fp_eq[fcp_eqidx],
+                                    LPFC_QUEUE_REARM);
+}
+
+/**
+ * lpfc_sli4_hba_setup - SLI4 device intialization PCI function
+ * @phba: Pointer to HBA context object.
+ *
+ * This function is the main SLI4 device intialization PCI function. This
+ * function is called by the HBA intialization code, HBA reset code and
+ * HBA error attention handler code. Caller is not required to hold any
+ * locks.
+ **/
+int
+lpfc_sli4_hba_setup(struct lpfc_hba *phba)
+{
+       int rc;
+       LPFC_MBOXQ_t *mboxq;
+       struct lpfc_mqe *mqe;
+       uint8_t *vpd;
+       uint32_t vpd_size;
+       uint32_t ftr_rsp = 0;
+       struct Scsi_Host *shost = lpfc_shost_from_vport(phba->pport);
+       struct lpfc_vport *vport = phba->pport;
+       struct lpfc_dmabuf *mp;
+
+       /* Perform a PCI function reset to start from clean */
+       rc = lpfc_pci_function_reset(phba);
+       if (unlikely(rc))
+               return -ENODEV;
+
+       /* Check the HBA Host Status Register for readyness */
+       rc = lpfc_sli4_post_status_check(phba);
+       if (unlikely(rc))
+               return -ENODEV;
+       else {
+               spin_lock_irq(&phba->hbalock);
+               phba->sli.sli_flag |= LPFC_SLI_ACTIVE;
+               spin_unlock_irq(&phba->hbalock);
+       }
+
+       /*
+        * Allocate a single mailbox container for initializing the
+        * port.
+        */
+       mboxq = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mboxq)
+               return -ENOMEM;
+
+       /*
+        * Continue initialization with default values even if driver failed
+        * to read FCoE param config regions
+        */
+       if (lpfc_sli4_read_fcoe_params(phba, mboxq))
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_INIT,
+                       "2570 Failed to read FCoE parameters \n");
+
+       /* Issue READ_REV to collect vpd and FW information. */
+       vpd_size = PAGE_SIZE;
+       vpd = kzalloc(vpd_size, GFP_KERNEL);
+       if (!vpd) {
+               rc = -ENOMEM;
+               goto out_free_mbox;
+       }
+
+       rc = lpfc_sli4_read_rev(phba, mboxq, vpd, &vpd_size);
+       if (unlikely(rc))
+               goto out_free_vpd;
+
+       mqe = &mboxq->u.mqe;
+       if ((bf_get(lpfc_mbx_rd_rev_sli_lvl,
+                   &mqe->un.read_rev) != LPFC_SLI_REV4) ||
+           (bf_get(lpfc_mbx_rd_rev_fcoe, &mqe->un.read_rev) == 0)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+                       "0376 READ_REV Error. SLI Level %d "
+                       "FCoE enabled %d\n",
+                       bf_get(lpfc_mbx_rd_rev_sli_lvl, &mqe->un.read_rev),
+                       bf_get(lpfc_mbx_rd_rev_fcoe, &mqe->un.read_rev));
+               rc = -EIO;
+               goto out_free_vpd;
+       }
+       /* Single threaded at this point, no need for lock */
+       spin_lock_irq(&phba->hbalock);
+       phba->hba_flag |= HBA_FCOE_SUPPORT;
+       spin_unlock_irq(&phba->hbalock);
+       /*
+        * Evaluate the read rev and vpd data. Populate the driver
+        * state with the results. If this routine fails, the failure
+        * is not fatal as the driver will use generic values.
+        */
+       rc = lpfc_parse_vpd(phba, vpd, vpd_size);
+       if (unlikely(!rc)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+                               "0377 Error %d parsing vpd. "
+                               "Using defaults.\n", rc);
+               rc = 0;
+       }
+
+       /* By now, we should determine the SLI revision, hard code for now */
+       phba->sli_rev = LPFC_SLI_REV4;
+
+       /*
+        * Discover the port's supported feature set and match it against the
+        * hosts requests.
+        */
+       lpfc_request_features(phba, mboxq);
+       rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+       if (unlikely(rc)) {
+               rc = -EIO;
+               goto out_free_vpd;
+       }
+
+       /*
+        * The port must support FCP initiator mode as this is the
+        * only mode running in the host.
+        */
+       if (!(bf_get(lpfc_mbx_rq_ftr_rsp_fcpi, &mqe->un.req_ftrs))) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_SLI,
+                               "0378 No support for fcpi mode.\n");
+               ftr_rsp++;
+       }
+
+       /*
+        * If the port cannot support the host's requested features
+        * then turn off the global config parameters to disable the
+        * feature in the driver.  This is not a fatal error.
+        */
+       if ((phba->cfg_enable_bg) &&
+           !(bf_get(lpfc_mbx_rq_ftr_rsp_dif, &mqe->un.req_ftrs)))
+               ftr_rsp++;
+
+       if (phba->max_vpi && phba->cfg_enable_npiv &&
+           !(bf_get(lpfc_mbx_rq_ftr_rsp_npiv, &mqe->un.req_ftrs)))
+               ftr_rsp++;
+
+       if (ftr_rsp) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_SLI,
+                               "0379 Feature Mismatch Data: x%08x %08x "
+                               "x%x x%x x%x\n", mqe->un.req_ftrs.word2,
+                               mqe->un.req_ftrs.word3, phba->cfg_enable_bg,
+                               phba->cfg_enable_npiv, phba->max_vpi);
+               if (!(bf_get(lpfc_mbx_rq_ftr_rsp_dif, &mqe->un.req_ftrs)))
+                       phba->cfg_enable_bg = 0;
+               if (!(bf_get(lpfc_mbx_rq_ftr_rsp_npiv, &mqe->un.req_ftrs)))
+                       phba->cfg_enable_npiv = 0;
+       }
+
+       /* These SLI3 features are assumed in SLI4 */
+       spin_lock_irq(&phba->hbalock);
+       phba->sli3_options |= (LPFC_SLI3_NPIV_ENABLED | LPFC_SLI3_HBQ_ENABLED);
+       spin_unlock_irq(&phba->hbalock);
+
+       /* Read the port's service parameters. */
+       lpfc_read_sparam(phba, mboxq, vport->vpi);
+       mboxq->vport = vport;
+       rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+       mp = (struct lpfc_dmabuf *) mboxq->context1;
+       if (rc == MBX_SUCCESS) {
+               memcpy(&vport->fc_sparam, mp->virt, sizeof(struct serv_parm));
+               rc = 0;
+       }
+
+       /*
+        * This memory was allocated by the lpfc_read_sparam routine. Release
+        * it to the mbuf pool.
+        */
+       lpfc_mbuf_free(phba, mp->virt, mp->phys);
+       kfree(mp);
+       mboxq->context1 = NULL;
+       if (unlikely(rc)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+                               "0382 READ_SPARAM command failed "
+                               "status %d, mbxStatus x%x\n",
+                               rc, bf_get(lpfc_mqe_status, mqe));
+               phba->link_state = LPFC_HBA_ERROR;
+               rc = -EIO;
+               goto out_free_vpd;
+       }
+
+       if (phba->cfg_soft_wwnn)
+               u64_to_wwn(phba->cfg_soft_wwnn,
+                          vport->fc_sparam.nodeName.u.wwn);
+       if (phba->cfg_soft_wwpn)
+               u64_to_wwn(phba->cfg_soft_wwpn,
+                          vport->fc_sparam.portName.u.wwn);
+       memcpy(&vport->fc_nodename, &vport->fc_sparam.nodeName,
+              sizeof(struct lpfc_name));
+       memcpy(&vport->fc_portname, &vport->fc_sparam.portName,
+              sizeof(struct lpfc_name));
+
+       /* Update the fc_host data structures with new wwn. */
+       fc_host_node_name(shost) = wwn_to_u64(vport->fc_nodename.u.wwn);
+       fc_host_port_name(shost) = wwn_to_u64(vport->fc_portname.u.wwn);
+
+       /* Register SGL pool to the device using non-embedded mailbox command */
+       rc = lpfc_sli4_post_sgl_list(phba);
+       if (unlikely(rc)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+                               "0582 Error %d during sgl post operation", rc);
+               rc = -ENODEV;
+               goto out_free_vpd;
+       }
+
+       /* Register SCSI SGL pool to the device */
+       rc = lpfc_sli4_repost_scsi_sgl_list(phba);
+       if (unlikely(rc)) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_SLI,
+                               "0383 Error %d during scsi sgl post opeation",
+                               rc);
+               /* Some Scsi buffers were moved to the abort scsi list */
+               /* A pci function reset will repost them */
+               rc = -ENODEV;
+               goto out_free_vpd;
+       }
+
+       /* Post the rpi header region to the device. */
+       rc = lpfc_sli4_post_all_rpi_hdrs(phba);
+       if (unlikely(rc)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+                               "0393 Error %d during rpi post operation\n",
+                               rc);
+               rc = -ENODEV;
+               goto out_free_vpd;
+       }
+       /* Temporary initialization of lpfc_fip_flag to non-fip */
+       bf_set(lpfc_fip_flag, &phba->sli4_hba.sli4_flags, 0);
+
+       /* Set up all the queues to the device */
+       rc = lpfc_sli4_queue_setup(phba);
+       if (unlikely(rc)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+                               "0381 Error %d during queue setup.\n ", rc);
+               goto out_stop_timers;
+       }
+
+       /* Arm the CQs and then EQs on device */
+       lpfc_sli4_arm_cqeq_intr(phba);
+
+       /* Indicate device interrupt mode */
+       phba->sli4_hba.intr_enable = 1;
+
+       /* Allow asynchronous mailbox command to go through */
+       spin_lock_irq(&phba->hbalock);
+       phba->sli.sli_flag &= ~LPFC_SLI_ASYNC_MBX_BLK;
+       spin_unlock_irq(&phba->hbalock);
+
+       /* Post receive buffers to the device */
+       lpfc_sli4_rb_setup(phba);
+
+       /* Start the ELS watchdog timer */
+       /*
+        * The driver for SLI4 is not yet ready to process timeouts
+        * or interrupts.  Once it is, the comment bars can be removed.
+        */
+       /* mod_timer(&vport->els_tmofunc,
+        *           jiffies + HZ * (phba->fc_ratov*2)); */
+
+       /* Start heart beat timer */
+       mod_timer(&phba->hb_tmofunc,
+                 jiffies + HZ * LPFC_HB_MBOX_INTERVAL);
+       phba->hb_outstanding = 0;
+       phba->last_completion_time = jiffies;
+
+       /* Start error attention (ERATT) polling timer */
+       mod_timer(&phba->eratt_poll, jiffies + HZ * LPFC_ERATT_POLL_INTERVAL);
+
+       /*
+        * The port is ready, set the host's link state to LINK_DOWN
+        * in preparation for link interrupts.
+        */
+       lpfc_init_link(phba, mboxq, phba->cfg_topology, phba->cfg_link_speed);
+       mboxq->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+       lpfc_set_loopback_flag(phba);
+       /* Change driver state to LPFC_LINK_DOWN right before init link */
+       spin_lock_irq(&phba->hbalock);
+       phba->link_state = LPFC_LINK_DOWN;
+       spin_unlock_irq(&phba->hbalock);
+       rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
+       if (unlikely(rc != MBX_NOT_FINISHED)) {
+               kfree(vpd);
+               return 0;
+       } else
+               rc = -EIO;
+
+       /* Unset all the queues set up in this routine when error out */
+       if (rc)
+               lpfc_sli4_queue_unset(phba);
+
+out_stop_timers:
+       if (rc)
+               lpfc_stop_hba_timers(phba);
+out_free_vpd:
+       kfree(vpd);
+out_free_mbox:
+       mempool_free(mboxq, phba->mbox_mem_pool);
+       return rc;
+}
 
 /**
  * lpfc_mbox_timeout - Timeout call back function for mbox timer
@@ -3244,7 +4608,7 @@ void
 lpfc_mbox_timeout_handler(struct lpfc_hba *phba)
 {
        LPFC_MBOXQ_t *pmbox = phba->sli.mbox_active;
-       MAILBOX_t *mb = &pmbox->mb;
+       MAILBOX_t *mb = &pmbox->u.mb;
        struct lpfc_sli *psli = &phba->sli;
        struct lpfc_sli_ring *pring;
 
@@ -3281,7 +4645,7 @@ lpfc_mbox_timeout_handler(struct lpfc_hba *phba)
        spin_unlock_irq(&phba->pport->work_port_lock);
        spin_lock_irq(&phba->hbalock);
        phba->link_state = LPFC_LINK_UNKNOWN;
-       psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
+       psli->sli_flag &= ~LPFC_SLI_ACTIVE;
        spin_unlock_irq(&phba->hbalock);
 
        pring = &psli->ring[psli->fcp_ring];
@@ -3289,32 +4653,20 @@ lpfc_mbox_timeout_handler(struct lpfc_hba *phba)
 
        lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
                        "0345 Resetting board due to mailbox timeout\n");
-       /*
-        * lpfc_offline calls lpfc_sli_hba_down which will clean up
-        * on oustanding mailbox commands.
-        */
-       /* If resets are disabled then set error state and return. */
-       if (!phba->cfg_enable_hba_reset) {
-               phba->link_state = LPFC_HBA_ERROR;
-               return;
-       }
-       lpfc_offline_prep(phba);
-       lpfc_offline(phba);
-       lpfc_sli_brdrestart(phba);
-       lpfc_online(phba);
-       lpfc_unblock_mgmt_io(phba);
-       return;
+
+       /* Reset the HBA device */
+       lpfc_reset_hba(phba);
 }
 
 /**
- * lpfc_sli_issue_mbox - Issue a mailbox command to firmware
+ * lpfc_sli_issue_mbox_s3 - Issue an SLI3 mailbox command to firmware
  * @phba: Pointer to HBA context object.
  * @pmbox: Pointer to mailbox object.
  * @flag: Flag indicating how the mailbox need to be processed.
  *
  * This function is called by discovery code and HBA management code
- * to submit a mailbox command to firmware. This function gets the
- * hbalock to protect the data structures.
+ * to submit a mailbox command to firmware with SLI-3 interface spec. This
+ * function gets the hbalock to protect the data structures.
  * The mailbox command can be submitted in polling mode, in which case
  * this function will wait in a polling loop for the completion of the
  * mailbox.
@@ -3332,8 +4684,9 @@ lpfc_mbox_timeout_handler(struct lpfc_hba *phba)
  * return codes the caller owns the mailbox command after the return of
  * the function.
  **/
-int
-lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
+static int
+lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox,
+                      uint32_t flag)
 {
        MAILBOX_t *mb;
        struct lpfc_sli *psli = &phba->sli;
@@ -3349,8 +4702,12 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
        spin_lock_irqsave(&phba->hbalock, drvr_flag);
        if (!pmbox) {
                /* processing mbox queue from intr_handler */
-               processing_queue = 1;
-               phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+               if (unlikely(psli->sli_flag & LPFC_SLI_ASYNC_MBX_BLK)) {
+                       spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
+                       return MBX_SUCCESS;
+               }
+               processing_queue = 1;
+               phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
                pmbox = lpfc_mbox_get(phba);
                if (!pmbox) {
                        spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
@@ -3365,7 +4722,7 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
                        lpfc_printf_log(phba, KERN_ERR,
                                        LOG_MBOX | LOG_VPORT,
                                        "1806 Mbox x%x failed. No vport\n",
-                                       pmbox->mb.mbxCommand);
+                                       pmbox->u.mb.mbxCommand);
                        dump_stack();
                        goto out_not_finished;
                }
@@ -3385,21 +4742,29 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
 
        psli = &phba->sli;
 
-       mb = &pmbox->mb;
+       mb = &pmbox->u.mb;
        status = MBX_SUCCESS;
 
        if (phba->link_state == LPFC_HBA_ERROR) {
                spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
 
                /* Mbox command <mbxCommand> cannot issue */
-               LOG_MBOX_CANNOT_ISSUE_DATA(phba, pmbox, psli, flag);
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+                               "(%d):0311 Mailbox command x%x cannot "
+                               "issue Data: x%x x%x\n",
+                               pmbox->vport ? pmbox->vport->vpi : 0,
+                               pmbox->u.mb.mbxCommand, psli->sli_flag, flag);
                goto out_not_finished;
        }
 
        if (mb->mbxCommand != MBX_KILL_BOARD && flag & MBX_NOWAIT &&
            !(readl(phba->HCregaddr) & HC_MBINT_ENA)) {
                spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
-               LOG_MBOX_CANNOT_ISSUE_DATA(phba, pmbox, psli, flag);
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+                               "(%d):2528 Mailbox command x%x cannot "
+                               "issue Data: x%x x%x\n",
+                               pmbox->vport ? pmbox->vport->vpi : 0,
+                               pmbox->u.mb.mbxCommand, psli->sli_flag, flag);
                goto out_not_finished;
        }
 
@@ -3413,14 +4778,24 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
                        spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
 
                        /* Mbox command <mbxCommand> cannot issue */
-                       LOG_MBOX_CANNOT_ISSUE_DATA(phba, pmbox, psli, flag);
+                       lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+                                       "(%d):2529 Mailbox command x%x "
+                                       "cannot issue Data: x%x x%x\n",
+                                       pmbox->vport ? pmbox->vport->vpi : 0,
+                                       pmbox->u.mb.mbxCommand,
+                                       psli->sli_flag, flag);
                        goto out_not_finished;
                }
 
-               if (!(psli->sli_flag & LPFC_SLI2_ACTIVE)) {
+               if (!(psli->sli_flag & LPFC_SLI_ACTIVE)) {
                        spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
                        /* Mbox command <mbxCommand> cannot issue */
-                       LOG_MBOX_CANNOT_ISSUE_DATA(phba, pmbox, psli, flag);
+                       lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+                                       "(%d):2530 Mailbox command x%x "
+                                       "cannot issue Data: x%x x%x\n",
+                                       pmbox->vport ? pmbox->vport->vpi : 0,
+                                       pmbox->u.mb.mbxCommand,
+                                       psli->sli_flag, flag);
                        goto out_not_finished;
                }
 
@@ -3462,12 +4837,17 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
 
        /* If we are not polling, we MUST be in SLI2 mode */
        if (flag != MBX_POLL) {
-               if (!(psli->sli_flag & LPFC_SLI2_ACTIVE) &&
+               if (!(psli->sli_flag & LPFC_SLI_ACTIVE) &&
                    (mb->mbxCommand != MBX_KILL_BOARD)) {
                        psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
                        spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
                        /* Mbox command <mbxCommand> cannot issue */
-                       LOG_MBOX_CANNOT_ISSUE_DATA(phba, pmbox, psli, flag);
+                       lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+                                       "(%d):2531 Mailbox command x%x "
+                                       "cannot issue Data: x%x x%x\n",
+                                       pmbox->vport ? pmbox->vport->vpi : 0,
+                                       pmbox->u.mb.mbxCommand,
+                                       psli->sli_flag, flag);
                        goto out_not_finished;
                }
                /* timeout active mbox command */
@@ -3506,7 +4886,7 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
        /* next set own bit for the adapter and copy over command word */
        mb->mbxOwner = OWN_CHIP;
 
-       if (psli->sli_flag & LPFC_SLI2_ACTIVE) {
+       if (psli->sli_flag & LPFC_SLI_ACTIVE) {
                /* First copy command data to host SLIM area */
                lpfc_sli_pcimem_bcopy(mb, phba->mbox, MAILBOX_CMD_SIZE);
        } else {
@@ -3529,7 +4909,7 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
 
                if (mb->mbxCommand == MBX_CONFIG_PORT) {
                        /* switch over to host mailbox */
-                       psli->sli_flag |= LPFC_SLI2_ACTIVE;
+                       psli->sli_flag |= LPFC_SLI_ACTIVE;
                }
        }
 
@@ -3552,7 +4932,7 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
                writel(CA_MBATT, phba->CAregaddr);
                readl(phba->CAregaddr); /* flush */
 
-               if (psli->sli_flag & LPFC_SLI2_ACTIVE) {
+               if (psli->sli_flag & LPFC_SLI_ACTIVE) {
                        /* First read mbox status word */
                        word0 = *((uint32_t *)phba->mbox);
                        word0 = le32_to_cpu(word0);
@@ -3591,7 +4971,7 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
                                spin_lock_irqsave(&phba->hbalock, drvr_flag);
                        }
 
-                       if (psli->sli_flag & LPFC_SLI2_ACTIVE) {
+                       if (psli->sli_flag & LPFC_SLI_ACTIVE) {
                                /* First copy command data */
                                word0 = *((uint32_t *)phba->mbox);
                                word0 = le32_to_cpu(word0);
@@ -3604,7 +4984,7 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
                                        if (((slimword0 & OWN_CHIP) != OWN_CHIP)
                                            && slimmb->mbxStatus) {
                                                psli->sli_flag &=
-                                                   ~LPFC_SLI2_ACTIVE;
+                                                   ~LPFC_SLI_ACTIVE;
                                                word0 = slimword0;
                                        }
                                }
@@ -3616,7 +4996,7 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
                        ha_copy = readl(phba->HAregaddr);
                }
 
-               if (psli->sli_flag & LPFC_SLI2_ACTIVE) {
+               if (psli->sli_flag & LPFC_SLI_ACTIVE) {
                        /* copy results back to user */
                        lpfc_sli_pcimem_bcopy(phba->mbox, mb, MAILBOX_CMD_SIZE);
                } else {
@@ -3643,12 +5023,419 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
 
 out_not_finished:
        if (processing_queue) {
-               pmbox->mb.mbxStatus = MBX_NOT_FINISHED;
+               pmbox->u.mb.mbxStatus = MBX_NOT_FINISHED;
                lpfc_mbox_cmpl_put(phba, pmbox);
        }
        return MBX_NOT_FINISHED;
 }
 
+/**
+ * lpfc_sli4_post_sync_mbox - Post an SLI4 mailbox to the bootstrap mailbox
+ * @phba: Pointer to HBA context object.
+ * @mboxq: Pointer to mailbox object.
+ *
+ * The function posts a mailbox to the port.  The mailbox is expected
+ * to be comletely filled in and ready for the port to operate on it.
+ * This routine executes a synchronous completion operation on the
+ * mailbox by polling for its completion.
+ *
+ * The caller must not be holding any locks when calling this routine.
+ *
+ * Returns:
+ *     MBX_SUCCESS - mailbox posted successfully
+ *     Any of the MBX error values.
+ **/
+static int
+lpfc_sli4_post_sync_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+       int rc = MBX_SUCCESS;
+       unsigned long iflag;
+       uint32_t db_ready;
+       uint32_t mcqe_status;
+       uint32_t mbx_cmnd;
+       unsigned long timeout;
+       struct lpfc_sli *psli = &phba->sli;
+       struct lpfc_mqe *mb = &mboxq->u.mqe;
+       struct lpfc_bmbx_create *mbox_rgn;
+       struct dma_address *dma_address;
+       struct lpfc_register bmbx_reg;
+
+       /*
+        * Only one mailbox can be active to the bootstrap mailbox region
+        * at a time and there is no queueing provided.
+        */
+       spin_lock_irqsave(&phba->hbalock, iflag);
+       if (psli->sli_flag & LPFC_SLI_MBOX_ACTIVE) {
+               spin_unlock_irqrestore(&phba->hbalock, iflag);
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+                               "(%d):2532 Mailbox command x%x (x%x) "
+                               "cannot issue Data: x%x x%x\n",
+                               mboxq->vport ? mboxq->vport->vpi : 0,
+                               mboxq->u.mb.mbxCommand,
+                               lpfc_sli4_mbox_opcode_get(phba, mboxq),
+                               psli->sli_flag, MBX_POLL);
+               return MBXERR_ERROR;
+       }
+       /* The server grabs the token and owns it until release */
+       psli->sli_flag |= LPFC_SLI_MBOX_ACTIVE;
+       phba->sli.mbox_active = mboxq;
+       spin_unlock_irqrestore(&phba->hbalock, iflag);
+
+       /*
+        * Initialize the bootstrap memory region to avoid stale data areas
+        * in the mailbox post.  Then copy the caller's mailbox contents to
+        * the bmbx mailbox region.
+        */
+       mbx_cmnd = bf_get(lpfc_mqe_command, mb);
+       memset(phba->sli4_hba.bmbx.avirt, 0, sizeof(struct lpfc_bmbx_create));
+       lpfc_sli_pcimem_bcopy(mb, phba->sli4_hba.bmbx.avirt,
+                             sizeof(struct lpfc_mqe));
+
+       /* Post the high mailbox dma address to the port and wait for ready. */
+       dma_address = &phba->sli4_hba.bmbx.dma_address;
+       writel(dma_address->addr_hi, phba->sli4_hba.BMBXregaddr);
+
+       timeout = msecs_to_jiffies(lpfc_mbox_tmo_val(phba, mbx_cmnd)
+                                  * 1000) + jiffies;
+       do {
+               bmbx_reg.word0 = readl(phba->sli4_hba.BMBXregaddr);
+               db_ready = bf_get(lpfc_bmbx_rdy, &bmbx_reg);
+               if (!db_ready)
+                       msleep(2);
+
+               if (time_after(jiffies, timeout)) {
+                       rc = MBXERR_ERROR;
+                       goto exit;
+               }
+       } while (!db_ready);
+
+       /* Post the low mailbox dma address to the port. */
+       writel(dma_address->addr_lo, phba->sli4_hba.BMBXregaddr);
+       timeout = msecs_to_jiffies(lpfc_mbox_tmo_val(phba, mbx_cmnd)
+                                  * 1000) + jiffies;
+       do {
+               bmbx_reg.word0 = readl(phba->sli4_hba.BMBXregaddr);
+               db_ready = bf_get(lpfc_bmbx_rdy, &bmbx_reg);
+               if (!db_ready)
+                       msleep(2);
+
+               if (time_after(jiffies, timeout)) {
+                       rc = MBXERR_ERROR;
+                       goto exit;
+               }
+       } while (!db_ready);
+
+       /*
+        * Read the CQ to ensure the mailbox has completed.
+        * If so, update the mailbox status so that the upper layers
+        * can complete the request normally.
+        */
+       lpfc_sli_pcimem_bcopy(phba->sli4_hba.bmbx.avirt, mb,
+                             sizeof(struct lpfc_mqe));
+       mbox_rgn = (struct lpfc_bmbx_create *) phba->sli4_hba.bmbx.avirt;
+       lpfc_sli_pcimem_bcopy(&mbox_rgn->mcqe, &mboxq->mcqe,
+                             sizeof(struct lpfc_mcqe));
+       mcqe_status = bf_get(lpfc_mcqe_status, &mbox_rgn->mcqe);
+
+       /* Prefix the mailbox status with range x4000 to note SLI4 status. */
+       if (mcqe_status != MB_CQE_STATUS_SUCCESS) {
+               bf_set(lpfc_mqe_status, mb, LPFC_MBX_ERROR_RANGE | mcqe_status);
+               rc = MBXERR_ERROR;
+       }
+
+       lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
+                       "(%d):0356 Mailbox cmd x%x (x%x) Status x%x "
+                       "Data: x%x x%x x%x x%x x%x x%x x%x x%x x%x x%x x%x"
+                       " x%x x%x CQ: x%x x%x x%x x%x\n",
+                       mboxq->vport ? mboxq->vport->vpi : 0,
+                       mbx_cmnd, lpfc_sli4_mbox_opcode_get(phba, mboxq),
+                       bf_get(lpfc_mqe_status, mb),
+                       mb->un.mb_words[0], mb->un.mb_words[1],
+                       mb->un.mb_words[2], mb->un.mb_words[3],
+                       mb->un.mb_words[4], mb->un.mb_words[5],
+                       mb->un.mb_words[6], mb->un.mb_words[7],
+                       mb->un.mb_words[8], mb->un.mb_words[9],
+                       mb->un.mb_words[10], mb->un.mb_words[11],
+                       mb->un.mb_words[12], mboxq->mcqe.word0,
+                       mboxq->mcqe.mcqe_tag0,  mboxq->mcqe.mcqe_tag1,
+                       mboxq->mcqe.trailer);
+exit:
+       /* We are holding the token, no needed for lock when release */
+       spin_lock_irqsave(&phba->hbalock, iflag);
+       psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+       phba->sli.mbox_active = NULL;
+       spin_unlock_irqrestore(&phba->hbalock, iflag);
+       return rc;
+}
+
+/**
+ * lpfc_sli_issue_mbox_s4 - Issue an SLI4 mailbox command to firmware
+ * @phba: Pointer to HBA context object.
+ * @pmbox: Pointer to mailbox object.
+ * @flag: Flag indicating how the mailbox need to be processed.
+ *
+ * This function is called by discovery code and HBA management code to submit
+ * a mailbox command to firmware with SLI-4 interface spec.
+ *
+ * Return codes the caller owns the mailbox command after the return of the
+ * function.
+ **/
+static int
+lpfc_sli_issue_mbox_s4(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
+                      uint32_t flag)
+{
+       struct lpfc_sli *psli = &phba->sli;
+       unsigned long iflags;
+       int rc;
+
+       /* Detect polling mode and jump to a handler */
+       if (!phba->sli4_hba.intr_enable) {
+               if (flag == MBX_POLL)
+                       rc = lpfc_sli4_post_sync_mbox(phba, mboxq);
+               else
+                       rc = -EIO;
+               if (rc != MBX_SUCCESS)
+                       lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+                                       "(%d):2541 Mailbox command x%x "
+                                       "(x%x) cannot issue Data: x%x x%x\n",
+                                       mboxq->vport ? mboxq->vport->vpi : 0,
+                                       mboxq->u.mb.mbxCommand,
+                                       lpfc_sli4_mbox_opcode_get(phba, mboxq),
+                                       psli->sli_flag, flag);
+               return rc;
+       } else if (flag == MBX_POLL) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+                               "(%d):2542 Mailbox command x%x (x%x) "
+                               "cannot issue Data: x%x x%x\n",
+                               mboxq->vport ? mboxq->vport->vpi : 0,
+                               mboxq->u.mb.mbxCommand,
+                               lpfc_sli4_mbox_opcode_get(phba, mboxq),
+                               psli->sli_flag, flag);
+               return -EIO;
+       }
+
+       /* Now, interrupt mode asynchrous mailbox command */
+       rc = lpfc_mbox_cmd_check(phba, mboxq);
+       if (rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+                               "(%d):2543 Mailbox command x%x (x%x) "
+                               "cannot issue Data: x%x x%x\n",
+                               mboxq->vport ? mboxq->vport->vpi : 0,
+                               mboxq->u.mb.mbxCommand,
+                               lpfc_sli4_mbox_opcode_get(phba, mboxq),
+                               psli->sli_flag, flag);
+               goto out_not_finished;
+       }
+       rc = lpfc_mbox_dev_check(phba);
+       if (unlikely(rc)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+                               "(%d):2544 Mailbox command x%x (x%x) "
+                               "cannot issue Data: x%x x%x\n",
+                               mboxq->vport ? mboxq->vport->vpi : 0,
+                               mboxq->u.mb.mbxCommand,
+                               lpfc_sli4_mbox_opcode_get(phba, mboxq),
+                               psli->sli_flag, flag);
+               goto out_not_finished;
+       }
+
+       /* Put the mailbox command to the driver internal FIFO */
+       psli->slistat.mbox_busy++;
+       spin_lock_irqsave(&phba->hbalock, iflags);
+       lpfc_mbox_put(phba, mboxq);
+       spin_unlock_irqrestore(&phba->hbalock, iflags);
+       lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
+                       "(%d):0354 Mbox cmd issue - Enqueue Data: "
+                       "x%x (x%x) x%x x%x x%x\n",
+                       mboxq->vport ? mboxq->vport->vpi : 0xffffff,
+                       bf_get(lpfc_mqe_command, &mboxq->u.mqe),
+                       lpfc_sli4_mbox_opcode_get(phba, mboxq),
+                       phba->pport->port_state,
+                       psli->sli_flag, MBX_NOWAIT);
+       /* Wake up worker thread to transport mailbox command from head */
+       lpfc_worker_wake_up(phba);
+
+       return MBX_BUSY;
+
+out_not_finished:
+       return MBX_NOT_FINISHED;
+}
+
+/**
+ * lpfc_sli4_post_async_mbox - Post an SLI4 mailbox command to device
+ * @phba: Pointer to HBA context object.
+ *
+ * This function is called by worker thread to send a mailbox command to
+ * SLI4 HBA firmware.
+ *
+ **/
+int
+lpfc_sli4_post_async_mbox(struct lpfc_hba *phba)
+{
+       struct lpfc_sli *psli = &phba->sli;
+       LPFC_MBOXQ_t *mboxq;
+       int rc = MBX_SUCCESS;
+       unsigned long iflags;
+       struct lpfc_mqe *mqe;
+       uint32_t mbx_cmnd;
+
+       /* Check interrupt mode before post async mailbox command */
+       if (unlikely(!phba->sli4_hba.intr_enable))
+               return MBX_NOT_FINISHED;
+
+       /* Check for mailbox command service token */
+       spin_lock_irqsave(&phba->hbalock, iflags);
+       if (unlikely(psli->sli_flag & LPFC_SLI_ASYNC_MBX_BLK)) {
+               spin_unlock_irqrestore(&phba->hbalock, iflags);
+               return MBX_NOT_FINISHED;
+       }
+       if (psli->sli_flag & LPFC_SLI_MBOX_ACTIVE) {
+               spin_unlock_irqrestore(&phba->hbalock, iflags);
+               return MBX_NOT_FINISHED;
+       }
+       if (unlikely(phba->sli.mbox_active)) {
+               spin_unlock_irqrestore(&phba->hbalock, iflags);
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+                               "0384 There is pending active mailbox cmd\n");
+               return MBX_NOT_FINISHED;
+       }
+       /* Take the mailbox command service token */
+       psli->sli_flag |= LPFC_SLI_MBOX_ACTIVE;
+
+       /* Get the next mailbox command from head of queue */
+       mboxq = lpfc_mbox_get(phba);
+
+       /* If no more mailbox command waiting for post, we're done */
+       if (!mboxq) {
+               psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+               spin_unlock_irqrestore(&phba->hbalock, iflags);
+               return MBX_SUCCESS;
+       }
+       phba->sli.mbox_active = mboxq;
+       spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+       /* Check device readiness for posting mailbox command */
+       rc = lpfc_mbox_dev_check(phba);
+       if (unlikely(rc))
+               /* Driver clean routine will clean up pending mailbox */
+               goto out_not_finished;
+
+       /* Prepare the mbox command to be posted */
+       mqe = &mboxq->u.mqe;
+       mbx_cmnd = bf_get(lpfc_mqe_command, mqe);
+
+       /* Start timer for the mbox_tmo and log some mailbox post messages */
+       mod_timer(&psli->mbox_tmo, (jiffies +
+                 (HZ * lpfc_mbox_tmo_val(phba, mbx_cmnd))));
+
+       lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
+                       "(%d):0355 Mailbox cmd x%x (x%x) issue Data: "
+                       "x%x x%x\n",
+                       mboxq->vport ? mboxq->vport->vpi : 0, mbx_cmnd,
+                       lpfc_sli4_mbox_opcode_get(phba, mboxq),
+                       phba->pport->port_state, psli->sli_flag);
+
+       if (mbx_cmnd != MBX_HEARTBEAT) {
+               if (mboxq->vport) {
+                       lpfc_debugfs_disc_trc(mboxq->vport,
+                               LPFC_DISC_TRC_MBOX_VPORT,
+                               "MBOX Send vport: cmd:x%x mb:x%x x%x",
+                               mbx_cmnd, mqe->un.mb_words[0],
+                               mqe->un.mb_words[1]);
+               } else {
+                       lpfc_debugfs_disc_trc(phba->pport,
+                               LPFC_DISC_TRC_MBOX,
+                               "MBOX Send: cmd:x%x mb:x%x x%x",
+                               mbx_cmnd, mqe->un.mb_words[0],
+                               mqe->un.mb_words[1]);
+               }
+       }
+       psli->slistat.mbox_cmd++;
+
+       /* Post the mailbox command to the port */
+       rc = lpfc_sli4_mq_put(phba->sli4_hba.mbx_wq, mqe);
+       if (rc != MBX_SUCCESS) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+                               "(%d):2533 Mailbox command x%x (x%x) "
+                               "cannot issue Data: x%x x%x\n",
+                               mboxq->vport ? mboxq->vport->vpi : 0,
+                               mboxq->u.mb.mbxCommand,
+                               lpfc_sli4_mbox_opcode_get(phba, mboxq),
+                               psli->sli_flag, MBX_NOWAIT);
+               goto out_not_finished;
+       }
+
+       return rc;
+
+out_not_finished:
+       spin_lock_irqsave(&phba->hbalock, iflags);
+       mboxq->u.mb.mbxStatus = MBX_NOT_FINISHED;
+       __lpfc_mbox_cmpl_put(phba, mboxq);
+       /* Release the token */
+       psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+       phba->sli.mbox_active = NULL;
+       spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+       return MBX_NOT_FINISHED;
+}
+
+/**
+ * lpfc_sli_issue_mbox - Wrapper func for issuing mailbox command
+ * @phba: Pointer to HBA context object.
+ * @pmbox: Pointer to mailbox object.
+ * @flag: Flag indicating how the mailbox need to be processed.
+ *
+ * This routine wraps the actual SLI3 or SLI4 mailbox issuing routine from
+ * the API jump table function pointer from the lpfc_hba struct.
+ *
+ * Return codes the caller owns the mailbox command after the return of the
+ * function.
+ **/
+int
+lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag)
+{
+       return phba->lpfc_sli_issue_mbox(phba, pmbox, flag);
+}
+
+/**
+ * lpfc_mbox_api_table_setup - Set up mbox api fucntion jump table
+ * @phba: The hba struct for which this call is being executed.
+ * @dev_grp: The HBA PCI-Device group number.
+ *
+ * This routine sets up the mbox interface API function jump table in @phba
+ * struct.
+ * Returns: 0 - success, -ENODEV - failure.
+ **/
+int
+lpfc_mbox_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
+{
+
+       switch (dev_grp) {
+       case LPFC_PCI_DEV_LP:
+               phba->lpfc_sli_issue_mbox = lpfc_sli_issue_mbox_s3;
+               phba->lpfc_sli_handle_slow_ring_event =
+                               lpfc_sli_handle_slow_ring_event_s3;
+               phba->lpfc_sli_hbq_to_firmware = lpfc_sli_hbq_to_firmware_s3;
+               phba->lpfc_sli_brdrestart = lpfc_sli_brdrestart_s3;
+               phba->lpfc_sli_brdready = lpfc_sli_brdready_s3;
+               break;
+       case LPFC_PCI_DEV_OC:
+               phba->lpfc_sli_issue_mbox = lpfc_sli_issue_mbox_s4;
+               phba->lpfc_sli_handle_slow_ring_event =
+                               lpfc_sli_handle_slow_ring_event_s4;
+               phba->lpfc_sli_hbq_to_firmware = lpfc_sli_hbq_to_firmware_s4;
+               phba->lpfc_sli_brdrestart = lpfc_sli_brdrestart_s4;
+               phba->lpfc_sli_brdready = lpfc_sli_brdready_s4;
+               break;
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1420 Invalid HBA PCI-device group: 0x%x\n",
+                               dev_grp);
+               return -ENODEV;
+               break;
+       }
+       return 0;
+}
+
 /**
  * __lpfc_sli_ringtx_put - Add an iocb to the txq
  * @phba: Pointer to HBA context object.
@@ -3701,35 +5488,34 @@ lpfc_sli_next_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
 }
 
 /**
- * __lpfc_sli_issue_iocb - Lockless version of lpfc_sli_issue_iocb
+ * __lpfc_sli_issue_iocb_s3 - SLI3 device lockless ver of lpfc_sli_issue_iocb
  * @phba: Pointer to HBA context object.
- * @pring: Pointer to driver SLI ring object.
+ * @ring_number: SLI ring number to issue iocb on.
  * @piocb: Pointer to command iocb.
  * @flag: Flag indicating if this command can be put into txq.
  *
- * __lpfc_sli_issue_iocb is used by other functions in the driver
- * to issue an iocb command to the HBA. If the PCI slot is recovering
- * from error state or if HBA is resetting or if LPFC_STOP_IOCB_EVENT
- * flag is turned on, the function returns IOCB_ERROR.
- * When the link is down, this function allows only iocbs for
- * posting buffers.
- * This function finds next available slot in the command ring and
- * posts the command to the available slot and writes the port
- * attention register to request HBA start processing new iocb.
- * If there is no slot available in the ring and
- * flag & SLI_IOCB_RET_IOCB is set, the new iocb is added to the
- * txq, otherwise the function returns IOCB_BUSY.
- *
- * This function is called with hbalock held.
- * The function will return success after it successfully submit the
- * iocb to firmware or after adding to the txq.
+ * __lpfc_sli_issue_iocb_s3 is used by other functions in the driver to issue
+ * an iocb command to an HBA with SLI-3 interface spec. If the PCI slot is
+ * recovering from error state, if HBA is resetting or if LPFC_STOP_IOCB_EVENT
+ * flag is turned on, the function returns IOCB_ERROR. When the link is down,
+ * this function allows only iocbs for posting buffers. This function finds
+ * next available slot in the command ring and posts the command to the
+ * available slot and writes the port attention register to request HBA start
+ * processing new iocb. If there is no slot available in the ring and
+ * flag & SLI_IOCB_RET_IOCB is set, the new iocb is added to the txq, otherwise
+ * the function returns IOCB_BUSY.
+ *
+ * This function is called with hbalock held. The function will return success
+ * after it successfully submit the iocb to firmware or after adding to the
+ * txq.
  **/
 static int
-__lpfc_sli_issue_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+__lpfc_sli_issue_iocb_s3(struct lpfc_hba *phba, uint32_t ring_number,
                    struct lpfc_iocbq *piocb, uint32_t flag)
 {
        struct lpfc_iocbq *nextiocb;
        IOCB_t *iocb;
+       struct lpfc_sli_ring *pring = &phba->sli.ring[ring_number];
 
        if (piocb->iocb_cmpl && (!piocb->vport) &&
           (piocb->iocb.ulpCommand != CMD_ABORT_XRI_CN) &&
@@ -3833,74 +5619,566 @@ __lpfc_sli_issue_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
        return IOCB_BUSY;
 }
 
+/**
+ * lpfc_sli4_bpl2sgl - Convert the bpl/bde to a sgl.
+ * @phba: Pointer to HBA context object.
+ * @piocb: Pointer to command iocb.
+ * @sglq: Pointer to the scatter gather queue object.
+ *
+ * This routine converts the bpl or bde that is in the IOCB
+ * to a sgl list for the sli4 hardware. The physical address
+ * of the bpl/bde is converted back to a virtual address.
+ * If the IOCB contains a BPL then the list of BDE's is
+ * converted to sli4_sge's. If the IOCB contains a single
+ * BDE then it is converted to a single sli_sge.
+ * The IOCB is still in cpu endianess so the contents of
+ * the bpl can be used without byte swapping.
+ *
+ * Returns valid XRI = Success, NO_XRI = Failure.
+**/
+static uint16_t
+lpfc_sli4_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq,
+               struct lpfc_sglq *sglq)
+{
+       uint16_t xritag = NO_XRI;
+       struct ulp_bde64 *bpl = NULL;
+       struct ulp_bde64 bde;
+       struct sli4_sge *sgl  = NULL;
+       IOCB_t *icmd;
+       int numBdes = 0;
+       int i = 0;
+
+       if (!piocbq || !sglq)
+               return xritag;
+
+       sgl  = (struct sli4_sge *)sglq->sgl;
+       icmd = &piocbq->iocb;
+       if (icmd->un.genreq64.bdl.bdeFlags == BUFF_TYPE_BLP_64) {
+               numBdes = icmd->un.genreq64.bdl.bdeSize /
+                               sizeof(struct ulp_bde64);
+               /* The addrHigh and addrLow fields within the IOCB
+                * have not been byteswapped yet so there is no
+                * need to swap them back.
+                */
+               bpl  = (struct ulp_bde64 *)
+                       ((struct lpfc_dmabuf *)piocbq->context3)->virt;
+
+               if (!bpl)
+                       return xritag;
+
+               for (i = 0; i < numBdes; i++) {
+                       /* Should already be byte swapped. */
+                       sgl->addr_hi =  bpl->addrHigh;
+                       sgl->addr_lo =  bpl->addrLow;
+                       /* swap the size field back to the cpu so we
+                        * can assign it to the sgl.
+                        */
+                       bde.tus.w  = le32_to_cpu(bpl->tus.w);
+                       bf_set(lpfc_sli4_sge_len, sgl, bde.tus.f.bdeSize);
+                       if ((i+1) == numBdes)
+                               bf_set(lpfc_sli4_sge_last, sgl, 1);
+                       else
+                               bf_set(lpfc_sli4_sge_last, sgl, 0);
+                       sgl->word2 = cpu_to_le32(sgl->word2);
+                       sgl->word3 = cpu_to_le32(sgl->word3);
+                       bpl++;
+                       sgl++;
+               }
+       } else if (icmd->un.genreq64.bdl.bdeFlags == BUFF_TYPE_BDE_64) {
+                       /* The addrHigh and addrLow fields of the BDE have not
+                        * been byteswapped yet so they need to be swapped
+                        * before putting them in the sgl.
+                        */
+                       sgl->addr_hi =
+                               cpu_to_le32(icmd->un.genreq64.bdl.addrHigh);
+                       sgl->addr_lo =
+                               cpu_to_le32(icmd->un.genreq64.bdl.addrLow);
+                       bf_set(lpfc_sli4_sge_len, sgl,
+                               icmd->un.genreq64.bdl.bdeSize);
+                       bf_set(lpfc_sli4_sge_last, sgl, 1);
+                       sgl->word2 = cpu_to_le32(sgl->word2);
+                       sgl->word3 = cpu_to_le32(sgl->word3);
+       }
+       return sglq->sli4_xritag;
+}
 
 /**
- * lpfc_sli_issue_iocb - Wrapper function for __lpfc_sli_issue_iocb
+ * lpfc_sli4_scmd_to_wqidx_distr - scsi command to SLI4 WQ index distribution
  * @phba: Pointer to HBA context object.
- * @pring: Pointer to driver SLI ring object.
  * @piocb: Pointer to command iocb.
- * @flag: Flag indicating if this command can be put into txq.
  *
- * lpfc_sli_issue_iocb is a wrapper around __lpfc_sli_issue_iocb
- * function. This function gets the hbalock and calls
- * __lpfc_sli_issue_iocb function and will return the error returned
- * by __lpfc_sli_issue_iocb function. This wrapper is used by
- * functions which do not hold hbalock.
+ * This routine performs a round robin SCSI command to SLI4 FCP WQ index
+ * distribution.
+ *
+ * Return: index into SLI4 fast-path FCP queue index.
  **/
-int
-lpfc_sli_issue_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
-                   struct lpfc_iocbq *piocb, uint32_t flag)
+static uint32_t
+lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba, struct lpfc_iocbq *piocb)
 {
-       unsigned long iflags;
-       int rc;
-
-       spin_lock_irqsave(&phba->hbalock, iflags);
-       rc = __lpfc_sli_issue_iocb(phba, pring, piocb, flag);
-       spin_unlock_irqrestore(&phba->hbalock, iflags);
+       static uint32_t fcp_qidx;
 
-       return rc;
+       return fcp_qidx++ % phba->cfg_fcp_wq_count;
 }
 
 /**
- * lpfc_extra_ring_setup - Extra ring setup function
+ * lpfc_sli_iocb2wqe - Convert the IOCB to a work queue entry.
  * @phba: Pointer to HBA context object.
+ * @piocb: Pointer to command iocb.
+ * @wqe: Pointer to the work queue entry.
  *
- * This function is called while driver attaches with the
- * HBA to setup the extra ring. The extra ring is used
- * only when driver needs to support target mode functionality
- * or IP over FC functionalities.
+ * This routine converts the iocb command to its Work Queue Entry
+ * equivalent. The wqe pointer should not have any fields set when
+ * this routine is called because it will memcpy over them.
+ * This routine does not set the CQ_ID or the WQEC bits in the
+ * wqe.
  *
- * This function is called with no lock held.
+ * Returns: 0 = Success, IOCB_ERROR = Failure.
  **/
 static int
-lpfc_extra_ring_setup( struct lpfc_hba *phba)
+lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
+               union lpfc_wqe *wqe)
 {
-       struct lpfc_sli *psli;
-       struct lpfc_sli_ring *pring;
+       uint32_t payload_len = 0;
+       uint8_t ct = 0;
+       uint32_t fip;
+       uint32_t abort_tag;
+       uint8_t command_type = ELS_COMMAND_NON_FIP;
+       uint8_t cmnd;
+       uint16_t xritag;
+       struct ulp_bde64 *bpl = NULL;
+
+       fip = bf_get(lpfc_fip_flag, &phba->sli4_hba.sli4_flags);
+       /* The fcp commands will set command type */
+       if ((!(iocbq->iocb_flag &  LPFC_IO_FCP)) && (!fip))
+               command_type = ELS_COMMAND_NON_FIP;
+       else if (!(iocbq->iocb_flag &  LPFC_IO_FCP))
+               command_type = ELS_COMMAND_FIP;
+       else if (iocbq->iocb_flag &  LPFC_IO_FCP)
+               command_type = FCP_COMMAND;
+       else {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                       "2019 Invalid cmd 0x%x\n",
+                       iocbq->iocb.ulpCommand);
+               return IOCB_ERROR;
+       }
+       /* Some of the fields are in the right position already */
+       memcpy(wqe, &iocbq->iocb, sizeof(union lpfc_wqe));
+       abort_tag = (uint32_t) iocbq->iotag;
+       xritag = iocbq->sli4_xritag;
+       wqe->words[7] = 0; /* The ct field has moved so reset */
+       /* words0-2 bpl convert bde */
+       if (iocbq->iocb.un.genreq64.bdl.bdeFlags == BUFF_TYPE_BLP_64) {
+               bpl  = (struct ulp_bde64 *)
+                       ((struct lpfc_dmabuf *)iocbq->context3)->virt;
+               if (!bpl)
+                       return IOCB_ERROR;
 
-       psli = &phba->sli;
+               /* Should already be byte swapped. */
+               wqe->generic.bde.addrHigh =  le32_to_cpu(bpl->addrHigh);
+               wqe->generic.bde.addrLow =  le32_to_cpu(bpl->addrLow);
+               /* swap the size field back to the cpu so we
+                * can assign it to the sgl.
+                */
+               wqe->generic.bde.tus.w  = le32_to_cpu(bpl->tus.w);
+               payload_len = wqe->generic.bde.tus.f.bdeSize;
+       } else
+               payload_len = iocbq->iocb.un.fcpi64.bdl.bdeSize;
 
-       /* Adjust cmd/rsp ring iocb entries more evenly */
+       iocbq->iocb.ulpIoTag = iocbq->iotag;
+       cmnd = iocbq->iocb.ulpCommand;
 
-       /* Take some away from the FCP ring */
-       pring = &psli->ring[psli->fcp_ring];
-       pring->numCiocb -= SLI2_IOCB_CMD_R1XTRA_ENTRIES;
-       pring->numRiocb -= SLI2_IOCB_RSP_R1XTRA_ENTRIES;
-       pring->numCiocb -= SLI2_IOCB_CMD_R3XTRA_ENTRIES;
-       pring->numRiocb -= SLI2_IOCB_RSP_R3XTRA_ENTRIES;
+       switch (iocbq->iocb.ulpCommand) {
+       case CMD_ELS_REQUEST64_CR:
+               if (!iocbq->iocb.ulpLe) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "2007 Only Limited Edition cmd Format"
+                               " supported 0x%x\n",
+                               iocbq->iocb.ulpCommand);
+                       return IOCB_ERROR;
+               }
+               wqe->els_req.payload_len = payload_len;
+               /* Els_reguest64 has a TMO */
+               bf_set(wqe_tmo, &wqe->els_req.wqe_com,
+                       iocbq->iocb.ulpTimeout);
+               /* Need a VF for word 4 set the vf bit*/
+               bf_set(els_req64_vf, &wqe->els_req, 0);
+               /* And a VFID for word 12 */
+               bf_set(els_req64_vfid, &wqe->els_req, 0);
+               /*
+                * Set ct field to 3, indicates that the context_tag field
+                * contains the FCFI and remote N_Port_ID is
+                * in word 5.
+                */
 
-       /* and give them to the extra ring */
-       pring = &psli->ring[psli->extra_ring];
+               ct = ((iocbq->iocb.ulpCt_h << 1) | iocbq->iocb.ulpCt_l);
+               bf_set(lpfc_wqe_gen_context, &wqe->generic,
+                               iocbq->iocb.ulpContext);
 
-       pring->numCiocb += SLI2_IOCB_CMD_R1XTRA_ENTRIES;
-       pring->numRiocb += SLI2_IOCB_RSP_R1XTRA_ENTRIES;
-       pring->numCiocb += SLI2_IOCB_CMD_R3XTRA_ENTRIES;
-       pring->numRiocb += SLI2_IOCB_RSP_R3XTRA_ENTRIES;
+               if (iocbq->vport->fc_myDID != 0) {
+                       bf_set(els_req64_sid, &wqe->els_req,
+                                iocbq->vport->fc_myDID);
+                       bf_set(els_req64_sp, &wqe->els_req, 1);
+               }
+               bf_set(lpfc_wqe_gen_ct, &wqe->generic, ct);
+               bf_set(lpfc_wqe_gen_pu, &wqe->generic, 0);
+               /* CCP CCPE PV PRI in word10 were set in the memcpy */
+       break;
+       case CMD_XMIT_SEQUENCE64_CR:
+               /* word3 iocb=io_tag32 wqe=payload_offset */
+               /* payload offset used for multilpe outstanding
+                * sequences on the same exchange
+                */
+               wqe->words[3] = 0;
+               /* word4 relative_offset memcpy */
+               /* word5 r_ctl/df_ctl memcpy */
+               bf_set(lpfc_wqe_gen_pu, &wqe->generic, 0);
+               wqe->xmit_sequence.xmit_len = payload_len;
+       break;
+       case CMD_XMIT_BCAST64_CN:
+               /* word3 iocb=iotag32 wqe=payload_len */
+               wqe->words[3] = 0; /* no definition for this in wqe */
+               /* word4 iocb=rsvd wqe=rsvd */
+               /* word5 iocb=rctl/type/df_ctl wqe=rctl/type/df_ctl memcpy */
+               /* word6 iocb=ctxt_tag/io_tag wqe=ctxt_tag/xri */
+               bf_set(lpfc_wqe_gen_ct, &wqe->generic,
+                       ((iocbq->iocb.ulpCt_h << 1) | iocbq->iocb.ulpCt_l));
+       break;
+       case CMD_FCP_IWRITE64_CR:
+               command_type = FCP_COMMAND_DATA_OUT;
+               /* The struct for wqe fcp_iwrite has 3 fields that are somewhat
+                * confusing.
+                * word3 is payload_len: byte offset to the sgl entry for the
+                * fcp_command.
+                * word4 is total xfer len, same as the IOCB->ulpParameter.
+                * word5 is initial xfer len 0 = wait for xfer-ready
+                */
 
-       /* Setup default profile for this ring */
-       pring->iotag_max = 4096;
-       pring->num_mask = 1;
-       pring->prt[0].profile = 0;      /* Mask 0 */
+               /* Always wait for xfer-ready before sending data */
+               wqe->fcp_iwrite.initial_xfer_len = 0;
+               /* word 4 (xfer length) should have been set on the memcpy */
+
+       /* allow write to fall through to read */
+       case CMD_FCP_IREAD64_CR:
+               /* FCP_CMD is always the 1st sgl entry */
+               wqe->fcp_iread.payload_len =
+                       payload_len + sizeof(struct fcp_rsp);
+
+               /* word 4 (xfer length) should have been set on the memcpy */
+
+               bf_set(lpfc_wqe_gen_erp, &wqe->generic,
+                       iocbq->iocb.ulpFCP2Rcvy);
+               bf_set(lpfc_wqe_gen_lnk, &wqe->generic, iocbq->iocb.ulpXS);
+               /* The XC bit and the XS bit are similar. The driver never
+                * tracked whether or not the exchange was previouslly open.
+                * XC = Exchange create, 0 is create. 1 is already open.
+                * XS = link cmd: 1 do not close the exchange after command.
+                * XS = 0 close exchange when command completes.
+                * The only time we would not set the XC bit is when the XS bit
+                * is set and we are sending our 2nd or greater command on
+                * this exchange.
+                */
+
+       /* ALLOW read & write to fall through to ICMD64 */
+       case CMD_FCP_ICMND64_CR:
+               /* Always open the exchange */
+               bf_set(wqe_xc, &wqe->fcp_iread.wqe_com, 0);
+
+               wqe->words[10] &= 0xffff0000; /* zero out ebde count */
+               bf_set(lpfc_wqe_gen_pu, &wqe->generic, iocbq->iocb.ulpPU);
+       break;
+       case CMD_GEN_REQUEST64_CR:
+               /* word3 command length is described as byte offset to the
+                * rsp_data. Would always be 16, sizeof(struct sli4_sge)
+                * sgl[0] = cmnd
+                * sgl[1] = rsp.
+                *
+                */
+               wqe->gen_req.command_len = payload_len;
+               /* Word4 parameter  copied in the memcpy */
+               /* Word5 [rctl, type, df_ctl, la] copied in memcpy */
+               /* word6 context tag copied in memcpy */
+               if (iocbq->iocb.ulpCt_h  || iocbq->iocb.ulpCt_l) {
+                       ct = ((iocbq->iocb.ulpCt_h << 1) | iocbq->iocb.ulpCt_l);
+                       lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "2015 Invalid CT %x command 0x%x\n",
+                               ct, iocbq->iocb.ulpCommand);
+                       return IOCB_ERROR;
+               }
+               bf_set(lpfc_wqe_gen_ct, &wqe->generic, 0);
+               bf_set(wqe_tmo, &wqe->gen_req.wqe_com,
+                       iocbq->iocb.ulpTimeout);
+
+               bf_set(lpfc_wqe_gen_pu, &wqe->generic, iocbq->iocb.ulpPU);
+               command_type = OTHER_COMMAND;
+       break;
+       case CMD_XMIT_ELS_RSP64_CX:
+               /* words0-2 BDE memcpy */
+               /* word3 iocb=iotag32 wqe=rsvd */
+               wqe->words[3] = 0;
+               /* word4 iocb=did wge=rsvd. */
+               wqe->words[4] = 0;
+               /* word5 iocb=rsvd wge=did */
+               bf_set(wqe_els_did, &wqe->xmit_els_rsp.wqe_dest,
+                        iocbq->iocb.un.elsreq64.remoteID);
+
+               bf_set(lpfc_wqe_gen_ct, &wqe->generic,
+                       ((iocbq->iocb.ulpCt_h << 1) | iocbq->iocb.ulpCt_l));
+
+               bf_set(lpfc_wqe_gen_pu, &wqe->generic, iocbq->iocb.ulpPU);
+               bf_set(wqe_rcvoxid, &wqe->generic, iocbq->iocb.ulpContext);
+               if (!iocbq->iocb.ulpCt_h && iocbq->iocb.ulpCt_l)
+                       bf_set(lpfc_wqe_gen_context, &wqe->generic,
+                              iocbq->vport->vpi + phba->vpi_base);
+               command_type = OTHER_COMMAND;
+       break;
+       case CMD_CLOSE_XRI_CN:
+       case CMD_ABORT_XRI_CN:
+       case CMD_ABORT_XRI_CX:
+               /* words 0-2 memcpy should be 0 rserved */
+               /* port will send abts */
+               if (iocbq->iocb.ulpCommand == CMD_CLOSE_XRI_CN)
+                       /*
+                        * The link is down so the fw does not need to send abts
+                        * on the wire.
+                        */
+                       bf_set(abort_cmd_ia, &wqe->abort_cmd, 1);
+               else
+                       bf_set(abort_cmd_ia, &wqe->abort_cmd, 0);
+               bf_set(abort_cmd_criteria, &wqe->abort_cmd, T_XRI_TAG);
+               abort_tag = iocbq->iocb.un.acxri.abortIoTag;
+               wqe->words[5] = 0;
+               bf_set(lpfc_wqe_gen_ct, &wqe->generic,
+                       ((iocbq->iocb.ulpCt_h << 1) | iocbq->iocb.ulpCt_l));
+               abort_tag = iocbq->iocb.un.acxri.abortIoTag;
+               wqe->generic.abort_tag = abort_tag;
+               /*
+                * The abort handler will send us CMD_ABORT_XRI_CN or
+                * CMD_CLOSE_XRI_CN and the fw only accepts CMD_ABORT_XRI_CX
+                */
+               bf_set(lpfc_wqe_gen_command, &wqe->generic, CMD_ABORT_XRI_CX);
+               cmnd = CMD_ABORT_XRI_CX;
+               command_type = OTHER_COMMAND;
+               xritag = 0;
+       break;
+       case CMD_XRI_ABORTED_CX:
+       case CMD_CREATE_XRI_CR: /* Do we expect to use this? */
+               /* words0-2 are all 0's no bde */
+               /* word3 and word4 are rsvrd */
+               wqe->words[3] = 0;
+               wqe->words[4] = 0;
+               /* word5 iocb=rsvd wge=did */
+               /* There is no remote port id in the IOCB? */
+               /* Let this fall through and fail */
+       case CMD_IOCB_FCP_IBIDIR64_CR: /* bidirectional xfer */
+       case CMD_FCP_TSEND64_CX: /* Target mode send xfer-ready */
+       case CMD_FCP_TRSP64_CX: /* Target mode rcv */
+       case CMD_FCP_AUTO_TRSP_CX: /* Auto target rsp */
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "2014 Invalid command 0x%x\n",
+                               iocbq->iocb.ulpCommand);
+               return IOCB_ERROR;
+       break;
+
+       }
+       bf_set(lpfc_wqe_gen_xri, &wqe->generic, xritag);
+       bf_set(lpfc_wqe_gen_request_tag, &wqe->generic, iocbq->iotag);
+       wqe->generic.abort_tag = abort_tag;
+       bf_set(lpfc_wqe_gen_cmd_type, &wqe->generic, command_type);
+       bf_set(lpfc_wqe_gen_command, &wqe->generic, cmnd);
+       bf_set(lpfc_wqe_gen_class, &wqe->generic, iocbq->iocb.ulpClass);
+       bf_set(lpfc_wqe_gen_cq_id, &wqe->generic, LPFC_WQE_CQ_ID_DEFAULT);
+
+       return 0;
+}
+
+/**
+ * __lpfc_sli_issue_iocb_s4 - SLI4 device lockless ver of lpfc_sli_issue_iocb
+ * @phba: Pointer to HBA context object.
+ * @ring_number: SLI ring number to issue iocb on.
+ * @piocb: Pointer to command iocb.
+ * @flag: Flag indicating if this command can be put into txq.
+ *
+ * __lpfc_sli_issue_iocb_s4 is used by other functions in the driver to issue
+ * an iocb command to an HBA with SLI-4 interface spec.
+ *
+ * This function is called with hbalock held. The function will return success
+ * after it successfully submit the iocb to firmware or after adding to the
+ * txq.
+ **/
+static int
+__lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
+                        struct lpfc_iocbq *piocb, uint32_t flag)
+{
+       struct lpfc_sglq *sglq;
+       uint16_t xritag;
+       union lpfc_wqe wqe;
+       struct lpfc_sli_ring *pring = &phba->sli.ring[ring_number];
+       uint32_t fcp_wqidx;
+
+       if (piocb->sli4_xritag == NO_XRI) {
+               if (piocb->iocb.ulpCommand == CMD_ABORT_XRI_CN ||
+                       piocb->iocb.ulpCommand == CMD_CLOSE_XRI_CN)
+                       sglq = NULL;
+               else {
+                       sglq = __lpfc_sli_get_sglq(phba);
+                       if (!sglq)
+                               return IOCB_ERROR;
+                       piocb->sli4_xritag = sglq->sli4_xritag;
+               }
+       } else if (piocb->iocb_flag &  LPFC_IO_FCP) {
+               sglq = NULL; /* These IO's already have an XRI and
+                             * a mapped sgl.
+                             */
+       } else {
+               /* This is a continuation of a commandi,(CX) so this
+                * sglq is on the active list
+                */
+               sglq = __lpfc_get_active_sglq(phba, piocb->sli4_xritag);
+               if (!sglq)
+                       return IOCB_ERROR;
+       }
+
+       if (sglq) {
+               xritag = lpfc_sli4_bpl2sgl(phba, piocb, sglq);
+               if (xritag != sglq->sli4_xritag)
+                       return IOCB_ERROR;
+       }
+
+       if (lpfc_sli4_iocb2wqe(phba, piocb, &wqe))
+               return IOCB_ERROR;
+
+       if (piocb->iocb_flag &  LPFC_IO_FCP) {
+               fcp_wqidx = lpfc_sli4_scmd_to_wqidx_distr(phba, piocb);
+               if (lpfc_sli4_wq_put(phba->sli4_hba.fcp_wq[fcp_wqidx], &wqe))
+                       return IOCB_ERROR;
+       } else {
+               if (lpfc_sli4_wq_put(phba->sli4_hba.els_wq, &wqe))
+                       return IOCB_ERROR;
+       }
+       lpfc_sli_ringtxcmpl_put(phba, pring, piocb);
+
+       return 0;
+}
+
+/**
+ * __lpfc_sli_issue_iocb - Wrapper func of lockless version for issuing iocb
+ *
+ * This routine wraps the actual lockless version for issusing IOCB function
+ * pointer from the lpfc_hba struct.
+ *
+ * Return codes:
+ *     IOCB_ERROR - Error
+ *     IOCB_SUCCESS - Success
+ *     IOCB_BUSY - Busy
+ **/
+static inline int
+__lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number,
+               struct lpfc_iocbq *piocb, uint32_t flag)
+{
+       return phba->__lpfc_sli_issue_iocb(phba, ring_number, piocb, flag);
+}
+
+/**
+ * lpfc_sli_api_table_setup - Set up sli api fucntion jump table
+ * @phba: The hba struct for which this call is being executed.
+ * @dev_grp: The HBA PCI-Device group number.
+ *
+ * This routine sets up the SLI interface API function jump table in @phba
+ * struct.
+ * Returns: 0 - success, -ENODEV - failure.
+ **/
+int
+lpfc_sli_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
+{
+
+       switch (dev_grp) {
+       case LPFC_PCI_DEV_LP:
+               phba->__lpfc_sli_issue_iocb = __lpfc_sli_issue_iocb_s3;
+               phba->__lpfc_sli_release_iocbq = __lpfc_sli_release_iocbq_s3;
+               break;
+       case LPFC_PCI_DEV_OC:
+               phba->__lpfc_sli_issue_iocb = __lpfc_sli_issue_iocb_s4;
+               phba->__lpfc_sli_release_iocbq = __lpfc_sli_release_iocbq_s4;
+               break;
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "1419 Invalid HBA PCI-device group: 0x%x\n",
+                               dev_grp);
+               return -ENODEV;
+               break;
+       }
+       phba->lpfc_get_iocb_from_iocbq = lpfc_get_iocb_from_iocbq;
+       return 0;
+}
+
+/**
+ * lpfc_sli_issue_iocb - Wrapper function for __lpfc_sli_issue_iocb
+ * @phba: Pointer to HBA context object.
+ * @pring: Pointer to driver SLI ring object.
+ * @piocb: Pointer to command iocb.
+ * @flag: Flag indicating if this command can be put into txq.
+ *
+ * lpfc_sli_issue_iocb is a wrapper around __lpfc_sli_issue_iocb
+ * function. This function gets the hbalock and calls
+ * __lpfc_sli_issue_iocb function and will return the error returned
+ * by __lpfc_sli_issue_iocb function. This wrapper is used by
+ * functions which do not hold hbalock.
+ **/
+int
+lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number,
+                   struct lpfc_iocbq *piocb, uint32_t flag)
+{
+       unsigned long iflags;
+       int rc;
+
+       spin_lock_irqsave(&phba->hbalock, iflags);
+       rc = __lpfc_sli_issue_iocb(phba, ring_number, piocb, flag);
+       spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+       return rc;
+}
+
+/**
+ * lpfc_extra_ring_setup - Extra ring setup function
+ * @phba: Pointer to HBA context object.
+ *
+ * This function is called while driver attaches with the
+ * HBA to setup the extra ring. The extra ring is used
+ * only when driver needs to support target mode functionality
+ * or IP over FC functionalities.
+ *
+ * This function is called with no lock held.
+ **/
+static int
+lpfc_extra_ring_setup( struct lpfc_hba *phba)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_sli_ring *pring;
+
+       psli = &phba->sli;
+
+       /* Adjust cmd/rsp ring iocb entries more evenly */
+
+       /* Take some away from the FCP ring */
+       pring = &psli->ring[psli->fcp_ring];
+       pring->numCiocb -= SLI2_IOCB_CMD_R1XTRA_ENTRIES;
+       pring->numRiocb -= SLI2_IOCB_RSP_R1XTRA_ENTRIES;
+       pring->numCiocb -= SLI2_IOCB_CMD_R3XTRA_ENTRIES;
+       pring->numRiocb -= SLI2_IOCB_RSP_R3XTRA_ENTRIES;
+
+       /* and give them to the extra ring */
+       pring = &psli->ring[psli->extra_ring];
+
+       pring->numCiocb += SLI2_IOCB_CMD_R1XTRA_ENTRIES;
+       pring->numRiocb += SLI2_IOCB_RSP_R1XTRA_ENTRIES;
+       pring->numCiocb += SLI2_IOCB_CMD_R3XTRA_ENTRIES;
+       pring->numRiocb += SLI2_IOCB_RSP_R3XTRA_ENTRIES;
+
+       /* Setup default profile for this ring */
+       pring->iotag_max = 4096;
+       pring->num_mask = 1;
+       pring->prt[0].profile = 0;      /* Mask 0 */
        pring->prt[0].rctl = phba->cfg_multi_ring_rctl;
        pring->prt[0].type = phba->cfg_multi_ring_type;
        pring->prt[0].lpfc_sli_rcv_unsol_event = NULL;
@@ -4147,6 +6425,52 @@ lpfc_sli_queue_setup(struct lpfc_hba *phba)
        return 1;
 }
 
+/**
+ * lpfc_sli_mbox_sys_flush - Flush mailbox command sub-system
+ * @phba: Pointer to HBA context object.
+ *
+ * This routine flushes the mailbox command subsystem. It will unconditionally
+ * flush all the mailbox commands in the three possible stages in the mailbox
+ * command sub-system: pending mailbox command queue; the outstanding mailbox
+ * command; and completed mailbox command queue. It is caller's responsibility
+ * to make sure that the driver is in the proper state to flush the mailbox
+ * command sub-system. Namely, the posting of mailbox commands into the
+ * pending mailbox command queue from the various clients must be stopped;
+ * either the HBA is in a state that it will never works on the outstanding
+ * mailbox command (such as in EEH or ERATT conditions) or the outstanding
+ * mailbox command has been completed.
+ **/
+static void
+lpfc_sli_mbox_sys_flush(struct lpfc_hba *phba)
+{
+       LIST_HEAD(completions);
+       struct lpfc_sli *psli = &phba->sli;
+       LPFC_MBOXQ_t *pmb;
+       unsigned long iflag;
+
+       /* Flush all the mailbox commands in the mbox system */
+       spin_lock_irqsave(&phba->hbalock, iflag);
+       /* The pending mailbox command queue */
+       list_splice_init(&phba->sli.mboxq, &completions);
+       /* The outstanding active mailbox command */
+       if (psli->mbox_active) {
+               list_add_tail(&psli->mbox_active->list, &completions);
+               psli->mbox_active = NULL;
+               psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+       }
+       /* The completed mailbox command queue */
+       list_splice_init(&phba->sli.mboxq_cmpl, &completions);
+       spin_unlock_irqrestore(&phba->hbalock, iflag);
+
+       /* Return all flushed mailbox commands with MBX_NOT_FINISHED status */
+       while (!list_empty(&completions)) {
+               list_remove_head(&completions, pmb, LPFC_MBOXQ_t, list);
+               pmb->u.mb.mbxStatus = MBX_NOT_FINISHED;
+               if (pmb->mbox_cmpl)
+                       pmb->mbox_cmpl(phba, pmb);
+       }
+}
+
 /**
  * lpfc_sli_host_down - Vport cleanup function
  * @vport: Pointer to virtual port object.
@@ -4240,9 +6564,11 @@ lpfc_sli_hba_down(struct lpfc_hba *phba)
        struct lpfc_sli *psli = &phba->sli;
        struct lpfc_sli_ring *pring;
        struct lpfc_dmabuf *buf_ptr;
-       LPFC_MBOXQ_t *pmb;
-       int i;
        unsigned long flags = 0;
+       int i;
+
+       /* Shutdown the mailbox command sub-system */
+       lpfc_sli_mbox_sys_shutdown(phba);
 
        lpfc_hba_down_prep(phba);
 
@@ -4287,28 +6613,42 @@ lpfc_sli_hba_down(struct lpfc_hba *phba)
 
        /* Return any active mbox cmds */
        del_timer_sync(&psli->mbox_tmo);
-       spin_lock_irqsave(&phba->hbalock, flags);
 
-       spin_lock(&phba->pport->work_port_lock);
+       spin_lock_irqsave(&phba->pport->work_port_lock, flags);
        phba->pport->work_port_events &= ~WORKER_MBOX_TMO;
-       spin_unlock(&phba->pport->work_port_lock);
+       spin_unlock_irqrestore(&phba->pport->work_port_lock, flags);
 
-       /* Return any pending or completed mbox cmds */
-       list_splice_init(&phba->sli.mboxq, &completions);
-       if (psli->mbox_active) {
-               list_add_tail(&psli->mbox_active->list, &completions);
-               psli->mbox_active = NULL;
-               psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
-       }
-       list_splice_init(&phba->sli.mboxq_cmpl, &completions);
-       spin_unlock_irqrestore(&phba->hbalock, flags);
+       return 1;
+}
+
+/**
+ * lpfc_sli4_hba_down - PCI function resource cleanup for the SLI4 HBA
+ * @phba: Pointer to HBA context object.
+ *
+ * This function cleans up all queues, iocb, buffers, mailbox commands while
+ * shutting down the SLI4 HBA FCoE function. This function is called with no
+ * lock held and always returns 1.
+ *
+ * This function does the following to cleanup driver FCoE function resources:
+ * - Free discovery resources for each virtual port
+ * - Cleanup any pending fabric iocbs
+ * - Iterate through the iocb txq and free each entry in the list.
+ * - Free up any buffer posted to the HBA.
+ * - Clean up all the queue entries: WQ, RQ, MQ, EQ, CQ, etc.
+ * - Free mailbox commands in the mailbox queue.
+ **/
+int
+lpfc_sli4_hba_down(struct lpfc_hba *phba)
+{
+       /* Stop the SLI4 device port */
+       lpfc_stop_port(phba);
+
+       /* Tear down the queues in the HBA */
+       lpfc_sli4_queue_unset(phba);
+
+       /* unregister default FCFI from the HBA */
+       lpfc_sli4_fcfi_unreg(phba, phba->fcf.fcfi);
 
-       while (!list_empty(&completions)) {
-               list_remove_head(&completions, pmb, LPFC_MBOXQ_t, list);
-               pmb->mb.mbxStatus = MBX_NOT_FINISHED;
-               if (pmb->mbox_cmpl)
-                       pmb->mbox_cmpl(phba,pmb);
-       }
        return 1;
 }
 
@@ -4639,7 +6979,10 @@ lpfc_sli_issue_abort_iotag(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
        iabt = &abtsiocbp->iocb;
        iabt->un.acxri.abortType = ABORT_TYPE_ABTS;
        iabt->un.acxri.abortContextTag = icmd->ulpContext;
-       iabt->un.acxri.abortIoTag = icmd->ulpIoTag;
+       if (phba->sli_rev == LPFC_SLI_REV4)
+               iabt->un.acxri.abortIoTag = cmdiocb->sli4_xritag;
+       else
+               iabt->un.acxri.abortIoTag = icmd->ulpIoTag;
        iabt->ulpLe = 1;
        iabt->ulpClass = icmd->ulpClass;
 
@@ -4655,7 +6998,7 @@ lpfc_sli_issue_abort_iotag(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
                         "abort cmd iotag x%x\n",
                         iabt->un.acxri.abortContextTag,
                         iabt->un.acxri.abortIoTag, abtsiocbp->iotag);
-       retval = __lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0);
+       retval = __lpfc_sli_issue_iocb(phba, pring->ringno, abtsiocbp, 0);
 
        if (retval)
                __lpfc_sli_release_iocbq(phba, abtsiocbp);
@@ -4838,7 +7181,10 @@ lpfc_sli_abort_iocb(struct lpfc_vport *vport, struct lpfc_sli_ring *pring,
                cmd = &iocbq->iocb;
                abtsiocb->iocb.un.acxri.abortType = ABORT_TYPE_ABTS;
                abtsiocb->iocb.un.acxri.abortContextTag = cmd->ulpContext;
-               abtsiocb->iocb.un.acxri.abortIoTag = cmd->ulpIoTag;
+               if (phba->sli_rev == LPFC_SLI_REV4)
+                       abtsiocb->iocb.un.acxri.abortIoTag = iocbq->sli4_xritag;
+               else
+                       abtsiocb->iocb.un.acxri.abortIoTag = cmd->ulpIoTag;
                abtsiocb->iocb.ulpLe = 1;
                abtsiocb->iocb.ulpClass = cmd->ulpClass;
                abtsiocb->vport = phba->pport;
@@ -4850,7 +7196,8 @@ lpfc_sli_abort_iocb(struct lpfc_vport *vport, struct lpfc_sli_ring *pring,
 
                /* Setup callback routine and issue the command. */
                abtsiocb->iocb_cmpl = lpfc_sli_abort_fcp_cmpl;
-               ret_val = lpfc_sli_issue_iocb(phba, pring, abtsiocb, 0);
+               ret_val = lpfc_sli_issue_iocb(phba, pring->ringno,
+                                             abtsiocb, 0);
                if (ret_val == IOCB_ERROR) {
                        lpfc_sli_release_iocbq(phba, abtsiocb);
                        errcnt++;
@@ -4931,7 +7278,7 @@ lpfc_sli_wake_iocb_wait(struct lpfc_hba *phba,
  **/
 int
 lpfc_sli_issue_iocb_wait(struct lpfc_hba *phba,
-                        struct lpfc_sli_ring *pring,
+                        uint32_t ring_number,
                         struct lpfc_iocbq *piocb,
                         struct lpfc_iocbq *prspiocbq,
                         uint32_t timeout)
@@ -4962,7 +7309,7 @@ lpfc_sli_issue_iocb_wait(struct lpfc_hba *phba,
                readl(phba->HCregaddr); /* flush */
        }
 
-       retval = lpfc_sli_issue_iocb(phba, pring, piocb, 0);
+       retval = lpfc_sli_issue_iocb(phba, ring_number, piocb, 0);
        if (retval == IOCB_SUCCESS) {
                timeout_req = timeout * HZ;
                timeleft = wait_event_timeout(done_q,
@@ -5077,140 +7424,267 @@ lpfc_sli_issue_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq,
 }
 
 /**
- * lpfc_sli_flush_mbox_queue - mailbox queue cleanup function
+ * lpfc_sli_mbox_sys_shutdown - shutdown mailbox command sub-system
  * @phba: Pointer to HBA context.
  *
- * This function is called to cleanup any pending mailbox
- * objects in the driver queue before bringing the HBA offline.
- * This function is called while resetting the HBA.
- * The function is called without any lock held. The function
- * takes hbalock to update SLI data structure.
- * This function returns 1 when there is an active mailbox
- * command pending else returns 0.
+ * This function is called to shutdown the driver's mailbox sub-system.
+ * It first marks the mailbox sub-system is in a block state to prevent
+ * the asynchronous mailbox command from issued off the pending mailbox
+ * command queue. If the mailbox command sub-system shutdown is due to
+ * HBA error conditions such as EEH or ERATT, this routine shall invoke
+ * the mailbox sub-system flush routine to forcefully bring down the
+ * mailbox sub-system. Otherwise, if it is due to normal condition (such
+ * as with offline or HBA function reset), this routine will wait for the
+ * outstanding mailbox command to complete before invoking the mailbox
+ * sub-system flush routine to gracefully bring down mailbox sub-system.
  **/
-int
-lpfc_sli_flush_mbox_queue(struct lpfc_hba * phba)
+void
+lpfc_sli_mbox_sys_shutdown(struct lpfc_hba *phba)
 {
-       struct lpfc_vport *vport = phba->pport;
-       int i = 0;
-       uint32_t ha_copy;
+       struct lpfc_sli *psli = &phba->sli;
+       uint8_t actcmd = MBX_HEARTBEAT;
+       unsigned long timeout;
 
-       while (phba->sli.sli_flag & LPFC_SLI_MBOX_ACTIVE && !vport->stopped) {
-               if (i++ > LPFC_MBOX_TMO * 1000)
-                       return 1;
+       spin_lock_irq(&phba->hbalock);
+       psli->sli_flag |= LPFC_SLI_ASYNC_MBX_BLK;
+       spin_unlock_irq(&phba->hbalock);
 
-               /*
-                * Call lpfc_sli_handle_mb_event only if a mailbox cmd
-                * did finish. This way we won't get the misleading
-                * "Stray Mailbox Interrupt" message.
-                */
+       if (psli->sli_flag & LPFC_SLI_ACTIVE) {
                spin_lock_irq(&phba->hbalock);
-               ha_copy = phba->work_ha;
-               phba->work_ha &= ~HA_MBATT;
+               if (phba->sli.mbox_active)
+                       actcmd = phba->sli.mbox_active->u.mb.mbxCommand;
                spin_unlock_irq(&phba->hbalock);
-
-               if (ha_copy & HA_MBATT)
-                       if (lpfc_sli_handle_mb_event(phba) == 0)
-                               i = 0;
-
-               msleep(1);
+               /* Determine how long we might wait for the active mailbox
+                * command to be gracefully completed by firmware.
+                */
+               timeout = msecs_to_jiffies(lpfc_mbox_tmo_val(phba, actcmd) *
+                                          1000) + jiffies;
+               while (phba->sli.mbox_active) {
+                       /* Check active mailbox complete status every 2ms */
+                       msleep(2);
+                       if (time_after(jiffies, timeout))
+                               /* Timeout, let the mailbox flush routine to
+                                * forcefully release active mailbox command
+                                */
+                               break;
+               }
        }
-
-       return (phba->sli.sli_flag & LPFC_SLI_MBOX_ACTIVE) ? 1 : 0;
+       lpfc_sli_mbox_sys_flush(phba);
 }
 
 /**
- * lpfc_sli_check_eratt - check error attention events
+ * lpfc_sli_eratt_read - read sli-3 error attention events
  * @phba: Pointer to HBA context.
  *
- * This function is called form timer soft interrupt context to check HBA's
- * error attention register bit for error attention events.
+ * This function is called to read the SLI3 device error attention registers
+ * for possible error attention events. The caller must hold the hostlock
+ * with spin_lock_irq().
  *
  * This fucntion returns 1 when there is Error Attention in the Host Attention
  * Register and returns 0 otherwise.
  **/
-int
-lpfc_sli_check_eratt(struct lpfc_hba *phba)
+static int
+lpfc_sli_eratt_read(struct lpfc_hba *phba)
 {
        uint32_t ha_copy;
 
-       /* If PCI channel is offline, don't process it */
-       if (unlikely(pci_channel_offline(phba->pcidev)))
-               return 0;
-
-       /* If somebody is waiting to handle an eratt, don't process it
-        * here. The brdkill function will do this.
-        */
-       if (phba->link_flag & LS_IGNORE_ERATT)
-               return 0;
-
-       /* Check if interrupt handler handles this ERATT */
-       spin_lock_irq(&phba->hbalock);
-       if (phba->hba_flag & HBA_ERATT_HANDLED) {
-               /* Interrupt handler has handled ERATT */
-               spin_unlock_irq(&phba->hbalock);
-               return 0;
-       }
-
-       /*
-        * If there is deferred error attention, do not check for error
-        * attention
-        */
-       if (unlikely(phba->hba_flag & DEFER_ERATT)) {
-               spin_unlock_irq(&phba->hbalock);
-               return 0;
-       }
-
-       /* Read chip Host Attention (HA) register */
-       ha_copy = readl(phba->HAregaddr);
-       if (ha_copy & HA_ERATT) {
-               /* Read host status register to retrieve error event */
-               lpfc_sli_read_hs(phba);
+       /* Read chip Host Attention (HA) register */
+       ha_copy = readl(phba->HAregaddr);
+       if (ha_copy & HA_ERATT) {
+               /* Read host status register to retrieve error event */
+               lpfc_sli_read_hs(phba);
 
                /* Check if there is a deferred error condition is active */
                if ((HS_FFER1 & phba->work_hs) &&
-                       ((HS_FFER2 | HS_FFER3 | HS_FFER4 | HS_FFER5 |
-                       HS_FFER6 | HS_FFER7) & phba->work_hs)) {
+                   ((HS_FFER2 | HS_FFER3 | HS_FFER4 | HS_FFER5 |
+                    HS_FFER6 | HS_FFER7) & phba->work_hs)) {
+                       spin_lock_irq(&phba->hbalock);
                        phba->hba_flag |= DEFER_ERATT;
+                       spin_unlock_irq(&phba->hbalock);
                        /* Clear all interrupt enable conditions */
                        writel(0, phba->HCregaddr);
                        readl(phba->HCregaddr);
                }
 
                /* Set the driver HA work bitmap */
+               spin_lock_irq(&phba->hbalock);
                phba->work_ha |= HA_ERATT;
                /* Indicate polling handles this ERATT */
                phba->hba_flag |= HBA_ERATT_HANDLED;
                spin_unlock_irq(&phba->hbalock);
                return 1;
        }
+       return 0;
+}
+
+/**
+ * lpfc_sli4_eratt_read - read sli-4 error attention events
+ * @phba: Pointer to HBA context.
+ *
+ * This function is called to read the SLI4 device error attention registers
+ * for possible error attention events. The caller must hold the hostlock
+ * with spin_lock_irq().
+ *
+ * This fucntion returns 1 when there is Error Attention in the Host Attention
+ * Register and returns 0 otherwise.
+ **/
+static int
+lpfc_sli4_eratt_read(struct lpfc_hba *phba)
+{
+       uint32_t uerr_sta_hi, uerr_sta_lo;
+       uint32_t onlnreg0, onlnreg1;
+
+       /* For now, use the SLI4 device internal unrecoverable error
+        * registers for error attention. This can be changed later.
+        */
+       onlnreg0 = readl(phba->sli4_hba.ONLINE0regaddr);
+       onlnreg1 = readl(phba->sli4_hba.ONLINE1regaddr);
+       if ((onlnreg0 != LPFC_ONLINE_NERR) || (onlnreg1 != LPFC_ONLINE_NERR)) {
+               uerr_sta_lo = readl(phba->sli4_hba.UERRLOregaddr);
+               uerr_sta_hi = readl(phba->sli4_hba.UERRHIregaddr);
+               if (uerr_sta_lo || uerr_sta_hi) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                                       "1423 HBA Unrecoverable error: "
+                                       "uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, "
+                                       "online0_reg=0x%x, online1_reg=0x%x\n",
+                                       uerr_sta_lo, uerr_sta_hi,
+                                       onlnreg0, onlnreg1);
+                       /* TEMP: as the driver error recover logic is not
+                        * fully developed, we just log the error message
+                        * and the device error attention action is now
+                        * temporarily disabled.
+                        */
+                       return 0;
+                       phba->work_status[0] = uerr_sta_lo;
+                       phba->work_status[1] = uerr_sta_hi;
+                       spin_lock_irq(&phba->hbalock);
+                       /* Set the driver HA work bitmap */
+                       phba->work_ha |= HA_ERATT;
+                       /* Indicate polling handles this ERATT */
+                       phba->hba_flag |= HBA_ERATT_HANDLED;
+                       spin_unlock_irq(&phba->hbalock);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+/**
+ * lpfc_sli_check_eratt - check error attention events
+ * @phba: Pointer to HBA context.
+ *
+ * This function is called from timer soft interrupt context to check HBA's
+ * error attention register bit for error attention events.
+ *
+ * This fucntion returns 1 when there is Error Attention in the Host Attention
+ * Register and returns 0 otherwise.
+ **/
+int
+lpfc_sli_check_eratt(struct lpfc_hba *phba)
+{
+       uint32_t ha_copy;
+
+       /* If somebody is waiting to handle an eratt, don't process it
+        * here. The brdkill function will do this.
+        */
+       if (phba->link_flag & LS_IGNORE_ERATT)
+               return 0;
+
+       /* Check if interrupt handler handles this ERATT */
+       spin_lock_irq(&phba->hbalock);
+       if (phba->hba_flag & HBA_ERATT_HANDLED) {
+               /* Interrupt handler has handled ERATT */
+               spin_unlock_irq(&phba->hbalock);
+               return 0;
+       }
+
+       /*
+        * If there is deferred error attention, do not check for error
+        * attention
+        */
+       if (unlikely(phba->hba_flag & DEFER_ERATT)) {
+               spin_unlock_irq(&phba->hbalock);
+               return 0;
+       }
+
+       /* If PCI channel is offline, don't process it */
+       if (unlikely(pci_channel_offline(phba->pcidev))) {
+               spin_unlock_irq(&phba->hbalock);
+               return 0;
+       }
+
+       switch (phba->sli_rev) {
+       case LPFC_SLI_REV2:
+       case LPFC_SLI_REV3:
+               /* Read chip Host Attention (HA) register */
+               ha_copy = lpfc_sli_eratt_read(phba);
+               break;
+       case LPFC_SLI_REV4:
+               /* Read devcie Uncoverable Error (UERR) registers */
+               ha_copy = lpfc_sli4_eratt_read(phba);
+               break;
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0299 Invalid SLI revision (%d)\n",
+                               phba->sli_rev);
+               ha_copy = 0;
+               break;
+       }
        spin_unlock_irq(&phba->hbalock);
+
+       return ha_copy;
+}
+
+/**
+ * lpfc_intr_state_check - Check device state for interrupt handling
+ * @phba: Pointer to HBA context.
+ *
+ * This inline routine checks whether a device or its PCI slot is in a state
+ * that the interrupt should be handled.
+ *
+ * This function returns 0 if the device or the PCI slot is in a state that
+ * interrupt should be handled, otherwise -EIO.
+ */
+static inline int
+lpfc_intr_state_check(struct lpfc_hba *phba)
+{
+       /* If the pci channel is offline, ignore all the interrupts */
+       if (unlikely(pci_channel_offline(phba->pcidev)))
+               return -EIO;
+
+       /* Update device level interrupt statistics */
+       phba->sli.slistat.sli_intr++;
+
+       /* Ignore all interrupts during initialization. */
+       if (unlikely(phba->link_state < LPFC_LINK_DOWN))
+               return -EIO;
+
        return 0;
 }
 
 /**
- * lpfc_sp_intr_handler - The slow-path interrupt handler of lpfc driver
+ * lpfc_sli_sp_intr_handler - Slow-path interrupt handler to SLI-3 device
  * @irq: Interrupt number.
  * @dev_id: The device context pointer.
  *
  * This function is directly called from the PCI layer as an interrupt
- * service routine when the device is enabled with MSI-X multi-message
- * interrupt mode and there are slow-path events in the HBA. However,
- * when the device is enabled with either MSI or Pin-IRQ interrupt mode,
- * this function is called as part of the device-level interrupt handler.
- * When the PCI slot is in error recovery or the HBA is undergoing
- * initialization, the interrupt handler will not process the interrupt.
- * The link attention and ELS ring attention events are handled by the
- * worker thread. The interrupt handler signals the worker thread and
- * and returns for these events. This function is called without any
- * lock held. It gets the hbalock to access and update SLI data
+ * service routine when device with SLI-3 interface spec is enabled with
+ * MSI-X multi-message interrupt mode and there are slow-path events in
+ * the HBA. However, when the device is enabled with either MSI or Pin-IRQ
+ * interrupt mode, this function is called as part of the device-level
+ * interrupt handler. When the PCI slot is in error recovery or the HBA
+ * is undergoing initialization, the interrupt handler will not process
+ * the interrupt. The link attention and ELS ring attention events are
+ * handled by the worker thread. The interrupt handler signals the worker
+ * thread and returns for these events. This function is called without
+ * any lock held. It gets the hbalock to access and update SLI data
  * structures.
  *
  * This function returns IRQ_HANDLED when interrupt is handled else it
  * returns IRQ_NONE.
  **/
 irqreturn_t
-lpfc_sp_intr_handler(int irq, void *dev_id)
+lpfc_sli_sp_intr_handler(int irq, void *dev_id)
 {
        struct lpfc_hba  *phba;
        uint32_t ha_copy;
@@ -5240,13 +7714,8 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
         * individual interrupt handler in MSI-X multi-message interrupt mode
         */
        if (phba->intr_type == MSIX) {
-               /* If the pci channel is offline, ignore all the interrupts */
-               if (unlikely(pci_channel_offline(phba->pcidev)))
-                       return IRQ_NONE;
-               /* Update device-level interrupt statistics */
-               phba->sli.slistat.sli_intr++;
-               /* Ignore all interrupts during initialization. */
-               if (unlikely(phba->link_state < LPFC_LINK_DOWN))
+               /* Check device state for handling interrupt */
+               if (lpfc_intr_state_check(phba))
                        return IRQ_NONE;
                /* Need to read HA REG for slow-path events */
                spin_lock_irqsave(&phba->hbalock, iflag);
@@ -5271,7 +7740,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
                 * interrupt.
                 */
                if (unlikely(phba->hba_flag & DEFER_ERATT)) {
-                       spin_unlock_irq(&phba->hbalock);
+                       spin_unlock_irqrestore(&phba->hbalock, iflag);
                        return IRQ_NONE;
                }
 
@@ -5364,7 +7833,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
 
                if ((work_ha_copy & HA_MBATT) && (phba->sli.mbox_active)) {
                        pmb = phba->sli.mbox_active;
-                       pmbox = &pmb->mb;
+                       pmbox = &pmb->u.mb;
                        mbox = phba->mbox;
                        vport = pmb->vport;
 
@@ -5434,7 +7903,8 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
                                                        LOG_MBOX | LOG_SLI,
                                                        "0350 rc should have"
                                                        "been MBX_BUSY");
-                                               goto send_current_mbox;
+                                               if (rc != MBX_NOT_FINISHED)
+                                                       goto send_current_mbox;
                                        }
                                }
                                spin_lock_irqsave(
@@ -5471,29 +7941,29 @@ send_current_mbox:
        }
        return IRQ_HANDLED;
 
-} /* lpfc_sp_intr_handler */
+} /* lpfc_sli_sp_intr_handler */
 
 /**
- * lpfc_fp_intr_handler - The fast-path interrupt handler of lpfc driver
+ * lpfc_sli_fp_intr_handler - Fast-path interrupt handler to SLI-3 device.
  * @irq: Interrupt number.
  * @dev_id: The device context pointer.
  *
  * This function is directly called from the PCI layer as an interrupt
- * service routine when the device is enabled with MSI-X multi-message
- * interrupt mode and there is a fast-path FCP IOCB ring event in the
- * HBA. However, when the device is enabled with either MSI or Pin-IRQ
- * interrupt mode, this function is called as part of the device-level
- * interrupt handler. When the PCI slot is in error recovery or the HBA
- * is undergoing initialization, the interrupt handler will not process
- * the interrupt. The SCSI FCP fast-path ring event are handled in the
- * intrrupt context. This function is called without any lock held. It
- * gets the hbalock to access and update SLI data structures.
+ * service routine when device with SLI-3 interface spec is enabled with
+ * MSI-X multi-message interrupt mode and there is a fast-path FCP IOCB
+ * ring event in the HBA. However, when the device is enabled with either
+ * MSI or Pin-IRQ interrupt mode, this function is called as part of the
+ * device-level interrupt handler. When the PCI slot is in error recovery
+ * or the HBA is undergoing initialization, the interrupt handler will not
+ * process the interrupt. The SCSI FCP fast-path ring event are handled in
+ * the intrrupt context. This function is called without any lock held.
+ * It gets the hbalock to access and update SLI data structures.
  *
  * This function returns IRQ_HANDLED when interrupt is handled else it
  * returns IRQ_NONE.
  **/
 irqreturn_t
-lpfc_fp_intr_handler(int irq, void *dev_id)
+lpfc_sli_fp_intr_handler(int irq, void *dev_id)
 {
        struct lpfc_hba  *phba;
        uint32_t ha_copy;
@@ -5513,13 +7983,8 @@ lpfc_fp_intr_handler(int irq, void *dev_id)
         * individual interrupt handler in MSI-X multi-message interrupt mode
         */
        if (phba->intr_type == MSIX) {
-               /* If pci channel is offline, ignore all the interrupts */
-               if (unlikely(pci_channel_offline(phba->pcidev)))
-                       return IRQ_NONE;
-               /* Update device-level interrupt statistics */
-               phba->sli.slistat.sli_intr++;
-               /* Ignore all interrupts during initialization. */
-               if (unlikely(phba->link_state < LPFC_LINK_DOWN))
+               /* Check device state for handling interrupt */
+               if (lpfc_intr_state_check(phba))
                        return IRQ_NONE;
                /* Need to read HA REG for FCP ring and other ring events */
                ha_copy = readl(phba->HAregaddr);
@@ -5530,7 +7995,7 @@ lpfc_fp_intr_handler(int irq, void *dev_id)
                 * any interrupt.
                 */
                if (unlikely(phba->hba_flag & DEFER_ERATT)) {
-                       spin_unlock_irq(&phba->hbalock);
+                       spin_unlock_irqrestore(&phba->hbalock, iflag);
                        return IRQ_NONE;
                }
                writel((ha_copy & (HA_R0_CLR_MSK | HA_R1_CLR_MSK)),
@@ -5566,26 +8031,27 @@ lpfc_fp_intr_handler(int irq, void *dev_id)
                }
        }
        return IRQ_HANDLED;
-}  /* lpfc_fp_intr_handler */
+}  /* lpfc_sli_fp_intr_handler */
 
 /**
- * lpfc_intr_handler - The device-level interrupt handler of lpfc driver
+ * lpfc_sli_intr_handler - Device-level interrupt handler to SLI-3 device
  * @irq: Interrupt number.
  * @dev_id: The device context pointer.
  *
- * This function is the device-level interrupt handler called from the PCI
- * layer when either MSI or Pin-IRQ interrupt mode is enabled and there is
- * an event in the HBA which requires driver attention. This function
- * invokes the slow-path interrupt attention handling function and fast-path
- * interrupt attention handling function in turn to process the relevant
- * HBA attention events. This function is called without any lock held. It
- * gets the hbalock to access and update SLI data structures.
+ * This function is the HBA device-level interrupt handler to device with
+ * SLI-3 interface spec, called from the PCI layer when either MSI or
+ * Pin-IRQ interrupt mode is enabled and there is an event in the HBA which
+ * requires driver attention. This function invokes the slow-path interrupt
+ * attention handling function and fast-path interrupt attention handling
+ * function in turn to process the relevant HBA attention events. This
+ * function is called without any lock held. It gets the hbalock to access
+ * and update SLI data structures.
  *
  * This function returns IRQ_HANDLED when interrupt is handled, else it
  * returns IRQ_NONE.
  **/
 irqreturn_t
-lpfc_intr_handler(int irq, void *dev_id)
+lpfc_sli_intr_handler(int irq, void *dev_id)
 {
        struct lpfc_hba  *phba;
        irqreturn_t sp_irq_rc, fp_irq_rc;
@@ -5600,15 +8066,8 @@ lpfc_intr_handler(int irq, void *dev_id)
        if (unlikely(!phba))
                return IRQ_NONE;
 
-       /* If the pci channel is offline, ignore all the interrupts. */
-       if (unlikely(pci_channel_offline(phba->pcidev)))
-               return IRQ_NONE;
-
-       /* Update device level interrupt statistics */
-       phba->sli.slistat.sli_intr++;
-
-       /* Ignore all interrupts during initialization. */
-       if (unlikely(phba->link_state < LPFC_LINK_DOWN))
+       /* Check device state for handling interrupt */
+       if (lpfc_intr_state_check(phba))
                return IRQ_NONE;
 
        spin_lock(&phba->hbalock);
@@ -5650,7 +8109,7 @@ lpfc_intr_handler(int irq, void *dev_id)
        status2 >>= (4*LPFC_ELS_RING);
 
        if (status1 || (status2 & HA_RXMASK))
-               sp_irq_rc = lpfc_sp_intr_handler(irq, dev_id);
+               sp_irq_rc = lpfc_sli_sp_intr_handler(irq, dev_id);
        else
                sp_irq_rc = IRQ_NONE;
 
@@ -5670,10 +8129,3322 @@ lpfc_intr_handler(int irq, void *dev_id)
                status2 = 0;
 
        if ((status1 & HA_RXMASK) || (status2 & HA_RXMASK))
-               fp_irq_rc = lpfc_fp_intr_handler(irq, dev_id);
+               fp_irq_rc = lpfc_sli_fp_intr_handler(irq, dev_id);
        else
                fp_irq_rc = IRQ_NONE;
 
        /* Return device-level interrupt handling status */
        return (sp_irq_rc == IRQ_HANDLED) ? sp_irq_rc : fp_irq_rc;
-}  /* lpfc_intr_handler */
+}  /* lpfc_sli_intr_handler */
+
+/**
+ * lpfc_sli4_fcp_xri_abort_event_proc - Process fcp xri abort event
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked by the worker thread to process all the pending
+ * SLI4 FCP abort XRI events.
+ **/
+void lpfc_sli4_fcp_xri_abort_event_proc(struct lpfc_hba *phba)
+{
+       struct lpfc_cq_event *cq_event;
+
+       /* First, declare the fcp xri abort event has been handled */
+       spin_lock_irq(&phba->hbalock);
+       phba->hba_flag &= ~FCP_XRI_ABORT_EVENT;
+       spin_unlock_irq(&phba->hbalock);
+       /* Now, handle all the fcp xri abort events */
+       while (!list_empty(&phba->sli4_hba.sp_fcp_xri_aborted_work_queue)) {
+               /* Get the first event from the head of the event queue */
+               spin_lock_irq(&phba->hbalock);
+               list_remove_head(&phba->sli4_hba.sp_fcp_xri_aborted_work_queue,
+                                cq_event, struct lpfc_cq_event, list);
+               spin_unlock_irq(&phba->hbalock);
+               /* Notify aborted XRI for FCP work queue */
+               lpfc_sli4_fcp_xri_aborted(phba, &cq_event->cqe.wcqe_axri);
+               /* Free the event processed back to the free pool */
+               lpfc_sli4_cq_event_release(phba, cq_event);
+       }
+}
+
+/**
+ * lpfc_sli4_els_xri_abort_event_proc - Process els xri abort event
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked by the worker thread to process all the pending
+ * SLI4 els abort xri events.
+ **/
+void lpfc_sli4_els_xri_abort_event_proc(struct lpfc_hba *phba)
+{
+       struct lpfc_cq_event *cq_event;
+
+       /* First, declare the els xri abort event has been handled */
+       spin_lock_irq(&phba->hbalock);
+       phba->hba_flag &= ~ELS_XRI_ABORT_EVENT;
+       spin_unlock_irq(&phba->hbalock);
+       /* Now, handle all the els xri abort events */
+       while (!list_empty(&phba->sli4_hba.sp_els_xri_aborted_work_queue)) {
+               /* Get the first event from the head of the event queue */
+               spin_lock_irq(&phba->hbalock);
+               list_remove_head(&phba->sli4_hba.sp_els_xri_aborted_work_queue,
+                                cq_event, struct lpfc_cq_event, list);
+               spin_unlock_irq(&phba->hbalock);
+               /* Notify aborted XRI for ELS work queue */
+               lpfc_sli4_els_xri_aborted(phba, &cq_event->cqe.wcqe_axri);
+               /* Free the event processed back to the free pool */
+               lpfc_sli4_cq_event_release(phba, cq_event);
+       }
+}
+
+static void
+lpfc_sli4_iocb_param_transfer(struct lpfc_iocbq *pIocbIn,
+                             struct lpfc_iocbq *pIocbOut,
+                             struct lpfc_wcqe_complete *wcqe)
+{
+       size_t offset = offsetof(struct lpfc_iocbq, iocb);
+
+       memcpy((char *)pIocbIn + offset, (char *)pIocbOut + offset,
+              sizeof(struct lpfc_iocbq) - offset);
+       memset(&pIocbIn->sli4_info, 0,
+              sizeof(struct lpfc_sli4_rspiocb_info));
+       /* Map WCQE parameters into irspiocb parameters */
+       pIocbIn->iocb.ulpStatus = bf_get(lpfc_wcqe_c_status, wcqe);
+       if (pIocbOut->iocb_flag & LPFC_IO_FCP)
+               if (pIocbIn->iocb.ulpStatus == IOSTAT_FCP_RSP_ERROR)
+                       pIocbIn->iocb.un.fcpi.fcpi_parm =
+                                       pIocbOut->iocb.un.fcpi.fcpi_parm -
+                                       wcqe->total_data_placed;
+               else
+                       pIocbIn->iocb.un.ulpWord[4] = wcqe->parameter;
+       else
+               pIocbIn->iocb.un.ulpWord[4] = wcqe->parameter;
+       /* Load in additional WCQE parameters */
+       pIocbIn->sli4_info.hw_status = bf_get(lpfc_wcqe_c_hw_status, wcqe);
+       pIocbIn->sli4_info.bfield = 0;
+       if (bf_get(lpfc_wcqe_c_xb, wcqe))
+               pIocbIn->sli4_info.bfield |= LPFC_XB;
+       if (bf_get(lpfc_wcqe_c_pv, wcqe)) {
+               pIocbIn->sli4_info.bfield |= LPFC_PV;
+               pIocbIn->sli4_info.priority =
+                                       bf_get(lpfc_wcqe_c_priority, wcqe);
+       }
+}
+
+/**
+ * lpfc_sli4_sp_handle_async_event - Handle an asynchroous event
+ * @phba: Pointer to HBA context object.
+ * @cqe: Pointer to mailbox completion queue entry.
+ *
+ * This routine process a mailbox completion queue entry with asynchrous
+ * event.
+ *
+ * Return: true if work posted to worker thread, otherwise false.
+ **/
+static bool
+lpfc_sli4_sp_handle_async_event(struct lpfc_hba *phba, struct lpfc_mcqe *mcqe)
+{
+       struct lpfc_cq_event *cq_event;
+       unsigned long iflags;
+
+       lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+                       "0392 Async Event: word0:x%x, word1:x%x, "
+                       "word2:x%x, word3:x%x\n", mcqe->word0,
+                       mcqe->mcqe_tag0, mcqe->mcqe_tag1, mcqe->trailer);
+
+       /* Allocate a new internal CQ_EVENT entry */
+       cq_event = lpfc_sli4_cq_event_alloc(phba);
+       if (!cq_event) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0394 Failed to allocate CQ_EVENT entry\n");
+               return false;
+       }
+
+       /* Move the CQE into an asynchronous event entry */
+       memcpy(&cq_event->cqe, mcqe, sizeof(struct lpfc_mcqe));
+       spin_lock_irqsave(&phba->hbalock, iflags);
+       list_add_tail(&cq_event->list, &phba->sli4_hba.sp_asynce_work_queue);
+       /* Set the async event flag */
+       phba->hba_flag |= ASYNC_EVENT;
+       spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+       return true;
+}
+
+/**
+ * lpfc_sli4_sp_handle_mbox_event - Handle a mailbox completion event
+ * @phba: Pointer to HBA context object.
+ * @cqe: Pointer to mailbox completion queue entry.
+ *
+ * This routine process a mailbox completion queue entry with mailbox
+ * completion event.
+ *
+ * Return: true if work posted to worker thread, otherwise false.
+ **/
+static bool
+lpfc_sli4_sp_handle_mbox_event(struct lpfc_hba *phba, struct lpfc_mcqe *mcqe)
+{
+       uint32_t mcqe_status;
+       MAILBOX_t *mbox, *pmbox;
+       struct lpfc_mqe *mqe;
+       struct lpfc_vport *vport;
+       struct lpfc_nodelist *ndlp;
+       struct lpfc_dmabuf *mp;
+       unsigned long iflags;
+       LPFC_MBOXQ_t *pmb;
+       bool workposted = false;
+       int rc;
+
+       /* If not a mailbox complete MCQE, out by checking mailbox consume */
+       if (!bf_get(lpfc_trailer_completed, mcqe))
+               goto out_no_mqe_complete;
+
+       /* Get the reference to the active mbox command */
+       spin_lock_irqsave(&phba->hbalock, iflags);
+       pmb = phba->sli.mbox_active;
+       if (unlikely(!pmb)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+                               "1832 No pending MBOX command to handle\n");
+               spin_unlock_irqrestore(&phba->hbalock, iflags);
+               goto out_no_mqe_complete;
+       }
+       spin_unlock_irqrestore(&phba->hbalock, iflags);
+       mqe = &pmb->u.mqe;
+       pmbox = (MAILBOX_t *)&pmb->u.mqe;
+       mbox = phba->mbox;
+       vport = pmb->vport;
+
+       /* Reset heartbeat timer */
+       phba->last_completion_time = jiffies;
+       del_timer(&phba->sli.mbox_tmo);
+
+       /* Move mbox data to caller's mailbox region, do endian swapping */
+       if (pmb->mbox_cmpl && mbox)
+               lpfc_sli_pcimem_bcopy(mbox, mqe, sizeof(struct lpfc_mqe));
+       /* Set the mailbox status with SLI4 range 0x4000 */
+       mcqe_status = bf_get(lpfc_mcqe_status, mcqe);
+       if (mcqe_status != MB_CQE_STATUS_SUCCESS)
+               bf_set(lpfc_mqe_status, mqe,
+                      (LPFC_MBX_ERROR_RANGE | mcqe_status));
+
+       if (pmb->mbox_flag & LPFC_MBX_IMED_UNREG) {
+               pmb->mbox_flag &= ~LPFC_MBX_IMED_UNREG;
+               lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_MBOX_VPORT,
+                                     "MBOX dflt rpi: status:x%x rpi:x%x",
+                                     mcqe_status,
+                                     pmbox->un.varWords[0], 0);
+               if (mcqe_status == MB_CQE_STATUS_SUCCESS) {
+                       mp = (struct lpfc_dmabuf *)(pmb->context1);
+                       ndlp = (struct lpfc_nodelist *)pmb->context2;
+                       /* Reg_LOGIN of dflt RPI was successful. Now lets get
+                        * RID of the PPI using the same mbox buffer.
+                        */
+                       lpfc_unreg_login(phba, vport->vpi,
+                                        pmbox->un.varWords[0], pmb);
+                       pmb->mbox_cmpl = lpfc_mbx_cmpl_dflt_rpi;
+                       pmb->context1 = mp;
+                       pmb->context2 = ndlp;
+                       pmb->vport = vport;
+                       rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
+                       if (rc != MBX_BUSY)
+                               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX |
+                                               LOG_SLI, "0385 rc should "
+                                               "have been MBX_BUSY\n");
+                       if (rc != MBX_NOT_FINISHED)
+                               goto send_current_mbox;
+               }
+       }
+       spin_lock_irqsave(&phba->pport->work_port_lock, iflags);
+       phba->pport->work_port_events &= ~WORKER_MBOX_TMO;
+       spin_unlock_irqrestore(&phba->pport->work_port_lock, iflags);
+
+       /* There is mailbox completion work to do */
+       spin_lock_irqsave(&phba->hbalock, iflags);
+       __lpfc_mbox_cmpl_put(phba, pmb);
+       phba->work_ha |= HA_MBATT;
+       spin_unlock_irqrestore(&phba->hbalock, iflags);
+       workposted = true;
+
+send_current_mbox:
+       spin_lock_irqsave(&phba->hbalock, iflags);
+       /* Release the mailbox command posting token */
+       phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+       /* Setting active mailbox pointer need to be in sync to flag clear */
+       phba->sli.mbox_active = NULL;
+       spin_unlock_irqrestore(&phba->hbalock, iflags);
+       /* Wake up worker thread to post the next pending mailbox command */
+       lpfc_worker_wake_up(phba);
+out_no_mqe_complete:
+       if (bf_get(lpfc_trailer_consumed, mcqe))
+               lpfc_sli4_mq_release(phba->sli4_hba.mbx_wq);
+       return workposted;
+}
+
+/**
+ * lpfc_sli4_sp_handle_mcqe - Process a mailbox completion queue entry
+ * @phba: Pointer to HBA context object.
+ * @cqe: Pointer to mailbox completion queue entry.
+ *
+ * This routine process a mailbox completion queue entry, it invokes the
+ * proper mailbox complete handling or asynchrous event handling routine
+ * according to the MCQE's async bit.
+ *
+ * Return: true if work posted to worker thread, otherwise false.
+ **/
+static bool
+lpfc_sli4_sp_handle_mcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe)
+{
+       struct lpfc_mcqe mcqe;
+       bool workposted;
+
+       /* Copy the mailbox MCQE and convert endian order as needed */
+       lpfc_sli_pcimem_bcopy(cqe, &mcqe, sizeof(struct lpfc_mcqe));
+
+       /* Invoke the proper event handling routine */
+       if (!bf_get(lpfc_trailer_async, &mcqe))
+               workposted = lpfc_sli4_sp_handle_mbox_event(phba, &mcqe);
+       else
+               workposted = lpfc_sli4_sp_handle_async_event(phba, &mcqe);
+       return workposted;
+}
+
+/**
+ * lpfc_sli4_sp_handle_els_wcqe - Handle els work-queue completion event
+ * @phba: Pointer to HBA context object.
+ * @wcqe: Pointer to work-queue completion queue entry.
+ *
+ * This routine handles an ELS work-queue completion event.
+ *
+ * Return: true if work posted to worker thread, otherwise false.
+ **/
+static bool
+lpfc_sli4_sp_handle_els_wcqe(struct lpfc_hba *phba,
+                            struct lpfc_wcqe_complete *wcqe)
+{
+       struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
+       struct lpfc_iocbq *cmdiocbq;
+       struct lpfc_iocbq *irspiocbq;
+       unsigned long iflags;
+       bool workposted = false;
+
+       spin_lock_irqsave(&phba->hbalock, iflags);
+       pring->stats.iocb_event++;
+       /* Look up the ELS command IOCB and create pseudo response IOCB */
+       cmdiocbq = lpfc_sli_iocbq_lookup_by_tag(phba, pring,
+                               bf_get(lpfc_wcqe_c_request_tag, wcqe));
+       spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+       if (unlikely(!cmdiocbq)) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+                               "0386 ELS complete with no corresponding "
+                               "cmdiocb: iotag (%d)\n",
+                               bf_get(lpfc_wcqe_c_request_tag, wcqe));
+               return workposted;
+       }
+
+       /* Fake the irspiocbq and copy necessary response information */
+       irspiocbq = lpfc_sli_get_iocbq(phba);
+       if (!irspiocbq) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0387 Failed to allocate an iocbq\n");
+               return workposted;
+       }
+       lpfc_sli4_iocb_param_transfer(irspiocbq, cmdiocbq, wcqe);
+
+       /* Add the irspiocb to the response IOCB work list */
+       spin_lock_irqsave(&phba->hbalock, iflags);
+       list_add_tail(&irspiocbq->list, &phba->sli4_hba.sp_rspiocb_work_queue);
+       /* Indicate ELS ring attention */
+       phba->work_ha |= (HA_R0ATT << (4*LPFC_ELS_RING));
+       spin_unlock_irqrestore(&phba->hbalock, iflags);
+       workposted = true;
+
+       return workposted;
+}
+
+/**
+ * lpfc_sli4_sp_handle_rel_wcqe - Handle slow-path WQ entry consumed event
+ * @phba: Pointer to HBA context object.
+ * @wcqe: Pointer to work-queue completion queue entry.
+ *
+ * This routine handles slow-path WQ entry comsumed event by invoking the
+ * proper WQ release routine to the slow-path WQ.
+ **/
+static void
+lpfc_sli4_sp_handle_rel_wcqe(struct lpfc_hba *phba,
+                            struct lpfc_wcqe_release *wcqe)
+{
+       /* Check for the slow-path ELS work queue */
+       if (bf_get(lpfc_wcqe_r_wq_id, wcqe) == phba->sli4_hba.els_wq->queue_id)
+               lpfc_sli4_wq_release(phba->sli4_hba.els_wq,
+                                    bf_get(lpfc_wcqe_r_wqe_index, wcqe));
+       else
+               lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+                               "2579 Slow-path wqe consume event carries "
+                               "miss-matched qid: wcqe-qid=x%x, sp-qid=x%x\n",
+                               bf_get(lpfc_wcqe_r_wqe_index, wcqe),
+                               phba->sli4_hba.els_wq->queue_id);
+}
+
+/**
+ * lpfc_sli4_sp_handle_abort_xri_wcqe - Handle a xri abort event
+ * @phba: Pointer to HBA context object.
+ * @cq: Pointer to a WQ completion queue.
+ * @wcqe: Pointer to work-queue completion queue entry.
+ *
+ * This routine handles an XRI abort event.
+ *
+ * Return: true if work posted to worker thread, otherwise false.
+ **/
+static bool
+lpfc_sli4_sp_handle_abort_xri_wcqe(struct lpfc_hba *phba,
+                                  struct lpfc_queue *cq,
+                                  struct sli4_wcqe_xri_aborted *wcqe)
+{
+       bool workposted = false;
+       struct lpfc_cq_event *cq_event;
+       unsigned long iflags;
+
+       /* Allocate a new internal CQ_EVENT entry */
+       cq_event = lpfc_sli4_cq_event_alloc(phba);
+       if (!cq_event) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0602 Failed to allocate CQ_EVENT entry\n");
+               return false;
+       }
+
+       /* Move the CQE into the proper xri abort event list */
+       memcpy(&cq_event->cqe, wcqe, sizeof(struct sli4_wcqe_xri_aborted));
+       switch (cq->subtype) {
+       case LPFC_FCP:
+               spin_lock_irqsave(&phba->hbalock, iflags);
+               list_add_tail(&cq_event->list,
+                             &phba->sli4_hba.sp_fcp_xri_aborted_work_queue);
+               /* Set the fcp xri abort event flag */
+               phba->hba_flag |= FCP_XRI_ABORT_EVENT;
+               spin_unlock_irqrestore(&phba->hbalock, iflags);
+               workposted = true;
+               break;
+       case LPFC_ELS:
+               spin_lock_irqsave(&phba->hbalock, iflags);
+               list_add_tail(&cq_event->list,
+                             &phba->sli4_hba.sp_els_xri_aborted_work_queue);
+               /* Set the els xri abort event flag */
+               phba->hba_flag |= ELS_XRI_ABORT_EVENT;
+               spin_unlock_irqrestore(&phba->hbalock, iflags);
+               workposted = true;
+               break;
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0603 Invalid work queue CQE subtype (x%x)\n",
+                               cq->subtype);
+               workposted = false;
+               break;
+       }
+       return workposted;
+}
+
+/**
+ * lpfc_sli4_sp_handle_wcqe - Process a work-queue completion queue entry
+ * @phba: Pointer to HBA context object.
+ * @cq: Pointer to the completion queue.
+ * @wcqe: Pointer to a completion queue entry.
+ *
+ * This routine process a slow-path work-queue completion queue entry.
+ *
+ * Return: true if work posted to worker thread, otherwise false.
+ **/
+static bool
+lpfc_sli4_sp_handle_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
+                        struct lpfc_cqe *cqe)
+{
+       struct lpfc_wcqe_complete wcqe;
+       bool workposted = false;
+
+       /* Copy the work queue CQE and convert endian order if needed */
+       lpfc_sli_pcimem_bcopy(cqe, &wcqe, sizeof(struct lpfc_cqe));
+
+       /* Check and process for different type of WCQE and dispatch */
+       switch (bf_get(lpfc_wcqe_c_code, &wcqe)) {
+       case CQE_CODE_COMPL_WQE:
+               /* Process the WQ complete event */
+               workposted = lpfc_sli4_sp_handle_els_wcqe(phba,
+                                       (struct lpfc_wcqe_complete *)&wcqe);
+               break;
+       case CQE_CODE_RELEASE_WQE:
+               /* Process the WQ release event */
+               lpfc_sli4_sp_handle_rel_wcqe(phba,
+                                       (struct lpfc_wcqe_release *)&wcqe);
+               break;
+       case CQE_CODE_XRI_ABORTED:
+               /* Process the WQ XRI abort event */
+               workposted = lpfc_sli4_sp_handle_abort_xri_wcqe(phba, cq,
+                                       (struct sli4_wcqe_xri_aborted *)&wcqe);
+               break;
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0388 Not a valid WCQE code: x%x\n",
+                               bf_get(lpfc_wcqe_c_code, &wcqe));
+               break;
+       }
+       return workposted;
+}
+
+/**
+ * lpfc_sli4_sp_handle_rcqe - Process a receive-queue completion queue entry
+ * @phba: Pointer to HBA context object.
+ * @rcqe: Pointer to receive-queue completion queue entry.
+ *
+ * This routine process a receive-queue completion queue entry.
+ *
+ * Return: true if work posted to worker thread, otherwise false.
+ **/
+static bool
+lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe)
+{
+       struct lpfc_rcqe rcqe;
+       bool workposted = false;
+       struct lpfc_queue *hrq = phba->sli4_hba.hdr_rq;
+       struct lpfc_queue *drq = phba->sli4_hba.dat_rq;
+       struct hbq_dmabuf *dma_buf;
+       uint32_t status;
+       unsigned long iflags;
+
+       /* Copy the receive queue CQE and convert endian order if needed */
+       lpfc_sli_pcimem_bcopy(cqe, &rcqe, sizeof(struct lpfc_rcqe));
+       lpfc_sli4_rq_release(hrq, drq);
+       if (bf_get(lpfc_rcqe_code, &rcqe) != CQE_CODE_RECEIVE)
+               goto out;
+       if (bf_get(lpfc_rcqe_rq_id, &rcqe) != hrq->queue_id)
+               goto out;
+
+       status = bf_get(lpfc_rcqe_status, &rcqe);
+       switch (status) {
+       case FC_STATUS_RQ_BUF_LEN_EXCEEDED:
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "2537 Receive Frame Truncated!!\n");
+       case FC_STATUS_RQ_SUCCESS:
+               spin_lock_irqsave(&phba->hbalock, iflags);
+               dma_buf = lpfc_sli_hbqbuf_get(&phba->hbqs[0].hbq_buffer_list);
+               if (!dma_buf) {
+                       spin_unlock_irqrestore(&phba->hbalock, iflags);
+                       goto out;
+               }
+               memcpy(&dma_buf->rcqe, &rcqe, sizeof(rcqe));
+               /* save off the frame for the word thread to process */
+               list_add_tail(&dma_buf->dbuf.list, &phba->rb_pend_list);
+               /* Frame received */
+               phba->hba_flag |= HBA_RECEIVE_BUFFER;
+               spin_unlock_irqrestore(&phba->hbalock, iflags);
+               workposted = true;
+               break;
+       case FC_STATUS_INSUFF_BUF_NEED_BUF:
+       case FC_STATUS_INSUFF_BUF_FRM_DISC:
+               /* Post more buffers if possible */
+               spin_lock_irqsave(&phba->hbalock, iflags);
+               phba->hba_flag |= HBA_POST_RECEIVE_BUFFER;
+               spin_unlock_irqrestore(&phba->hbalock, iflags);
+               workposted = true;
+               break;
+       }
+out:
+       return workposted;
+
+}
+
+/**
+ * lpfc_sli4_sp_handle_eqe - Process a slow-path event queue entry
+ * @phba: Pointer to HBA context object.
+ * @eqe: Pointer to fast-path event queue entry.
+ *
+ * This routine process a event queue entry from the slow-path event queue.
+ * It will check the MajorCode and MinorCode to determine this is for a
+ * completion event on a completion queue, if not, an error shall be logged
+ * and just return. Otherwise, it will get to the corresponding completion
+ * queue and process all the entries on that completion queue, rearm the
+ * completion queue, and then return.
+ *
+ **/
+static void
+lpfc_sli4_sp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe)
+{
+       struct lpfc_queue *cq = NULL, *childq, *speq;
+       struct lpfc_cqe *cqe;
+       bool workposted = false;
+       int ecount = 0;
+       uint16_t cqid;
+
+       if (bf_get(lpfc_eqe_major_code, eqe) != 0 ||
+           bf_get(lpfc_eqe_minor_code, eqe) != 0) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0359 Not a valid slow-path completion "
+                               "event: majorcode=x%x, minorcode=x%x\n",
+                               bf_get(lpfc_eqe_major_code, eqe),
+                               bf_get(lpfc_eqe_minor_code, eqe));
+               return;
+       }
+
+       /* Get the reference to the corresponding CQ */
+       cqid = bf_get(lpfc_eqe_resource_id, eqe);
+
+       /* Search for completion queue pointer matching this cqid */
+       speq = phba->sli4_hba.sp_eq;
+       list_for_each_entry(childq, &speq->child_list, list) {
+               if (childq->queue_id == cqid) {
+                       cq = childq;
+                       break;
+               }
+       }
+       if (unlikely(!cq)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0365 Slow-path CQ identifier (%d) does "
+                               "not exist\n", cqid);
+               return;
+       }
+
+       /* Process all the entries to the CQ */
+       switch (cq->type) {
+       case LPFC_MCQ:
+               while ((cqe = lpfc_sli4_cq_get(cq))) {
+                       workposted |= lpfc_sli4_sp_handle_mcqe(phba, cqe);
+                       if (!(++ecount % LPFC_GET_QE_REL_INT))
+                               lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM);
+               }
+               break;
+       case LPFC_WCQ:
+               while ((cqe = lpfc_sli4_cq_get(cq))) {
+                       workposted |= lpfc_sli4_sp_handle_wcqe(phba, cq, cqe);
+                       if (!(++ecount % LPFC_GET_QE_REL_INT))
+                               lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM);
+               }
+               break;
+       case LPFC_RCQ:
+               while ((cqe = lpfc_sli4_cq_get(cq))) {
+                       workposted |= lpfc_sli4_sp_handle_rcqe(phba, cqe);
+                       if (!(++ecount % LPFC_GET_QE_REL_INT))
+                               lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM);
+               }
+               break;
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0370 Invalid completion queue type (%d)\n",
+                               cq->type);
+               return;
+       }
+
+       /* Catch the no cq entry condition, log an error */
+       if (unlikely(ecount == 0))
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0371 No entry from the CQ: identifier "
+                               "(x%x), type (%d)\n", cq->queue_id, cq->type);
+
+       /* In any case, flash and re-arm the RCQ */
+       lpfc_sli4_cq_release(cq, LPFC_QUEUE_REARM);
+
+       /* wake up worker thread if there are works to be done */
+       if (workposted)
+               lpfc_worker_wake_up(phba);
+}
+
+/**
+ * lpfc_sli4_fp_handle_fcp_wcqe - Process fast-path work queue completion entry
+ * @eqe: Pointer to fast-path completion queue entry.
+ *
+ * This routine process a fast-path work queue completion entry from fast-path
+ * event queue for FCP command response completion.
+ **/
+static void
+lpfc_sli4_fp_handle_fcp_wcqe(struct lpfc_hba *phba,
+                            struct lpfc_wcqe_complete *wcqe)
+{
+       struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_FCP_RING];
+       struct lpfc_iocbq *cmdiocbq;
+       struct lpfc_iocbq irspiocbq;
+       unsigned long iflags;
+
+       spin_lock_irqsave(&phba->hbalock, iflags);
+       pring->stats.iocb_event++;
+       spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+       /* Check for response status */
+       if (unlikely(bf_get(lpfc_wcqe_c_status, wcqe))) {
+               /* If resource errors reported from HBA, reduce queue
+                * depth of the SCSI device.
+                */
+               if ((bf_get(lpfc_wcqe_c_status, wcqe) ==
+                    IOSTAT_LOCAL_REJECT) &&
+                   (wcqe->parameter == IOERR_NO_RESOURCES)) {
+                       phba->lpfc_rampdown_queue_depth(phba);
+               }
+               /* Log the error status */
+               lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+                               "0373 FCP complete error: status=x%x, "
+                               "hw_status=x%x, total_data_specified=%d, "
+                               "parameter=x%x, word3=x%x\n",
+                               bf_get(lpfc_wcqe_c_status, wcqe),
+                               bf_get(lpfc_wcqe_c_hw_status, wcqe),
+                               wcqe->total_data_placed, wcqe->parameter,
+                               wcqe->word3);
+       }
+
+       /* Look up the FCP command IOCB and create pseudo response IOCB */
+       spin_lock_irqsave(&phba->hbalock, iflags);
+       cmdiocbq = lpfc_sli_iocbq_lookup_by_tag(phba, pring,
+                               bf_get(lpfc_wcqe_c_request_tag, wcqe));
+       spin_unlock_irqrestore(&phba->hbalock, iflags);
+       if (unlikely(!cmdiocbq)) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+                               "0374 FCP complete with no corresponding "
+                               "cmdiocb: iotag (%d)\n",
+                               bf_get(lpfc_wcqe_c_request_tag, wcqe));
+               return;
+       }
+       if (unlikely(!cmdiocbq->iocb_cmpl)) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+                               "0375 FCP cmdiocb not callback function "
+                               "iotag: (%d)\n",
+                               bf_get(lpfc_wcqe_c_request_tag, wcqe));
+               return;
+       }
+
+       /* Fake the irspiocb and copy necessary response information */
+       lpfc_sli4_iocb_param_transfer(&irspiocbq, cmdiocbq, wcqe);
+
+       /* Pass the cmd_iocb and the rsp state to the upper layer */
+       (cmdiocbq->iocb_cmpl)(phba, cmdiocbq, &irspiocbq);
+}
+
+/**
+ * lpfc_sli4_fp_handle_rel_wcqe - Handle fast-path WQ entry consumed event
+ * @phba: Pointer to HBA context object.
+ * @cq: Pointer to completion queue.
+ * @wcqe: Pointer to work-queue completion queue entry.
+ *
+ * This routine handles an fast-path WQ entry comsumed event by invoking the
+ * proper WQ release routine to the slow-path WQ.
+ **/
+static void
+lpfc_sli4_fp_handle_rel_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
+                            struct lpfc_wcqe_release *wcqe)
+{
+       struct lpfc_queue *childwq;
+       bool wqid_matched = false;
+       uint16_t fcp_wqid;
+
+       /* Check for fast-path FCP work queue release */
+       fcp_wqid = bf_get(lpfc_wcqe_r_wq_id, wcqe);
+       list_for_each_entry(childwq, &cq->child_list, list) {
+               if (childwq->queue_id == fcp_wqid) {
+                       lpfc_sli4_wq_release(childwq,
+                                       bf_get(lpfc_wcqe_r_wqe_index, wcqe));
+                       wqid_matched = true;
+                       break;
+               }
+       }
+       /* Report warning log message if no match found */
+       if (wqid_matched != true)
+               lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+                               "2580 Fast-path wqe consume event carries "
+                               "miss-matched qid: wcqe-qid=x%x\n", fcp_wqid);
+}
+
+/**
+ * lpfc_sli4_fp_handle_wcqe - Process fast-path work queue completion entry
+ * @cq: Pointer to the completion queue.
+ * @eqe: Pointer to fast-path completion queue entry.
+ *
+ * This routine process a fast-path work queue completion entry from fast-path
+ * event queue for FCP command response completion.
+ **/
+static int
+lpfc_sli4_fp_handle_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
+                        struct lpfc_cqe *cqe)
+{
+       struct lpfc_wcqe_release wcqe;
+       bool workposted = false;
+
+       /* Copy the work queue CQE and convert endian order if needed */
+       lpfc_sli_pcimem_bcopy(cqe, &wcqe, sizeof(struct lpfc_cqe));
+
+       /* Check and process for different type of WCQE and dispatch */
+       switch (bf_get(lpfc_wcqe_c_code, &wcqe)) {
+       case CQE_CODE_COMPL_WQE:
+               /* Process the WQ complete event */
+               lpfc_sli4_fp_handle_fcp_wcqe(phba,
+                               (struct lpfc_wcqe_complete *)&wcqe);
+               break;
+       case CQE_CODE_RELEASE_WQE:
+               /* Process the WQ release event */
+               lpfc_sli4_fp_handle_rel_wcqe(phba, cq,
+                               (struct lpfc_wcqe_release *)&wcqe);
+               break;
+       case CQE_CODE_XRI_ABORTED:
+               /* Process the WQ XRI abort event */
+               workposted = lpfc_sli4_sp_handle_abort_xri_wcqe(phba, cq,
+                               (struct sli4_wcqe_xri_aborted *)&wcqe);
+               break;
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0144 Not a valid WCQE code: x%x\n",
+                               bf_get(lpfc_wcqe_c_code, &wcqe));
+               break;
+       }
+       return workposted;
+}
+
+/**
+ * lpfc_sli4_fp_handle_eqe - Process a fast-path event queue entry
+ * @phba: Pointer to HBA context object.
+ * @eqe: Pointer to fast-path event queue entry.
+ *
+ * This routine process a event queue entry from the fast-path event queue.
+ * It will check the MajorCode and MinorCode to determine this is for a
+ * completion event on a completion queue, if not, an error shall be logged
+ * and just return. Otherwise, it will get to the corresponding completion
+ * queue and process all the entries on the completion queue, rearm the
+ * completion queue, and then return.
+ **/
+static void
+lpfc_sli4_fp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe,
+                       uint32_t fcp_cqidx)
+{
+       struct lpfc_queue *cq;
+       struct lpfc_cqe *cqe;
+       bool workposted = false;
+       uint16_t cqid;
+       int ecount = 0;
+
+       if (unlikely(bf_get(lpfc_eqe_major_code, eqe) != 0) ||
+           unlikely(bf_get(lpfc_eqe_minor_code, eqe) != 0)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0366 Not a valid fast-path completion "
+                               "event: majorcode=x%x, minorcode=x%x\n",
+                               bf_get(lpfc_eqe_major_code, eqe),
+                               bf_get(lpfc_eqe_minor_code, eqe));
+               return;
+       }
+
+       cq = phba->sli4_hba.fcp_cq[fcp_cqidx];
+       if (unlikely(!cq)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0367 Fast-path completion queue does not "
+                               "exist\n");
+               return;
+       }
+
+       /* Get the reference to the corresponding CQ */
+       cqid = bf_get(lpfc_eqe_resource_id, eqe);
+       if (unlikely(cqid != cq->queue_id)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0368 Miss-matched fast-path completion "
+                               "queue identifier: eqcqid=%d, fcpcqid=%d\n",
+                               cqid, cq->queue_id);
+               return;
+       }
+
+       /* Process all the entries to the CQ */
+       while ((cqe = lpfc_sli4_cq_get(cq))) {
+               workposted |= lpfc_sli4_fp_handle_wcqe(phba, cq, cqe);
+               if (!(++ecount % LPFC_GET_QE_REL_INT))
+                       lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM);
+       }
+
+       /* Catch the no cq entry condition */
+       if (unlikely(ecount == 0))
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0369 No entry from fast-path completion "
+                               "queue fcpcqid=%d\n", cq->queue_id);
+
+       /* In any case, flash and re-arm the CQ */
+       lpfc_sli4_cq_release(cq, LPFC_QUEUE_REARM);
+
+       /* wake up worker thread if there are works to be done */
+       if (workposted)
+               lpfc_worker_wake_up(phba);
+}
+
+static void
+lpfc_sli4_eq_flush(struct lpfc_hba *phba, struct lpfc_queue *eq)
+{
+       struct lpfc_eqe *eqe;
+
+       /* walk all the EQ entries and drop on the floor */
+       while ((eqe = lpfc_sli4_eq_get(eq)))
+               ;
+
+       /* Clear and re-arm the EQ */
+       lpfc_sli4_eq_release(eq, LPFC_QUEUE_REARM);
+}
+
+/**
+ * lpfc_sli4_sp_intr_handler - Slow-path interrupt handler to SLI-4 device
+ * @irq: Interrupt number.
+ * @dev_id: The device context pointer.
+ *
+ * This function is directly called from the PCI layer as an interrupt
+ * service routine when device with SLI-4 interface spec is enabled with
+ * MSI-X multi-message interrupt mode and there are slow-path events in
+ * the HBA. However, when the device is enabled with either MSI or Pin-IRQ
+ * interrupt mode, this function is called as part of the device-level
+ * interrupt handler. When the PCI slot is in error recovery or the HBA is
+ * undergoing initialization, the interrupt handler will not process the
+ * interrupt. The link attention and ELS ring attention events are handled
+ * by the worker thread. The interrupt handler signals the worker thread
+ * and returns for these events. This function is called without any lock
+ * held. It gets the hbalock to access and update SLI data structures.
+ *
+ * This function returns IRQ_HANDLED when interrupt is handled else it
+ * returns IRQ_NONE.
+ **/
+irqreturn_t
+lpfc_sli4_sp_intr_handler(int irq, void *dev_id)
+{
+       struct lpfc_hba *phba;
+       struct lpfc_queue *speq;
+       struct lpfc_eqe *eqe;
+       unsigned long iflag;
+       int ecount = 0;
+
+       /*
+        * Get the driver's phba structure from the dev_id
+        */
+       phba = (struct lpfc_hba *)dev_id;
+
+       if (unlikely(!phba))
+               return IRQ_NONE;
+
+       /* Get to the EQ struct associated with this vector */
+       speq = phba->sli4_hba.sp_eq;
+
+       /* Check device state for handling interrupt */
+       if (unlikely(lpfc_intr_state_check(phba))) {
+               /* Check again for link_state with lock held */
+               spin_lock_irqsave(&phba->hbalock, iflag);
+               if (phba->link_state < LPFC_LINK_DOWN)
+                       /* Flush, clear interrupt, and rearm the EQ */
+                       lpfc_sli4_eq_flush(phba, speq);
+               spin_unlock_irqrestore(&phba->hbalock, iflag);
+               return IRQ_NONE;
+       }
+
+       /*
+        * Process all the event on FCP slow-path EQ
+        */
+       while ((eqe = lpfc_sli4_eq_get(speq))) {
+               lpfc_sli4_sp_handle_eqe(phba, eqe);
+               if (!(++ecount % LPFC_GET_QE_REL_INT))
+                       lpfc_sli4_eq_release(speq, LPFC_QUEUE_NOARM);
+       }
+
+       /* Always clear and re-arm the slow-path EQ */
+       lpfc_sli4_eq_release(speq, LPFC_QUEUE_REARM);
+
+       /* Catch the no cq entry condition */
+       if (unlikely(ecount == 0)) {
+               if (phba->intr_type == MSIX)
+                       /* MSI-X treated interrupt served as no EQ share INT */
+                       lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+                                       "0357 MSI-X interrupt with no EQE\n");
+               else
+                       /* Non MSI-X treated on interrupt as EQ share INT */
+                       return IRQ_NONE;
+       }
+
+       return IRQ_HANDLED;
+} /* lpfc_sli4_sp_intr_handler */
+
+/**
+ * lpfc_sli4_fp_intr_handler - Fast-path interrupt handler to SLI-4 device
+ * @irq: Interrupt number.
+ * @dev_id: The device context pointer.
+ *
+ * This function is directly called from the PCI layer as an interrupt
+ * service routine when device with SLI-4 interface spec is enabled with
+ * MSI-X multi-message interrupt mode and there is a fast-path FCP IOCB
+ * ring event in the HBA. However, when the device is enabled with either
+ * MSI or Pin-IRQ interrupt mode, this function is called as part of the
+ * device-level interrupt handler. When the PCI slot is in error recovery
+ * or the HBA is undergoing initialization, the interrupt handler will not
+ * process the interrupt. The SCSI FCP fast-path ring event are handled in
+ * the intrrupt context. This function is called without any lock held.
+ * It gets the hbalock to access and update SLI data structures. Note that,
+ * the FCP EQ to FCP CQ are one-to-one map such that the FCP EQ index is
+ * equal to that of FCP CQ index.
+ *
+ * This function returns IRQ_HANDLED when interrupt is handled else it
+ * returns IRQ_NONE.
+ **/
+irqreturn_t
+lpfc_sli4_fp_intr_handler(int irq, void *dev_id)
+{
+       struct lpfc_hba *phba;
+       struct lpfc_fcp_eq_hdl *fcp_eq_hdl;
+       struct lpfc_queue *fpeq;
+       struct lpfc_eqe *eqe;
+       unsigned long iflag;
+       int ecount = 0;
+       uint32_t fcp_eqidx;
+
+       /* Get the driver's phba structure from the dev_id */
+       fcp_eq_hdl = (struct lpfc_fcp_eq_hdl *)dev_id;
+       phba = fcp_eq_hdl->phba;
+       fcp_eqidx = fcp_eq_hdl->idx;
+
+       if (unlikely(!phba))
+               return IRQ_NONE;
+
+       /* Get to the EQ struct associated with this vector */
+       fpeq = phba->sli4_hba.fp_eq[fcp_eqidx];
+
+       /* Check device state for handling interrupt */
+       if (unlikely(lpfc_intr_state_check(phba))) {
+               /* Check again for link_state with lock held */
+               spin_lock_irqsave(&phba->hbalock, iflag);
+               if (phba->link_state < LPFC_LINK_DOWN)
+                       /* Flush, clear interrupt, and rearm the EQ */
+                       lpfc_sli4_eq_flush(phba, fpeq);
+               spin_unlock_irqrestore(&phba->hbalock, iflag);
+               return IRQ_NONE;
+       }
+
+       /*
+        * Process all the event on FCP fast-path EQ
+        */
+       while ((eqe = lpfc_sli4_eq_get(fpeq))) {
+               lpfc_sli4_fp_handle_eqe(phba, eqe, fcp_eqidx);
+               if (!(++ecount % LPFC_GET_QE_REL_INT))
+                       lpfc_sli4_eq_release(fpeq, LPFC_QUEUE_NOARM);
+       }
+
+       /* Always clear and re-arm the fast-path EQ */
+       lpfc_sli4_eq_release(fpeq, LPFC_QUEUE_REARM);
+
+       if (unlikely(ecount == 0)) {
+               if (phba->intr_type == MSIX)
+                       /* MSI-X treated interrupt served as no EQ share INT */
+                       lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+                                       "0358 MSI-X interrupt with no EQE\n");
+               else
+                       /* Non MSI-X treated on interrupt as EQ share INT */
+                       return IRQ_NONE;
+       }
+
+       return IRQ_HANDLED;
+} /* lpfc_sli4_fp_intr_handler */
+
+/**
+ * lpfc_sli4_intr_handler - Device-level interrupt handler for SLI-4 device
+ * @irq: Interrupt number.
+ * @dev_id: The device context pointer.
+ *
+ * This function is the device-level interrupt handler to device with SLI-4
+ * interface spec, called from the PCI layer when either MSI or Pin-IRQ
+ * interrupt mode is enabled and there is an event in the HBA which requires
+ * driver attention. This function invokes the slow-path interrupt attention
+ * handling function and fast-path interrupt attention handling function in
+ * turn to process the relevant HBA attention events. This function is called
+ * without any lock held. It gets the hbalock to access and update SLI data
+ * structures.
+ *
+ * This function returns IRQ_HANDLED when interrupt is handled, else it
+ * returns IRQ_NONE.
+ **/
+irqreturn_t
+lpfc_sli4_intr_handler(int irq, void *dev_id)
+{
+       struct lpfc_hba  *phba;
+       irqreturn_t sp_irq_rc, fp_irq_rc;
+       bool fp_handled = false;
+       uint32_t fcp_eqidx;
+
+       /* Get the driver's phba structure from the dev_id */
+       phba = (struct lpfc_hba *)dev_id;
+
+       if (unlikely(!phba))
+               return IRQ_NONE;
+
+       /*
+        * Invokes slow-path host attention interrupt handling as appropriate.
+        */
+       sp_irq_rc = lpfc_sli4_sp_intr_handler(irq, dev_id);
+
+       /*
+        * Invoke fast-path host attention interrupt handling as appropriate.
+        */
+       for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_eq_count; fcp_eqidx++) {
+               fp_irq_rc = lpfc_sli4_fp_intr_handler(irq,
+                                       &phba->sli4_hba.fcp_eq_hdl[fcp_eqidx]);
+               if (fp_irq_rc == IRQ_HANDLED)
+                       fp_handled |= true;
+       }
+
+       return (fp_handled == true) ? IRQ_HANDLED : sp_irq_rc;
+} /* lpfc_sli4_intr_handler */
+
+/**
+ * lpfc_sli4_queue_free - free a queue structure and associated memory
+ * @queue: The queue structure to free.
+ *
+ * This function frees a queue structure and the DMAable memeory used for
+ * the host resident queue. This function must be called after destroying the
+ * queue on the HBA.
+ **/
+void
+lpfc_sli4_queue_free(struct lpfc_queue *queue)
+{
+       struct lpfc_dmabuf *dmabuf;
+
+       if (!queue)
+               return;
+
+       while (!list_empty(&queue->page_list)) {
+               list_remove_head(&queue->page_list, dmabuf, struct lpfc_dmabuf,
+                                list);
+               dma_free_coherent(&queue->phba->pcidev->dev, PAGE_SIZE,
+                                 dmabuf->virt, dmabuf->phys);
+               kfree(dmabuf);
+       }
+       kfree(queue);
+       return;
+}
+
+/**
+ * lpfc_sli4_queue_alloc - Allocate and initialize a queue structure
+ * @phba: The HBA that this queue is being created on.
+ * @entry_size: The size of each queue entry for this queue.
+ * @entry count: The number of entries that this queue will handle.
+ *
+ * This function allocates a queue structure and the DMAable memory used for
+ * the host resident queue. This function must be called before creating the
+ * queue on the HBA.
+ **/
+struct lpfc_queue *
+lpfc_sli4_queue_alloc(struct lpfc_hba *phba, uint32_t entry_size,
+                     uint32_t entry_count)
+{
+       struct lpfc_queue *queue;
+       struct lpfc_dmabuf *dmabuf;
+       int x, total_qe_count;
+       void *dma_pointer;
+
+
+       queue = kzalloc(sizeof(struct lpfc_queue) +
+                       (sizeof(union sli4_qe) * entry_count), GFP_KERNEL);
+       if (!queue)
+               return NULL;
+       queue->page_count = (PAGE_ALIGN(entry_size * entry_count))/PAGE_SIZE;
+       INIT_LIST_HEAD(&queue->list);
+       INIT_LIST_HEAD(&queue->page_list);
+       INIT_LIST_HEAD(&queue->child_list);
+       for (x = 0, total_qe_count = 0; x < queue->page_count; x++) {
+               dmabuf = kzalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+               if (!dmabuf)
+                       goto out_fail;
+               dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev,
+                                                 PAGE_SIZE, &dmabuf->phys,
+                                                 GFP_KERNEL);
+               if (!dmabuf->virt) {
+                       kfree(dmabuf);
+                       goto out_fail;
+               }
+               dmabuf->buffer_tag = x;
+               list_add_tail(&dmabuf->list, &queue->page_list);
+               /* initialize queue's entry array */
+               dma_pointer = dmabuf->virt;
+               for (; total_qe_count < entry_count &&
+                    dma_pointer < (PAGE_SIZE + dmabuf->virt);
+                    total_qe_count++, dma_pointer += entry_size) {
+                       queue->qe[total_qe_count].address = dma_pointer;
+               }
+       }
+       queue->entry_size = entry_size;
+       queue->entry_count = entry_count;
+       queue->phba = phba;
+
+       return queue;
+out_fail:
+       lpfc_sli4_queue_free(queue);
+       return NULL;
+}
+
+/**
+ * lpfc_eq_create - Create an Event Queue on the HBA
+ * @phba: HBA structure that indicates port to create a queue on.
+ * @eq: The queue structure to use to create the event queue.
+ * @imax: The maximum interrupt per second limit.
+ *
+ * This function creates an event queue, as detailed in @eq, on a port,
+ * described by @phba by sending an EQ_CREATE mailbox command to the HBA.
+ *
+ * The @phba struct is used to send mailbox command to HBA. The @eq struct
+ * is used to get the entry count and entry size that are necessary to
+ * determine the number of pages to allocate and use for this queue. This
+ * function will send the EQ_CREATE mailbox command to the HBA to setup the
+ * event queue. This function is asynchronous and will wait for the mailbox
+ * command to finish before continuing.
+ *
+ * On success this function will return a zero. If unable to allocate enough
+ * memory this function will return ENOMEM. If the queue create mailbox command
+ * fails this function will return ENXIO.
+ **/
+uint32_t
+lpfc_eq_create(struct lpfc_hba *phba, struct lpfc_queue *eq, uint16_t imax)
+{
+       struct lpfc_mbx_eq_create *eq_create;
+       LPFC_MBOXQ_t *mbox;
+       int rc, length, status = 0;
+       struct lpfc_dmabuf *dmabuf;
+       uint32_t shdr_status, shdr_add_status;
+       union lpfc_sli4_cfg_shdr *shdr;
+       uint16_t dmult;
+
+       mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mbox)
+               return -ENOMEM;
+       length = (sizeof(struct lpfc_mbx_eq_create) -
+                 sizeof(struct lpfc_sli4_cfg_mhdr));
+       lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+                        LPFC_MBOX_OPCODE_EQ_CREATE,
+                        length, LPFC_SLI4_MBX_EMBED);
+       eq_create = &mbox->u.mqe.un.eq_create;
+       bf_set(lpfc_mbx_eq_create_num_pages, &eq_create->u.request,
+              eq->page_count);
+       bf_set(lpfc_eq_context_size, &eq_create->u.request.context,
+              LPFC_EQE_SIZE);
+       bf_set(lpfc_eq_context_valid, &eq_create->u.request.context, 1);
+       /* Calculate delay multiper from maximum interrupt per second */
+       dmult = LPFC_DMULT_CONST/imax - 1;
+       bf_set(lpfc_eq_context_delay_multi, &eq_create->u.request.context,
+              dmult);
+       switch (eq->entry_count) {
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0360 Unsupported EQ count. (%d)\n",
+                               eq->entry_count);
+               if (eq->entry_count < 256)
+                       return -EINVAL;
+               /* otherwise default to smallest count (drop through) */
+       case 256:
+               bf_set(lpfc_eq_context_count, &eq_create->u.request.context,
+                      LPFC_EQ_CNT_256);
+               break;
+       case 512:
+               bf_set(lpfc_eq_context_count, &eq_create->u.request.context,
+                      LPFC_EQ_CNT_512);
+               break;
+       case 1024:
+               bf_set(lpfc_eq_context_count, &eq_create->u.request.context,
+                      LPFC_EQ_CNT_1024);
+               break;
+       case 2048:
+               bf_set(lpfc_eq_context_count, &eq_create->u.request.context,
+                      LPFC_EQ_CNT_2048);
+               break;
+       case 4096:
+               bf_set(lpfc_eq_context_count, &eq_create->u.request.context,
+                      LPFC_EQ_CNT_4096);
+               break;
+       }
+       list_for_each_entry(dmabuf, &eq->page_list, list) {
+               eq_create->u.request.page[dmabuf->buffer_tag].addr_lo =
+                                       putPaddrLow(dmabuf->phys);
+               eq_create->u.request.page[dmabuf->buffer_tag].addr_hi =
+                                       putPaddrHigh(dmabuf->phys);
+       }
+       mbox->vport = phba->pport;
+       mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+       mbox->context1 = NULL;
+       rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+       shdr = (union lpfc_sli4_cfg_shdr *) &eq_create->header.cfg_shdr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+       if (shdr_status || shdr_add_status || rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2500 EQ_CREATE mailbox failed with "
+                               "status x%x add_status x%x, mbx status x%x\n",
+                               shdr_status, shdr_add_status, rc);
+               status = -ENXIO;
+       }
+       eq->type = LPFC_EQ;
+       eq->subtype = LPFC_NONE;
+       eq->queue_id = bf_get(lpfc_mbx_eq_create_q_id, &eq_create->u.response);
+       if (eq->queue_id == 0xFFFF)
+               status = -ENXIO;
+       eq->host_index = 0;
+       eq->hba_index = 0;
+
+       if (rc != MBX_TIMEOUT)
+               mempool_free(mbox, phba->mbox_mem_pool);
+       return status;
+}
+
+/**
+ * lpfc_cq_create - Create a Completion Queue on the HBA
+ * @phba: HBA structure that indicates port to create a queue on.
+ * @cq: The queue structure to use to create the completion queue.
+ * @eq: The event queue to bind this completion queue to.
+ *
+ * This function creates a completion queue, as detailed in @wq, on a port,
+ * described by @phba by sending a CQ_CREATE mailbox command to the HBA.
+ *
+ * The @phba struct is used to send mailbox command to HBA. The @cq struct
+ * is used to get the entry count and entry size that are necessary to
+ * determine the number of pages to allocate and use for this queue. The @eq
+ * is used to indicate which event queue to bind this completion queue to. This
+ * function will send the CQ_CREATE mailbox command to the HBA to setup the
+ * completion queue. This function is asynchronous and will wait for the mailbox
+ * command to finish before continuing.
+ *
+ * On success this function will return a zero. If unable to allocate enough
+ * memory this function will return ENOMEM. If the queue create mailbox command
+ * fails this function will return ENXIO.
+ **/
+uint32_t
+lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq,
+              struct lpfc_queue *eq, uint32_t type, uint32_t subtype)
+{
+       struct lpfc_mbx_cq_create *cq_create;
+       struct lpfc_dmabuf *dmabuf;
+       LPFC_MBOXQ_t *mbox;
+       int rc, length, status = 0;
+       uint32_t shdr_status, shdr_add_status;
+       union lpfc_sli4_cfg_shdr *shdr;
+
+       mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mbox)
+               return -ENOMEM;
+       length = (sizeof(struct lpfc_mbx_cq_create) -
+                 sizeof(struct lpfc_sli4_cfg_mhdr));
+       lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+                        LPFC_MBOX_OPCODE_CQ_CREATE,
+                        length, LPFC_SLI4_MBX_EMBED);
+       cq_create = &mbox->u.mqe.un.cq_create;
+       bf_set(lpfc_mbx_cq_create_num_pages, &cq_create->u.request,
+                   cq->page_count);
+       bf_set(lpfc_cq_context_event, &cq_create->u.request.context, 1);
+       bf_set(lpfc_cq_context_valid, &cq_create->u.request.context, 1);
+       bf_set(lpfc_cq_eq_id, &cq_create->u.request.context, eq->queue_id);
+       switch (cq->entry_count) {
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0361 Unsupported CQ count. (%d)\n",
+                               cq->entry_count);
+               if (cq->entry_count < 256)
+                       return -EINVAL;
+               /* otherwise default to smallest count (drop through) */
+       case 256:
+               bf_set(lpfc_cq_context_count, &cq_create->u.request.context,
+                      LPFC_CQ_CNT_256);
+               break;
+       case 512:
+               bf_set(lpfc_cq_context_count, &cq_create->u.request.context,
+                      LPFC_CQ_CNT_512);
+               break;
+       case 1024:
+               bf_set(lpfc_cq_context_count, &cq_create->u.request.context,
+                      LPFC_CQ_CNT_1024);
+               break;
+       }
+       list_for_each_entry(dmabuf, &cq->page_list, list) {
+               cq_create->u.request.page[dmabuf->buffer_tag].addr_lo =
+                                       putPaddrLow(dmabuf->phys);
+               cq_create->u.request.page[dmabuf->buffer_tag].addr_hi =
+                                       putPaddrHigh(dmabuf->phys);
+       }
+       rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+
+       /* The IOCTL status is embedded in the mailbox subheader. */
+       shdr = (union lpfc_sli4_cfg_shdr *) &cq_create->header.cfg_shdr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+       if (shdr_status || shdr_add_status || rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2501 CQ_CREATE mailbox failed with "
+                               "status x%x add_status x%x, mbx status x%x\n",
+                               shdr_status, shdr_add_status, rc);
+               status = -ENXIO;
+               goto out;
+       }
+       cq->queue_id = bf_get(lpfc_mbx_cq_create_q_id, &cq_create->u.response);
+       if (cq->queue_id == 0xFFFF) {
+               status = -ENXIO;
+               goto out;
+       }
+       /* link the cq onto the parent eq child list */
+       list_add_tail(&cq->list, &eq->child_list);
+       /* Set up completion queue's type and subtype */
+       cq->type = type;
+       cq->subtype = subtype;
+       cq->queue_id = bf_get(lpfc_mbx_cq_create_q_id, &cq_create->u.response);
+       cq->host_index = 0;
+       cq->hba_index = 0;
+out:
+
+       if (rc != MBX_TIMEOUT)
+               mempool_free(mbox, phba->mbox_mem_pool);
+       return status;
+}
+
+/**
+ * lpfc_mq_create - Create a mailbox Queue on the HBA
+ * @phba: HBA structure that indicates port to create a queue on.
+ * @mq: The queue structure to use to create the mailbox queue.
+ *
+ * This function creates a mailbox queue, as detailed in @mq, on a port,
+ * described by @phba by sending a MQ_CREATE mailbox command to the HBA.
+ *
+ * The @phba struct is used to send mailbox command to HBA. The @cq struct
+ * is used to get the entry count and entry size that are necessary to
+ * determine the number of pages to allocate and use for this queue. This
+ * function will send the MQ_CREATE mailbox command to the HBA to setup the
+ * mailbox queue. This function is asynchronous and will wait for the mailbox
+ * command to finish before continuing.
+ *
+ * On success this function will return a zero. If unable to allocate enough
+ * memory this function will return ENOMEM. If the queue create mailbox command
+ * fails this function will return ENXIO.
+ **/
+uint32_t
+lpfc_mq_create(struct lpfc_hba *phba, struct lpfc_queue *mq,
+              struct lpfc_queue *cq, uint32_t subtype)
+{
+       struct lpfc_mbx_mq_create *mq_create;
+       struct lpfc_dmabuf *dmabuf;
+       LPFC_MBOXQ_t *mbox;
+       int rc, length, status = 0;
+       uint32_t shdr_status, shdr_add_status;
+       union lpfc_sli4_cfg_shdr *shdr;
+
+       mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mbox)
+               return -ENOMEM;
+       length = (sizeof(struct lpfc_mbx_mq_create) -
+                 sizeof(struct lpfc_sli4_cfg_mhdr));
+       lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+                        LPFC_MBOX_OPCODE_MQ_CREATE,
+                        length, LPFC_SLI4_MBX_EMBED);
+       mq_create = &mbox->u.mqe.un.mq_create;
+       bf_set(lpfc_mbx_mq_create_num_pages, &mq_create->u.request,
+                   mq->page_count);
+       bf_set(lpfc_mq_context_cq_id, &mq_create->u.request.context,
+                   cq->queue_id);
+       bf_set(lpfc_mq_context_valid, &mq_create->u.request.context, 1);
+       switch (mq->entry_count) {
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0362 Unsupported MQ count. (%d)\n",
+                               mq->entry_count);
+               if (mq->entry_count < 16)
+                       return -EINVAL;
+               /* otherwise default to smallest count (drop through) */
+       case 16:
+               bf_set(lpfc_mq_context_count, &mq_create->u.request.context,
+                      LPFC_MQ_CNT_16);
+               break;
+       case 32:
+               bf_set(lpfc_mq_context_count, &mq_create->u.request.context,
+                      LPFC_MQ_CNT_32);
+               break;
+       case 64:
+               bf_set(lpfc_mq_context_count, &mq_create->u.request.context,
+                      LPFC_MQ_CNT_64);
+               break;
+       case 128:
+               bf_set(lpfc_mq_context_count, &mq_create->u.request.context,
+                      LPFC_MQ_CNT_128);
+               break;
+       }
+       list_for_each_entry(dmabuf, &mq->page_list, list) {
+               mq_create->u.request.page[dmabuf->buffer_tag].addr_lo =
+                                       putPaddrLow(dmabuf->phys);
+               mq_create->u.request.page[dmabuf->buffer_tag].addr_hi =
+                                       putPaddrHigh(dmabuf->phys);
+       }
+       rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+       /* The IOCTL status is embedded in the mailbox subheader. */
+       shdr = (union lpfc_sli4_cfg_shdr *) &mq_create->header.cfg_shdr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+       if (shdr_status || shdr_add_status || rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2502 MQ_CREATE mailbox failed with "
+                               "status x%x add_status x%x, mbx status x%x\n",
+                               shdr_status, shdr_add_status, rc);
+               status = -ENXIO;
+               goto out;
+       }
+       mq->queue_id = bf_get(lpfc_mbx_mq_create_q_id, &mq_create->u.response);
+       if (mq->queue_id == 0xFFFF) {
+               status = -ENXIO;
+               goto out;
+       }
+       mq->type = LPFC_MQ;
+       mq->subtype = subtype;
+       mq->host_index = 0;
+       mq->hba_index = 0;
+
+       /* link the mq onto the parent cq child list */
+       list_add_tail(&mq->list, &cq->child_list);
+out:
+       if (rc != MBX_TIMEOUT)
+               mempool_free(mbox, phba->mbox_mem_pool);
+       return status;
+}
+
+/**
+ * lpfc_wq_create - Create a Work Queue on the HBA
+ * @phba: HBA structure that indicates port to create a queue on.
+ * @wq: The queue structure to use to create the work queue.
+ * @cq: The completion queue to bind this work queue to.
+ * @subtype: The subtype of the work queue indicating its functionality.
+ *
+ * This function creates a work queue, as detailed in @wq, on a port, described
+ * by @phba by sending a WQ_CREATE mailbox command to the HBA.
+ *
+ * The @phba struct is used to send mailbox command to HBA. The @wq struct
+ * is used to get the entry count and entry size that are necessary to
+ * determine the number of pages to allocate and use for this queue. The @cq
+ * is used to indicate which completion queue to bind this work queue to. This
+ * function will send the WQ_CREATE mailbox command to the HBA to setup the
+ * work queue. This function is asynchronous and will wait for the mailbox
+ * command to finish before continuing.
+ *
+ * On success this function will return a zero. If unable to allocate enough
+ * memory this function will return ENOMEM. If the queue create mailbox command
+ * fails this function will return ENXIO.
+ **/
+uint32_t
+lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq,
+              struct lpfc_queue *cq, uint32_t subtype)
+{
+       struct lpfc_mbx_wq_create *wq_create;
+       struct lpfc_dmabuf *dmabuf;
+       LPFC_MBOXQ_t *mbox;
+       int rc, length, status = 0;
+       uint32_t shdr_status, shdr_add_status;
+       union lpfc_sli4_cfg_shdr *shdr;
+
+       mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mbox)
+               return -ENOMEM;
+       length = (sizeof(struct lpfc_mbx_wq_create) -
+                 sizeof(struct lpfc_sli4_cfg_mhdr));
+       lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+                        LPFC_MBOX_OPCODE_FCOE_WQ_CREATE,
+                        length, LPFC_SLI4_MBX_EMBED);
+       wq_create = &mbox->u.mqe.un.wq_create;
+       bf_set(lpfc_mbx_wq_create_num_pages, &wq_create->u.request,
+                   wq->page_count);
+       bf_set(lpfc_mbx_wq_create_cq_id, &wq_create->u.request,
+                   cq->queue_id);
+       list_for_each_entry(dmabuf, &wq->page_list, list) {
+               wq_create->u.request.page[dmabuf->buffer_tag].addr_lo =
+                                       putPaddrLow(dmabuf->phys);
+               wq_create->u.request.page[dmabuf->buffer_tag].addr_hi =
+                                       putPaddrHigh(dmabuf->phys);
+       }
+       rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+       /* The IOCTL status is embedded in the mailbox subheader. */
+       shdr = (union lpfc_sli4_cfg_shdr *) &wq_create->header.cfg_shdr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+       if (shdr_status || shdr_add_status || rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2503 WQ_CREATE mailbox failed with "
+                               "status x%x add_status x%x, mbx status x%x\n",
+                               shdr_status, shdr_add_status, rc);
+               status = -ENXIO;
+               goto out;
+       }
+       wq->queue_id = bf_get(lpfc_mbx_wq_create_q_id, &wq_create->u.response);
+       if (wq->queue_id == 0xFFFF) {
+               status = -ENXIO;
+               goto out;
+       }
+       wq->type = LPFC_WQ;
+       wq->subtype = subtype;
+       wq->host_index = 0;
+       wq->hba_index = 0;
+
+       /* link the wq onto the parent cq child list */
+       list_add_tail(&wq->list, &cq->child_list);
+out:
+       if (rc == MBX_TIMEOUT)
+               mempool_free(mbox, phba->mbox_mem_pool);
+       return status;
+}
+
+/**
+ * lpfc_rq_create - Create a Receive Queue on the HBA
+ * @phba: HBA structure that indicates port to create a queue on.
+ * @hrq: The queue structure to use to create the header receive queue.
+ * @drq: The queue structure to use to create the data receive queue.
+ * @cq: The completion queue to bind this work queue to.
+ *
+ * This function creates a receive buffer queue pair , as detailed in @hrq and
+ * @drq, on a port, described by @phba by sending a RQ_CREATE mailbox command
+ * to the HBA.
+ *
+ * The @phba struct is used to send mailbox command to HBA. The @drq and @hrq
+ * struct is used to get the entry count that is necessary to determine the
+ * number of pages to use for this queue. The @cq is used to indicate which
+ * completion queue to bind received buffers that are posted to these queues to.
+ * This function will send the RQ_CREATE mailbox command to the HBA to setup the
+ * receive queue pair. This function is asynchronous and will wait for the
+ * mailbox command to finish before continuing.
+ *
+ * On success this function will return a zero. If unable to allocate enough
+ * memory this function will return ENOMEM. If the queue create mailbox command
+ * fails this function will return ENXIO.
+ **/
+uint32_t
+lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
+              struct lpfc_queue *drq, struct lpfc_queue *cq, uint32_t subtype)
+{
+       struct lpfc_mbx_rq_create *rq_create;
+       struct lpfc_dmabuf *dmabuf;
+       LPFC_MBOXQ_t *mbox;
+       int rc, length, status = 0;
+       uint32_t shdr_status, shdr_add_status;
+       union lpfc_sli4_cfg_shdr *shdr;
+
+       if (hrq->entry_count != drq->entry_count)
+               return -EINVAL;
+       mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mbox)
+               return -ENOMEM;
+       length = (sizeof(struct lpfc_mbx_rq_create) -
+                 sizeof(struct lpfc_sli4_cfg_mhdr));
+       lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+                        LPFC_MBOX_OPCODE_FCOE_RQ_CREATE,
+                        length, LPFC_SLI4_MBX_EMBED);
+       rq_create = &mbox->u.mqe.un.rq_create;
+       switch (hrq->entry_count) {
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "2535 Unsupported RQ count. (%d)\n",
+                               hrq->entry_count);
+               if (hrq->entry_count < 512)
+                       return -EINVAL;
+               /* otherwise default to smallest count (drop through) */
+       case 512:
+               bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context,
+                      LPFC_RQ_RING_SIZE_512);
+               break;
+       case 1024:
+               bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context,
+                      LPFC_RQ_RING_SIZE_1024);
+               break;
+       case 2048:
+               bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context,
+                      LPFC_RQ_RING_SIZE_2048);
+               break;
+       case 4096:
+               bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context,
+                      LPFC_RQ_RING_SIZE_4096);
+               break;
+       }
+       bf_set(lpfc_rq_context_cq_id, &rq_create->u.request.context,
+              cq->queue_id);
+       bf_set(lpfc_mbx_rq_create_num_pages, &rq_create->u.request,
+              hrq->page_count);
+       bf_set(lpfc_rq_context_buf_size, &rq_create->u.request.context,
+              LPFC_HDR_BUF_SIZE);
+       list_for_each_entry(dmabuf, &hrq->page_list, list) {
+               rq_create->u.request.page[dmabuf->buffer_tag].addr_lo =
+                                       putPaddrLow(dmabuf->phys);
+               rq_create->u.request.page[dmabuf->buffer_tag].addr_hi =
+                                       putPaddrHigh(dmabuf->phys);
+       }
+       rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+       /* The IOCTL status is embedded in the mailbox subheader. */
+       shdr = (union lpfc_sli4_cfg_shdr *) &rq_create->header.cfg_shdr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+       if (shdr_status || shdr_add_status || rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2504 RQ_CREATE mailbox failed with "
+                               "status x%x add_status x%x, mbx status x%x\n",
+                               shdr_status, shdr_add_status, rc);
+               status = -ENXIO;
+               goto out;
+       }
+       hrq->queue_id = bf_get(lpfc_mbx_rq_create_q_id, &rq_create->u.response);
+       if (hrq->queue_id == 0xFFFF) {
+               status = -ENXIO;
+               goto out;
+       }
+       hrq->type = LPFC_HRQ;
+       hrq->subtype = subtype;
+       hrq->host_index = 0;
+       hrq->hba_index = 0;
+
+       /* now create the data queue */
+       lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+                        LPFC_MBOX_OPCODE_FCOE_RQ_CREATE,
+                        length, LPFC_SLI4_MBX_EMBED);
+       switch (drq->entry_count) {
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "2536 Unsupported RQ count. (%d)\n",
+                               drq->entry_count);
+               if (drq->entry_count < 512)
+                       return -EINVAL;
+               /* otherwise default to smallest count (drop through) */
+       case 512:
+               bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context,
+                      LPFC_RQ_RING_SIZE_512);
+               break;
+       case 1024:
+               bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context,
+                      LPFC_RQ_RING_SIZE_1024);
+               break;
+       case 2048:
+               bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context,
+                      LPFC_RQ_RING_SIZE_2048);
+               break;
+       case 4096:
+               bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context,
+                      LPFC_RQ_RING_SIZE_4096);
+               break;
+       }
+       bf_set(lpfc_rq_context_cq_id, &rq_create->u.request.context,
+              cq->queue_id);
+       bf_set(lpfc_mbx_rq_create_num_pages, &rq_create->u.request,
+              drq->page_count);
+       bf_set(lpfc_rq_context_buf_size, &rq_create->u.request.context,
+              LPFC_DATA_BUF_SIZE);
+       list_for_each_entry(dmabuf, &drq->page_list, list) {
+               rq_create->u.request.page[dmabuf->buffer_tag].addr_lo =
+                                       putPaddrLow(dmabuf->phys);
+               rq_create->u.request.page[dmabuf->buffer_tag].addr_hi =
+                                       putPaddrHigh(dmabuf->phys);
+       }
+       rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+       /* The IOCTL status is embedded in the mailbox subheader. */
+       shdr = (union lpfc_sli4_cfg_shdr *) &rq_create->header.cfg_shdr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+       if (shdr_status || shdr_add_status || rc) {
+               status = -ENXIO;
+               goto out;
+       }
+       drq->queue_id = bf_get(lpfc_mbx_rq_create_q_id, &rq_create->u.response);
+       if (drq->queue_id == 0xFFFF) {
+               status = -ENXIO;
+               goto out;
+       }
+       drq->type = LPFC_DRQ;
+       drq->subtype = subtype;
+       drq->host_index = 0;
+       drq->hba_index = 0;
+
+       /* link the header and data RQs onto the parent cq child list */
+       list_add_tail(&hrq->list, &cq->child_list);
+       list_add_tail(&drq->list, &cq->child_list);
+
+out:
+       if (rc != MBX_TIMEOUT)
+               mempool_free(mbox, phba->mbox_mem_pool);
+       return status;
+}
+
+/**
+ * lpfc_eq_destroy - Destroy an event Queue on the HBA
+ * @eq: The queue structure associated with the queue to destroy.
+ *
+ * This function destroys a queue, as detailed in @eq by sending an mailbox
+ * command, specific to the type of queue, to the HBA.
+ *
+ * The @eq struct is used to get the queue ID of the queue to destroy.
+ *
+ * On success this function will return a zero. If the queue destroy mailbox
+ * command fails this function will return ENXIO.
+ **/
+uint32_t
+lpfc_eq_destroy(struct lpfc_hba *phba, struct lpfc_queue *eq)
+{
+       LPFC_MBOXQ_t *mbox;
+       int rc, length, status = 0;
+       uint32_t shdr_status, shdr_add_status;
+       union lpfc_sli4_cfg_shdr *shdr;
+
+       if (!eq)
+               return -ENODEV;
+       mbox = mempool_alloc(eq->phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mbox)
+               return -ENOMEM;
+       length = (sizeof(struct lpfc_mbx_eq_destroy) -
+                 sizeof(struct lpfc_sli4_cfg_mhdr));
+       lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+                        LPFC_MBOX_OPCODE_EQ_DESTROY,
+                        length, LPFC_SLI4_MBX_EMBED);
+       bf_set(lpfc_mbx_eq_destroy_q_id, &mbox->u.mqe.un.eq_destroy.u.request,
+              eq->queue_id);
+       mbox->vport = eq->phba->pport;
+       mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+
+       rc = lpfc_sli_issue_mbox(eq->phba, mbox, MBX_POLL);
+       /* The IOCTL status is embedded in the mailbox subheader. */
+       shdr = (union lpfc_sli4_cfg_shdr *)
+               &mbox->u.mqe.un.eq_destroy.header.cfg_shdr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+       if (shdr_status || shdr_add_status || rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2505 EQ_DESTROY mailbox failed with "
+                               "status x%x add_status x%x, mbx status x%x\n",
+                               shdr_status, shdr_add_status, rc);
+               status = -ENXIO;
+       }
+
+       /* Remove eq from any list */
+       list_del_init(&eq->list);
+       if (rc != MBX_TIMEOUT)
+               mempool_free(mbox, eq->phba->mbox_mem_pool);
+       return status;
+}
+
+/**
+ * lpfc_cq_destroy - Destroy a Completion Queue on the HBA
+ * @cq: The queue structure associated with the queue to destroy.
+ *
+ * This function destroys a queue, as detailed in @cq by sending an mailbox
+ * command, specific to the type of queue, to the HBA.
+ *
+ * The @cq struct is used to get the queue ID of the queue to destroy.
+ *
+ * On success this function will return a zero. If the queue destroy mailbox
+ * command fails this function will return ENXIO.
+ **/
+uint32_t
+lpfc_cq_destroy(struct lpfc_hba *phba, struct lpfc_queue *cq)
+{
+       LPFC_MBOXQ_t *mbox;
+       int rc, length, status = 0;
+       uint32_t shdr_status, shdr_add_status;
+       union lpfc_sli4_cfg_shdr *shdr;
+
+       if (!cq)
+               return -ENODEV;
+       mbox = mempool_alloc(cq->phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mbox)
+               return -ENOMEM;
+       length = (sizeof(struct lpfc_mbx_cq_destroy) -
+                 sizeof(struct lpfc_sli4_cfg_mhdr));
+       lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+                        LPFC_MBOX_OPCODE_CQ_DESTROY,
+                        length, LPFC_SLI4_MBX_EMBED);
+       bf_set(lpfc_mbx_cq_destroy_q_id, &mbox->u.mqe.un.cq_destroy.u.request,
+              cq->queue_id);
+       mbox->vport = cq->phba->pport;
+       mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+       rc = lpfc_sli_issue_mbox(cq->phba, mbox, MBX_POLL);
+       /* The IOCTL status is embedded in the mailbox subheader. */
+       shdr = (union lpfc_sli4_cfg_shdr *)
+               &mbox->u.mqe.un.wq_create.header.cfg_shdr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+       if (shdr_status || shdr_add_status || rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2506 CQ_DESTROY mailbox failed with "
+                               "status x%x add_status x%x, mbx status x%x\n",
+                               shdr_status, shdr_add_status, rc);
+               status = -ENXIO;
+       }
+       /* Remove cq from any list */
+       list_del_init(&cq->list);
+       if (rc != MBX_TIMEOUT)
+               mempool_free(mbox, cq->phba->mbox_mem_pool);
+       return status;
+}
+
+/**
+ * lpfc_mq_destroy - Destroy a Mailbox Queue on the HBA
+ * @qm: The queue structure associated with the queue to destroy.
+ *
+ * This function destroys a queue, as detailed in @mq by sending an mailbox
+ * command, specific to the type of queue, to the HBA.
+ *
+ * The @mq struct is used to get the queue ID of the queue to destroy.
+ *
+ * On success this function will return a zero. If the queue destroy mailbox
+ * command fails this function will return ENXIO.
+ **/
+uint32_t
+lpfc_mq_destroy(struct lpfc_hba *phba, struct lpfc_queue *mq)
+{
+       LPFC_MBOXQ_t *mbox;
+       int rc, length, status = 0;
+       uint32_t shdr_status, shdr_add_status;
+       union lpfc_sli4_cfg_shdr *shdr;
+
+       if (!mq)
+               return -ENODEV;
+       mbox = mempool_alloc(mq->phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mbox)
+               return -ENOMEM;
+       length = (sizeof(struct lpfc_mbx_mq_destroy) -
+                 sizeof(struct lpfc_sli4_cfg_mhdr));
+       lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+                        LPFC_MBOX_OPCODE_MQ_DESTROY,
+                        length, LPFC_SLI4_MBX_EMBED);
+       bf_set(lpfc_mbx_mq_destroy_q_id, &mbox->u.mqe.un.mq_destroy.u.request,
+              mq->queue_id);
+       mbox->vport = mq->phba->pport;
+       mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+       rc = lpfc_sli_issue_mbox(mq->phba, mbox, MBX_POLL);
+       /* The IOCTL status is embedded in the mailbox subheader. */
+       shdr = (union lpfc_sli4_cfg_shdr *)
+               &mbox->u.mqe.un.mq_destroy.header.cfg_shdr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+       if (shdr_status || shdr_add_status || rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2507 MQ_DESTROY mailbox failed with "
+                               "status x%x add_status x%x, mbx status x%x\n",
+                               shdr_status, shdr_add_status, rc);
+               status = -ENXIO;
+       }
+       /* Remove mq from any list */
+       list_del_init(&mq->list);
+       if (rc != MBX_TIMEOUT)
+               mempool_free(mbox, mq->phba->mbox_mem_pool);
+       return status;
+}
+
+/**
+ * lpfc_wq_destroy - Destroy a Work Queue on the HBA
+ * @wq: The queue structure associated with the queue to destroy.
+ *
+ * This function destroys a queue, as detailed in @wq by sending an mailbox
+ * command, specific to the type of queue, to the HBA.
+ *
+ * The @wq struct is used to get the queue ID of the queue to destroy.
+ *
+ * On success this function will return a zero. If the queue destroy mailbox
+ * command fails this function will return ENXIO.
+ **/
+uint32_t
+lpfc_wq_destroy(struct lpfc_hba *phba, struct lpfc_queue *wq)
+{
+       LPFC_MBOXQ_t *mbox;
+       int rc, length, status = 0;
+       uint32_t shdr_status, shdr_add_status;
+       union lpfc_sli4_cfg_shdr *shdr;
+
+       if (!wq)
+               return -ENODEV;
+       mbox = mempool_alloc(wq->phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mbox)
+               return -ENOMEM;
+       length = (sizeof(struct lpfc_mbx_wq_destroy) -
+                 sizeof(struct lpfc_sli4_cfg_mhdr));
+       lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+                        LPFC_MBOX_OPCODE_FCOE_WQ_DESTROY,
+                        length, LPFC_SLI4_MBX_EMBED);
+       bf_set(lpfc_mbx_wq_destroy_q_id, &mbox->u.mqe.un.wq_destroy.u.request,
+              wq->queue_id);
+       mbox->vport = wq->phba->pport;
+       mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+       rc = lpfc_sli_issue_mbox(wq->phba, mbox, MBX_POLL);
+       shdr = (union lpfc_sli4_cfg_shdr *)
+               &mbox->u.mqe.un.wq_destroy.header.cfg_shdr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+       if (shdr_status || shdr_add_status || rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2508 WQ_DESTROY mailbox failed with "
+                               "status x%x add_status x%x, mbx status x%x\n",
+                               shdr_status, shdr_add_status, rc);
+               status = -ENXIO;
+       }
+       /* Remove wq from any list */
+       list_del_init(&wq->list);
+       if (rc != MBX_TIMEOUT)
+               mempool_free(mbox, wq->phba->mbox_mem_pool);
+       return status;
+}
+
+/**
+ * lpfc_rq_destroy - Destroy a Receive Queue on the HBA
+ * @rq: The queue structure associated with the queue to destroy.
+ *
+ * This function destroys a queue, as detailed in @rq by sending an mailbox
+ * command, specific to the type of queue, to the HBA.
+ *
+ * The @rq struct is used to get the queue ID of the queue to destroy.
+ *
+ * On success this function will return a zero. If the queue destroy mailbox
+ * command fails this function will return ENXIO.
+ **/
+uint32_t
+lpfc_rq_destroy(struct lpfc_hba *phba, struct lpfc_queue *hrq,
+               struct lpfc_queue *drq)
+{
+       LPFC_MBOXQ_t *mbox;
+       int rc, length, status = 0;
+       uint32_t shdr_status, shdr_add_status;
+       union lpfc_sli4_cfg_shdr *shdr;
+
+       if (!hrq || !drq)
+               return -ENODEV;
+       mbox = mempool_alloc(hrq->phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mbox)
+               return -ENOMEM;
+       length = (sizeof(struct lpfc_mbx_rq_destroy) -
+                 sizeof(struct mbox_header));
+       lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+                        LPFC_MBOX_OPCODE_FCOE_RQ_DESTROY,
+                        length, LPFC_SLI4_MBX_EMBED);
+       bf_set(lpfc_mbx_rq_destroy_q_id, &mbox->u.mqe.un.rq_destroy.u.request,
+              hrq->queue_id);
+       mbox->vport = hrq->phba->pport;
+       mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+       rc = lpfc_sli_issue_mbox(hrq->phba, mbox, MBX_POLL);
+       /* The IOCTL status is embedded in the mailbox subheader. */
+       shdr = (union lpfc_sli4_cfg_shdr *)
+               &mbox->u.mqe.un.rq_destroy.header.cfg_shdr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+       if (shdr_status || shdr_add_status || rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2509 RQ_DESTROY mailbox failed with "
+                               "status x%x add_status x%x, mbx status x%x\n",
+                               shdr_status, shdr_add_status, rc);
+               if (rc != MBX_TIMEOUT)
+                       mempool_free(mbox, hrq->phba->mbox_mem_pool);
+               return -ENXIO;
+       }
+       bf_set(lpfc_mbx_rq_destroy_q_id, &mbox->u.mqe.un.rq_destroy.u.request,
+              drq->queue_id);
+       rc = lpfc_sli_issue_mbox(drq->phba, mbox, MBX_POLL);
+       shdr = (union lpfc_sli4_cfg_shdr *)
+               &mbox->u.mqe.un.rq_destroy.header.cfg_shdr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+       if (shdr_status || shdr_add_status || rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2510 RQ_DESTROY mailbox failed with "
+                               "status x%x add_status x%x, mbx status x%x\n",
+                               shdr_status, shdr_add_status, rc);
+               status = -ENXIO;
+       }
+       list_del_init(&hrq->list);
+       list_del_init(&drq->list);
+       if (rc != MBX_TIMEOUT)
+               mempool_free(mbox, hrq->phba->mbox_mem_pool);
+       return status;
+}
+
+/**
+ * lpfc_sli4_post_sgl - Post scatter gather list for an XRI to HBA
+ * @phba: The virtual port for which this call being executed.
+ * @pdma_phys_addr0: Physical address of the 1st SGL page.
+ * @pdma_phys_addr1: Physical address of the 2nd SGL page.
+ * @xritag: the xritag that ties this io to the SGL pages.
+ *
+ * This routine will post the sgl pages for the IO that has the xritag
+ * that is in the iocbq structure. The xritag is assigned during iocbq
+ * creation and persists for as long as the driver is loaded.
+ * if the caller has fewer than 256 scatter gather segments to map then
+ * pdma_phys_addr1 should be 0.
+ * If the caller needs to map more than 256 scatter gather segment then
+ * pdma_phys_addr1 should be a valid physical address.
+ * physical address for SGLs must be 64 byte aligned.
+ * If you are going to map 2 SGL's then the first one must have 256 entries
+ * the second sgl can have between 1 and 256 entries.
+ *
+ * Return codes:
+ *     0 - Success
+ *     -ENXIO, -ENOMEM - Failure
+ **/
+int
+lpfc_sli4_post_sgl(struct lpfc_hba *phba,
+               dma_addr_t pdma_phys_addr0,
+               dma_addr_t pdma_phys_addr1,
+               uint16_t xritag)
+{
+       struct lpfc_mbx_post_sgl_pages *post_sgl_pages;
+       LPFC_MBOXQ_t *mbox;
+       int rc;
+       uint32_t shdr_status, shdr_add_status;
+       union lpfc_sli4_cfg_shdr *shdr;
+
+       if (xritag == NO_XRI) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0364 Invalid param:\n");
+               return -EINVAL;
+       }
+
+       mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mbox)
+               return -ENOMEM;
+
+       lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+                       LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES,
+                       sizeof(struct lpfc_mbx_post_sgl_pages) -
+                       sizeof(struct mbox_header), LPFC_SLI4_MBX_EMBED);
+
+       post_sgl_pages = (struct lpfc_mbx_post_sgl_pages *)
+                               &mbox->u.mqe.un.post_sgl_pages;
+       bf_set(lpfc_post_sgl_pages_xri, post_sgl_pages, xritag);
+       bf_set(lpfc_post_sgl_pages_xricnt, post_sgl_pages, 1);
+
+       post_sgl_pages->sgl_pg_pairs[0].sgl_pg0_addr_lo =
+                               cpu_to_le32(putPaddrLow(pdma_phys_addr0));
+       post_sgl_pages->sgl_pg_pairs[0].sgl_pg0_addr_hi =
+                               cpu_to_le32(putPaddrHigh(pdma_phys_addr0));
+
+       post_sgl_pages->sgl_pg_pairs[0].sgl_pg1_addr_lo =
+                               cpu_to_le32(putPaddrLow(pdma_phys_addr1));
+       post_sgl_pages->sgl_pg_pairs[0].sgl_pg1_addr_hi =
+                               cpu_to_le32(putPaddrHigh(pdma_phys_addr1));
+       if (!phba->sli4_hba.intr_enable)
+               rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+       else
+               rc = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO);
+       /* The IOCTL status is embedded in the mailbox subheader. */
+       shdr = (union lpfc_sli4_cfg_shdr *) &post_sgl_pages->header.cfg_shdr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+       if (rc != MBX_TIMEOUT)
+               mempool_free(mbox, phba->mbox_mem_pool);
+       if (shdr_status || shdr_add_status || rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2511 POST_SGL mailbox failed with "
+                               "status x%x add_status x%x, mbx status x%x\n",
+                               shdr_status, shdr_add_status, rc);
+               rc = -ENXIO;
+       }
+       return 0;
+}
+/**
+ * lpfc_sli4_remove_all_sgl_pages - Post scatter gather list for an XRI to HBA
+ * @phba: The virtual port for which this call being executed.
+ *
+ * This routine will remove all of the sgl pages registered with the hba.
+ *
+ * Return codes:
+ *     0 - Success
+ *     -ENXIO, -ENOMEM - Failure
+ **/
+int
+lpfc_sli4_remove_all_sgl_pages(struct lpfc_hba *phba)
+{
+       LPFC_MBOXQ_t *mbox;
+       int rc;
+       uint32_t shdr_status, shdr_add_status;
+       union lpfc_sli4_cfg_shdr *shdr;
+
+       mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mbox)
+               return -ENOMEM;
+
+       lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+                       LPFC_MBOX_OPCODE_FCOE_REMOVE_SGL_PAGES, 0,
+                       LPFC_SLI4_MBX_EMBED);
+       if (!phba->sli4_hba.intr_enable)
+               rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+       else
+               rc = lpfc_sli_issue_mbox_wait(phba, mbox, LPFC_MBOX_TMO);
+       /* The IOCTL status is embedded in the mailbox subheader. */
+       shdr = (union lpfc_sli4_cfg_shdr *)
+               &mbox->u.mqe.un.sli4_config.header.cfg_shdr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+       if (rc != MBX_TIMEOUT)
+               mempool_free(mbox, phba->mbox_mem_pool);
+       if (shdr_status || shdr_add_status || rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2512 REMOVE_ALL_SGL_PAGES mailbox failed with "
+                               "status x%x add_status x%x, mbx status x%x\n",
+                               shdr_status, shdr_add_status, rc);
+               rc = -ENXIO;
+       }
+       return rc;
+}
+
+/**
+ * lpfc_sli4_next_xritag - Get an xritag for the io
+ * @phba: Pointer to HBA context object.
+ *
+ * This function gets an xritag for the iocb. If there is no unused xritag
+ * it will return 0xffff.
+ * The function returns the allocated xritag if successful, else returns zero.
+ * Zero is not a valid xritag.
+ * The caller is not required to hold any lock.
+ **/
+uint16_t
+lpfc_sli4_next_xritag(struct lpfc_hba *phba)
+{
+       uint16_t xritag;
+
+       spin_lock_irq(&phba->hbalock);
+       xritag = phba->sli4_hba.next_xri;
+       if ((xritag != (uint16_t) -1) && xritag <
+               (phba->sli4_hba.max_cfg_param.max_xri
+                       + phba->sli4_hba.max_cfg_param.xri_base)) {
+               phba->sli4_hba.next_xri++;
+               phba->sli4_hba.max_cfg_param.xri_used++;
+               spin_unlock_irq(&phba->hbalock);
+               return xritag;
+       }
+       spin_unlock_irq(&phba->hbalock);
+
+       lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                       "2004 Failed to allocate XRI.last XRITAG is %d"
+                       " Max XRI is %d, Used XRI is %d\n",
+                       phba->sli4_hba.next_xri,
+                       phba->sli4_hba.max_cfg_param.max_xri,
+                       phba->sli4_hba.max_cfg_param.xri_used);
+       return -1;
+}
+
+/**
+ * lpfc_sli4_post_sgl_list - post a block of sgl list to the firmware.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to post a block of driver's sgl pages to the
+ * HBA using non-embedded mailbox command. No Lock is held. This routine
+ * is only called when the driver is loading and after all IO has been
+ * stopped.
+ **/
+int
+lpfc_sli4_post_sgl_list(struct lpfc_hba *phba)
+{
+       struct lpfc_sglq *sglq_entry;
+       struct lpfc_mbx_post_uembed_sgl_page1 *sgl;
+       struct sgl_page_pairs *sgl_pg_pairs;
+       void *viraddr;
+       LPFC_MBOXQ_t *mbox;
+       uint32_t reqlen, alloclen, pg_pairs;
+       uint32_t mbox_tmo;
+       uint16_t xritag_start = 0;
+       int els_xri_cnt, rc = 0;
+       uint32_t shdr_status, shdr_add_status;
+       union lpfc_sli4_cfg_shdr *shdr;
+
+       /* The number of sgls to be posted */
+       els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba);
+
+       reqlen = els_xri_cnt * sizeof(struct sgl_page_pairs) +
+                sizeof(union lpfc_sli4_cfg_shdr) + sizeof(uint32_t);
+       if (reqlen > PAGE_SIZE) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+                               "2559 Block sgl registration required DMA "
+                               "size (%d) great than a page\n", reqlen);
+               return -ENOMEM;
+       }
+       mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mbox) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2560 Failed to allocate mbox cmd memory\n");
+               return -ENOMEM;
+       }
+
+       /* Allocate DMA memory and set up the non-embedded mailbox command */
+       alloclen = lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+                        LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES, reqlen,
+                        LPFC_SLI4_MBX_NEMBED);
+
+       if (alloclen < reqlen) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0285 Allocated DMA memory size (%d) is "
+                               "less than the requested DMA memory "
+                               "size (%d)\n", alloclen, reqlen);
+               lpfc_sli4_mbox_cmd_free(phba, mbox);
+               return -ENOMEM;
+       }
+
+       /* Get the first SGE entry from the non-embedded DMA memory */
+       if (unlikely(!mbox->sge_array)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+                               "2525 Failed to get the non-embedded SGE "
+                               "virtual address\n");
+               lpfc_sli4_mbox_cmd_free(phba, mbox);
+               return -ENOMEM;
+       }
+       viraddr = mbox->sge_array->addr[0];
+
+       /* Set up the SGL pages in the non-embedded DMA pages */
+       sgl = (struct lpfc_mbx_post_uembed_sgl_page1 *)viraddr;
+       sgl_pg_pairs = &sgl->sgl_pg_pairs;
+
+       for (pg_pairs = 0; pg_pairs < els_xri_cnt; pg_pairs++) {
+               sglq_entry = phba->sli4_hba.lpfc_els_sgl_array[pg_pairs];
+               /* Set up the sge entry */
+               sgl_pg_pairs->sgl_pg0_addr_lo =
+                               cpu_to_le32(putPaddrLow(sglq_entry->phys));
+               sgl_pg_pairs->sgl_pg0_addr_hi =
+                               cpu_to_le32(putPaddrHigh(sglq_entry->phys));
+               sgl_pg_pairs->sgl_pg1_addr_lo =
+                               cpu_to_le32(putPaddrLow(0));
+               sgl_pg_pairs->sgl_pg1_addr_hi =
+                               cpu_to_le32(putPaddrHigh(0));
+               /* Keep the first xritag on the list */
+               if (pg_pairs == 0)
+                       xritag_start = sglq_entry->sli4_xritag;
+               sgl_pg_pairs++;
+       }
+       bf_set(lpfc_post_sgl_pages_xri, sgl, xritag_start);
+       pg_pairs = (pg_pairs > 0) ? (pg_pairs - 1) : pg_pairs;
+       bf_set(lpfc_post_sgl_pages_xricnt, sgl, pg_pairs);
+       /* Perform endian conversion if necessary */
+       sgl->word0 = cpu_to_le32(sgl->word0);
+
+       if (!phba->sli4_hba.intr_enable)
+               rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+       else {
+               mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
+               rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo);
+       }
+       shdr = (union lpfc_sli4_cfg_shdr *) &sgl->cfg_shdr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+       if (rc != MBX_TIMEOUT)
+               lpfc_sli4_mbox_cmd_free(phba, mbox);
+       if (shdr_status || shdr_add_status || rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "2513 POST_SGL_BLOCK mailbox command failed "
+                               "status x%x add_status x%x mbx status x%x\n",
+                               shdr_status, shdr_add_status, rc);
+               rc = -ENXIO;
+       }
+       return rc;
+}
+
+/**
+ * lpfc_sli4_post_scsi_sgl_block - post a block of scsi sgl list to firmware
+ * @phba: pointer to lpfc hba data structure.
+ * @sblist: pointer to scsi buffer list.
+ * @count: number of scsi buffers on the list.
+ *
+ * This routine is invoked to post a block of @count scsi sgl pages from a
+ * SCSI buffer list @sblist to the HBA using non-embedded mailbox command.
+ * No Lock is held.
+ *
+ **/
+int
+lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, struct list_head *sblist,
+                             int cnt)
+{
+       struct lpfc_scsi_buf *psb;
+       struct lpfc_mbx_post_uembed_sgl_page1 *sgl;
+       struct sgl_page_pairs *sgl_pg_pairs;
+       void *viraddr;
+       LPFC_MBOXQ_t *mbox;
+       uint32_t reqlen, alloclen, pg_pairs;
+       uint32_t mbox_tmo;
+       uint16_t xritag_start = 0;
+       int rc = 0;
+       uint32_t shdr_status, shdr_add_status;
+       dma_addr_t pdma_phys_bpl1;
+       union lpfc_sli4_cfg_shdr *shdr;
+
+       /* Calculate the requested length of the dma memory */
+       reqlen = cnt * sizeof(struct sgl_page_pairs) +
+                sizeof(union lpfc_sli4_cfg_shdr) + sizeof(uint32_t);
+       if (reqlen > PAGE_SIZE) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+                               "0217 Block sgl registration required DMA "
+                               "size (%d) great than a page\n", reqlen);
+               return -ENOMEM;
+       }
+       mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mbox) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0283 Failed to allocate mbox cmd memory\n");
+               return -ENOMEM;
+       }
+
+       /* Allocate DMA memory and set up the non-embedded mailbox command */
+       alloclen = lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+                               LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES, reqlen,
+                               LPFC_SLI4_MBX_NEMBED);
+
+       if (alloclen < reqlen) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2561 Allocated DMA memory size (%d) is "
+                               "less than the requested DMA memory "
+                               "size (%d)\n", alloclen, reqlen);
+               lpfc_sli4_mbox_cmd_free(phba, mbox);
+               return -ENOMEM;
+       }
+
+       /* Get the first SGE entry from the non-embedded DMA memory */
+       if (unlikely(!mbox->sge_array)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+                               "2565 Failed to get the non-embedded SGE "
+                               "virtual address\n");
+               lpfc_sli4_mbox_cmd_free(phba, mbox);
+               return -ENOMEM;
+       }
+       viraddr = mbox->sge_array->addr[0];
+
+       /* Set up the SGL pages in the non-embedded DMA pages */
+       sgl = (struct lpfc_mbx_post_uembed_sgl_page1 *)viraddr;
+       sgl_pg_pairs = &sgl->sgl_pg_pairs;
+
+       pg_pairs = 0;
+       list_for_each_entry(psb, sblist, list) {
+               /* Set up the sge entry */
+               sgl_pg_pairs->sgl_pg0_addr_lo =
+                       cpu_to_le32(putPaddrLow(psb->dma_phys_bpl));
+               sgl_pg_pairs->sgl_pg0_addr_hi =
+                       cpu_to_le32(putPaddrHigh(psb->dma_phys_bpl));
+               if (phba->cfg_sg_dma_buf_size > SGL_PAGE_SIZE)
+                       pdma_phys_bpl1 = psb->dma_phys_bpl + SGL_PAGE_SIZE;
+               else
+                       pdma_phys_bpl1 = 0;
+               sgl_pg_pairs->sgl_pg1_addr_lo =
+                       cpu_to_le32(putPaddrLow(pdma_phys_bpl1));
+               sgl_pg_pairs->sgl_pg1_addr_hi =
+                       cpu_to_le32(putPaddrHigh(pdma_phys_bpl1));
+               /* Keep the first xritag on the list */
+               if (pg_pairs == 0)
+                       xritag_start = psb->cur_iocbq.sli4_xritag;
+               sgl_pg_pairs++;
+               pg_pairs++;
+       }
+       bf_set(lpfc_post_sgl_pages_xri, sgl, xritag_start);
+       bf_set(lpfc_post_sgl_pages_xricnt, sgl, pg_pairs);
+       /* Perform endian conversion if necessary */
+       sgl->word0 = cpu_to_le32(sgl->word0);
+
+       if (!phba->sli4_hba.intr_enable)
+               rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL);
+       else {
+               mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
+               rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo);
+       }
+       shdr = (union lpfc_sli4_cfg_shdr *) &sgl->cfg_shdr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+       if (rc != MBX_TIMEOUT)
+               lpfc_sli4_mbox_cmd_free(phba, mbox);
+       if (shdr_status || shdr_add_status || rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "2564 POST_SGL_BLOCK mailbox command failed "
+                               "status x%x add_status x%x mbx status x%x\n",
+                               shdr_status, shdr_add_status, rc);
+               rc = -ENXIO;
+       }
+       return rc;
+}
+
+/**
+ * lpfc_fc_frame_check - Check that this frame is a valid frame to handle
+ * @phba: pointer to lpfc_hba struct that the frame was received on
+ * @fc_hdr: A pointer to the FC Header data (In Big Endian Format)
+ *
+ * This function checks the fields in the @fc_hdr to see if the FC frame is a
+ * valid type of frame that the LPFC driver will handle. This function will
+ * return a zero if the frame is a valid frame or a non zero value when the
+ * frame does not pass the check.
+ **/
+static int
+lpfc_fc_frame_check(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr)
+{
+       char *rctl_names[] = FC_RCTL_NAMES_INIT;
+       char *type_names[] = FC_TYPE_NAMES_INIT;
+       struct fc_vft_header *fc_vft_hdr;
+
+       switch (fc_hdr->fh_r_ctl) {
+       case FC_RCTL_DD_UNCAT:          /* uncategorized information */
+       case FC_RCTL_DD_SOL_DATA:       /* solicited data */
+       case FC_RCTL_DD_UNSOL_CTL:      /* unsolicited control */
+       case FC_RCTL_DD_SOL_CTL:        /* solicited control or reply */
+       case FC_RCTL_DD_UNSOL_DATA:     /* unsolicited data */
+       case FC_RCTL_DD_DATA_DESC:      /* data descriptor */
+       case FC_RCTL_DD_UNSOL_CMD:      /* unsolicited command */
+       case FC_RCTL_DD_CMD_STATUS:     /* command status */
+       case FC_RCTL_ELS_REQ:   /* extended link services request */
+       case FC_RCTL_ELS_REP:   /* extended link services reply */
+       case FC_RCTL_ELS4_REQ:  /* FC-4 ELS request */
+       case FC_RCTL_ELS4_REP:  /* FC-4 ELS reply */
+       case FC_RCTL_BA_NOP:    /* basic link service NOP */
+       case FC_RCTL_BA_ABTS:   /* basic link service abort */
+       case FC_RCTL_BA_RMC:    /* remove connection */
+       case FC_RCTL_BA_ACC:    /* basic accept */
+       case FC_RCTL_BA_RJT:    /* basic reject */
+       case FC_RCTL_BA_PRMT:
+       case FC_RCTL_ACK_1:     /* acknowledge_1 */
+       case FC_RCTL_ACK_0:     /* acknowledge_0 */
+       case FC_RCTL_P_RJT:     /* port reject */
+       case FC_RCTL_F_RJT:     /* fabric reject */
+       case FC_RCTL_P_BSY:     /* port busy */
+       case FC_RCTL_F_BSY:     /* fabric busy to data frame */
+       case FC_RCTL_F_BSYL:    /* fabric busy to link control frame */
+       case FC_RCTL_LCR:       /* link credit reset */
+       case FC_RCTL_END:       /* end */
+               break;
+       case FC_RCTL_VFTH:      /* Virtual Fabric tagging Header */
+               fc_vft_hdr = (struct fc_vft_header *)fc_hdr;
+               fc_hdr = &((struct fc_frame_header *)fc_vft_hdr)[1];
+               return lpfc_fc_frame_check(phba, fc_hdr);
+       default:
+               goto drop;
+       }
+       switch (fc_hdr->fh_type) {
+       case FC_TYPE_BLS:
+       case FC_TYPE_ELS:
+       case FC_TYPE_FCP:
+       case FC_TYPE_CT:
+               break;
+       case FC_TYPE_IP:
+       case FC_TYPE_ILS:
+       default:
+               goto drop;
+       }
+       lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                       "2538 Received frame rctl:%s type:%s\n",
+                       rctl_names[fc_hdr->fh_r_ctl],
+                       type_names[fc_hdr->fh_type]);
+       return 0;
+drop:
+       lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
+                       "2539 Dropped frame rctl:%s type:%s\n",
+                       rctl_names[fc_hdr->fh_r_ctl],
+                       type_names[fc_hdr->fh_type]);
+       return 1;
+}
+
+/**
+ * lpfc_fc_hdr_get_vfi - Get the VFI from an FC frame
+ * @fc_hdr: A pointer to the FC Header data (In Big Endian Format)
+ *
+ * This function processes the FC header to retrieve the VFI from the VF
+ * header, if one exists. This function will return the VFI if one exists
+ * or 0 if no VSAN Header exists.
+ **/
+static uint32_t
+lpfc_fc_hdr_get_vfi(struct fc_frame_header *fc_hdr)
+{
+       struct fc_vft_header *fc_vft_hdr = (struct fc_vft_header *)fc_hdr;
+
+       if (fc_hdr->fh_r_ctl != FC_RCTL_VFTH)
+               return 0;
+       return bf_get(fc_vft_hdr_vf_id, fc_vft_hdr);
+}
+
+/**
+ * lpfc_fc_frame_to_vport - Finds the vport that a frame is destined to
+ * @phba: Pointer to the HBA structure to search for the vport on
+ * @fc_hdr: A pointer to the FC Header data (In Big Endian Format)
+ * @fcfi: The FC Fabric ID that the frame came from
+ *
+ * This function searches the @phba for a vport that matches the content of the
+ * @fc_hdr passed in and the @fcfi. This function uses the @fc_hdr to fetch the
+ * VFI, if the Virtual Fabric Tagging Header exists, and the DID. This function
+ * returns the matching vport pointer or NULL if unable to match frame to a
+ * vport.
+ **/
+static struct lpfc_vport *
+lpfc_fc_frame_to_vport(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr,
+                      uint16_t fcfi)
+{
+       struct lpfc_vport **vports;
+       struct lpfc_vport *vport = NULL;
+       int i;
+       uint32_t did = (fc_hdr->fh_d_id[0] << 16 |
+                       fc_hdr->fh_d_id[1] << 8 |
+                       fc_hdr->fh_d_id[2]);
+
+       vports = lpfc_create_vport_work_array(phba);
+       if (vports != NULL)
+               for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) {
+                       if (phba->fcf.fcfi == fcfi &&
+                           vports[i]->vfi == lpfc_fc_hdr_get_vfi(fc_hdr) &&
+                           vports[i]->fc_myDID == did) {
+                               vport = vports[i];
+                               break;
+                       }
+               }
+       lpfc_destroy_vport_work_array(phba, vports);
+       return vport;
+}
+
+/**
+ * lpfc_fc_frame_add - Adds a frame to the vport's list of received sequences
+ * @dmabuf: pointer to a dmabuf that describes the hdr and data of the FC frame
+ *
+ * This function searches through the existing incomplete sequences that have
+ * been sent to this @vport. If the frame matches one of the incomplete
+ * sequences then the dbuf in the @dmabuf is added to the list of frames that
+ * make up that sequence. If no sequence is found that matches this frame then
+ * the function will add the hbuf in the @dmabuf to the @vport's rcv_buffer_list
+ * This function returns a pointer to the first dmabuf in the sequence list that
+ * the frame was linked to.
+ **/
+static struct hbq_dmabuf *
+lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf)
+{
+       struct fc_frame_header *new_hdr;
+       struct fc_frame_header *temp_hdr;
+       struct lpfc_dmabuf *d_buf;
+       struct lpfc_dmabuf *h_buf;
+       struct hbq_dmabuf *seq_dmabuf = NULL;
+       struct hbq_dmabuf *temp_dmabuf = NULL;
+
+       new_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
+       /* Use the hdr_buf to find the sequence that this frame belongs to */
+       list_for_each_entry(h_buf, &vport->rcv_buffer_list, list) {
+               temp_hdr = (struct fc_frame_header *)h_buf->virt;
+               if ((temp_hdr->fh_seq_id != new_hdr->fh_seq_id) ||
+                   (temp_hdr->fh_ox_id != new_hdr->fh_ox_id) ||
+                   (memcmp(&temp_hdr->fh_s_id, &new_hdr->fh_s_id, 3)))
+                       continue;
+               /* found a pending sequence that matches this frame */
+               seq_dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf);
+               break;
+       }
+       if (!seq_dmabuf) {
+               /*
+                * This indicates first frame received for this sequence.
+                * Queue the buffer on the vport's rcv_buffer_list.
+                */
+               list_add_tail(&dmabuf->hbuf.list, &vport->rcv_buffer_list);
+               return dmabuf;
+       }
+       temp_hdr = seq_dmabuf->hbuf.virt;
+       if (new_hdr->fh_seq_cnt < temp_hdr->fh_seq_cnt) {
+               list_add(&seq_dmabuf->dbuf.list, &dmabuf->dbuf.list);
+               return dmabuf;
+       }
+       /* find the correct place in the sequence to insert this frame */
+       list_for_each_entry_reverse(d_buf, &seq_dmabuf->dbuf.list, list) {
+               temp_dmabuf = container_of(d_buf, struct hbq_dmabuf, dbuf);
+               temp_hdr = (struct fc_frame_header *)temp_dmabuf->hbuf.virt;
+               /*
+                * If the frame's sequence count is greater than the frame on
+                * the list then insert the frame right after this frame
+                */
+               if (new_hdr->fh_seq_cnt > temp_hdr->fh_seq_cnt) {
+                       list_add(&dmabuf->dbuf.list, &temp_dmabuf->dbuf.list);
+                       return seq_dmabuf;
+               }
+       }
+       return NULL;
+}
+
+/**
+ * lpfc_seq_complete - Indicates if a sequence is complete
+ * @dmabuf: pointer to a dmabuf that describes the FC sequence
+ *
+ * This function checks the sequence, starting with the frame described by
+ * @dmabuf, to see if all the frames associated with this sequence are present.
+ * the frames associated with this sequence are linked to the @dmabuf using the
+ * dbuf list. This function looks for two major things. 1) That the first frame
+ * has a sequence count of zero. 2) There is a frame with last frame of sequence
+ * set. 3) That there are no holes in the sequence count. The function will
+ * return 1 when the sequence is complete, otherwise it will return 0.
+ **/
+static int
+lpfc_seq_complete(struct hbq_dmabuf *dmabuf)
+{
+       struct fc_frame_header *hdr;
+       struct lpfc_dmabuf *d_buf;
+       struct hbq_dmabuf *seq_dmabuf;
+       uint32_t fctl;
+       int seq_count = 0;
+
+       hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
+       /* make sure first fame of sequence has a sequence count of zero */
+       if (hdr->fh_seq_cnt != seq_count)
+               return 0;
+       fctl = (hdr->fh_f_ctl[0] << 16 |
+               hdr->fh_f_ctl[1] << 8 |
+               hdr->fh_f_ctl[2]);
+       /* If last frame of sequence we can return success. */
+       if (fctl & FC_FC_END_SEQ)
+               return 1;
+       list_for_each_entry(d_buf, &dmabuf->dbuf.list, list) {
+               seq_dmabuf = container_of(d_buf, struct hbq_dmabuf, dbuf);
+               hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
+               /* If there is a hole in the sequence count then fail. */
+               if (++seq_count != hdr->fh_seq_cnt)
+                       return 0;
+               fctl = (hdr->fh_f_ctl[0] << 16 |
+                       hdr->fh_f_ctl[1] << 8 |
+                       hdr->fh_f_ctl[2]);
+               /* If last frame of sequence we can return success. */
+               if (fctl & FC_FC_END_SEQ)
+                       return 1;
+       }
+       return 0;
+}
+
+/**
+ * lpfc_prep_seq - Prep sequence for ULP processing
+ * @vport: Pointer to the vport on which this sequence was received
+ * @dmabuf: pointer to a dmabuf that describes the FC sequence
+ *
+ * This function takes a sequence, described by a list of frames, and creates
+ * a list of iocbq structures to describe the sequence. This iocbq list will be
+ * used to issue to the generic unsolicited sequence handler. This routine
+ * returns a pointer to the first iocbq in the list. If the function is unable
+ * to allocate an iocbq then it throw out the received frames that were not
+ * able to be described and return a pointer to the first iocbq. If unable to
+ * allocate any iocbqs (including the first) this function will return NULL.
+ **/
+static struct lpfc_iocbq *
+lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
+{
+       struct lpfc_dmabuf *d_buf, *n_buf;
+       struct lpfc_iocbq *first_iocbq, *iocbq;
+       struct fc_frame_header *fc_hdr;
+       uint32_t sid;
+
+       fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
+       /* remove from receive buffer list */
+       list_del_init(&seq_dmabuf->hbuf.list);
+       /* get the Remote Port's SID */
+       sid = (fc_hdr->fh_s_id[0] << 16 |
+              fc_hdr->fh_s_id[1] << 8 |
+              fc_hdr->fh_s_id[2]);
+       /* Get an iocbq struct to fill in. */
+       first_iocbq = lpfc_sli_get_iocbq(vport->phba);
+       if (first_iocbq) {
+               /* Initialize the first IOCB. */
+               first_iocbq->iocb.ulpStatus = IOSTAT_SUCCESS;
+               first_iocbq->iocb.ulpCommand = CMD_IOCB_RCV_SEQ64_CX;
+               first_iocbq->iocb.ulpContext = be16_to_cpu(fc_hdr->fh_ox_id);
+               first_iocbq->iocb.unsli3.rcvsli3.vpi =
+                                       vport->vpi + vport->phba->vpi_base;
+               /* put the first buffer into the first IOCBq */
+               first_iocbq->context2 = &seq_dmabuf->dbuf;
+               first_iocbq->context3 = NULL;
+               first_iocbq->iocb.ulpBdeCount = 1;
+               first_iocbq->iocb.un.cont64[0].tus.f.bdeSize =
+                                                       LPFC_DATA_BUF_SIZE;
+               first_iocbq->iocb.un.rcvels.remoteID = sid;
+       }
+       iocbq = first_iocbq;
+       /*
+        * Each IOCBq can have two Buffers assigned, so go through the list
+        * of buffers for this sequence and save two buffers in each IOCBq
+        */
+       list_for_each_entry_safe(d_buf, n_buf, &seq_dmabuf->dbuf.list, list) {
+               if (!iocbq) {
+                       lpfc_in_buf_free(vport->phba, d_buf);
+                       continue;
+               }
+               if (!iocbq->context3) {
+                       iocbq->context3 = d_buf;
+                       iocbq->iocb.ulpBdeCount++;
+                       iocbq->iocb.unsli3.rcvsli3.bde2.tus.f.bdeSize =
+                                                       LPFC_DATA_BUF_SIZE;
+               } else {
+                       iocbq = lpfc_sli_get_iocbq(vport->phba);
+                       if (!iocbq) {
+                               if (first_iocbq) {
+                                       first_iocbq->iocb.ulpStatus =
+                                                       IOSTAT_FCP_RSP_ERROR;
+                                       first_iocbq->iocb.un.ulpWord[4] =
+                                                       IOERR_NO_RESOURCES;
+                               }
+                               lpfc_in_buf_free(vport->phba, d_buf);
+                               continue;
+                       }
+                       iocbq->context2 = d_buf;
+                       iocbq->context3 = NULL;
+                       iocbq->iocb.ulpBdeCount = 1;
+                       iocbq->iocb.un.cont64[0].tus.f.bdeSize =
+                                                       LPFC_DATA_BUF_SIZE;
+                       iocbq->iocb.un.rcvels.remoteID = sid;
+                       list_add_tail(&iocbq->list, &first_iocbq->list);
+               }
+       }
+       return first_iocbq;
+}
+
+/**
+ * lpfc_sli4_handle_received_buffer - Handle received buffers from firmware
+ * @phba: Pointer to HBA context object.
+ *
+ * This function is called with no lock held. This function processes all
+ * the received buffers and gives it to upper layers when a received buffer
+ * indicates that it is the final frame in the sequence. The interrupt
+ * service routine processes received buffers at interrupt contexts and adds
+ * received dma buffers to the rb_pend_list queue and signals the worker thread.
+ * Worker thread calls lpfc_sli4_handle_received_buffer, which will call the
+ * appropriate receive function when the final frame in a sequence is received.
+ **/
+int
+lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba)
+{
+       LIST_HEAD(cmplq);
+       struct hbq_dmabuf *dmabuf, *seq_dmabuf;
+       struct fc_frame_header *fc_hdr;
+       struct lpfc_vport *vport;
+       uint32_t fcfi;
+       struct lpfc_iocbq *iocbq;
+
+       /* Clear hba flag and get all received buffers into the cmplq */
+       spin_lock_irq(&phba->hbalock);
+       phba->hba_flag &= ~HBA_RECEIVE_BUFFER;
+       list_splice_init(&phba->rb_pend_list, &cmplq);
+       spin_unlock_irq(&phba->hbalock);
+
+       /* Process each received buffer */
+       while ((dmabuf = lpfc_sli_hbqbuf_get(&cmplq)) != NULL) {
+               fc_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
+               /* check to see if this a valid type of frame */
+               if (lpfc_fc_frame_check(phba, fc_hdr)) {
+                       lpfc_in_buf_free(phba, &dmabuf->dbuf);
+                       continue;
+               }
+               fcfi = bf_get(lpfc_rcqe_fcf_id, &dmabuf->rcqe);
+               vport = lpfc_fc_frame_to_vport(phba, fc_hdr, fcfi);
+               if (!vport) {
+                       /* throw out the frame */
+                       lpfc_in_buf_free(phba, &dmabuf->dbuf);
+                       continue;
+               }
+               /* Link this frame */
+               seq_dmabuf = lpfc_fc_frame_add(vport, dmabuf);
+               if (!seq_dmabuf) {
+                       /* unable to add frame to vport - throw it out */
+                       lpfc_in_buf_free(phba, &dmabuf->dbuf);
+                       continue;
+               }
+               /* If not last frame in sequence continue processing frames. */
+               if (!lpfc_seq_complete(seq_dmabuf)) {
+                       /*
+                        * When saving off frames post a new one and mark this
+                        * frame to be freed when it is finished.
+                        **/
+                       lpfc_sli_hbqbuf_fill_hbqs(phba, LPFC_ELS_HBQ, 1);
+                       dmabuf->tag = -1;
+                       continue;
+               }
+               fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
+               iocbq = lpfc_prep_seq(vport, seq_dmabuf);
+               if (!lpfc_complete_unsol_iocb(phba,
+                                             &phba->sli.ring[LPFC_ELS_RING],
+                                             iocbq, fc_hdr->fh_r_ctl,
+                                             fc_hdr->fh_type))
+                       lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+                                       "2540 Ring %d handler: unexpected Rctl "
+                                       "x%x Type x%x received\n",
+                                       LPFC_ELS_RING,
+                                       fc_hdr->fh_r_ctl, fc_hdr->fh_type);
+       };
+       return 0;
+}
+
+/**
+ * lpfc_sli4_post_all_rpi_hdrs - Post the rpi header memory region to the port
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to post rpi header templates to the
+ * HBA consistent with the SLI-4 interface spec.  This routine
+ * posts a PAGE_SIZE memory region to the port to hold up to
+ * PAGE_SIZE modulo 64 rpi context headers.
+ *
+ * This routine does not require any locks.  It's usage is expected
+ * to be driver load or reset recovery when the driver is
+ * sequential.
+ *
+ * Return codes
+ *     0 - sucessful
+ *      EIO - The mailbox failed to complete successfully.
+ *     When this error occurs, the driver is not guaranteed
+ *     to have any rpi regions posted to the device and
+ *     must either attempt to repost the regions or take a
+ *     fatal error.
+ **/
+int
+lpfc_sli4_post_all_rpi_hdrs(struct lpfc_hba *phba)
+{
+       struct lpfc_rpi_hdr *rpi_page;
+       uint32_t rc = 0;
+
+       /* Post all rpi memory regions to the port. */
+       list_for_each_entry(rpi_page, &phba->sli4_hba.lpfc_rpi_hdr_list, list) {
+               rc = lpfc_sli4_post_rpi_hdr(phba, rpi_page);
+               if (rc != MBX_SUCCESS) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                                       "2008 Error %d posting all rpi "
+                                       "headers\n", rc);
+                       rc = -EIO;
+                       break;
+               }
+       }
+
+       return rc;
+}
+
+/**
+ * lpfc_sli4_post_rpi_hdr - Post an rpi header memory region to the port
+ * @phba: pointer to lpfc hba data structure.
+ * @rpi_page:  pointer to the rpi memory region.
+ *
+ * This routine is invoked to post a single rpi header to the
+ * HBA consistent with the SLI-4 interface spec.  This memory region
+ * maps up to 64 rpi context regions.
+ *
+ * Return codes
+ *     0 - sucessful
+ *     ENOMEM - No available memory
+ *      EIO - The mailbox failed to complete successfully.
+ **/
+int
+lpfc_sli4_post_rpi_hdr(struct lpfc_hba *phba, struct lpfc_rpi_hdr *rpi_page)
+{
+       LPFC_MBOXQ_t *mboxq;
+       struct lpfc_mbx_post_hdr_tmpl *hdr_tmpl;
+       uint32_t rc = 0;
+       uint32_t mbox_tmo;
+       uint32_t shdr_status, shdr_add_status;
+       union lpfc_sli4_cfg_shdr *shdr;
+
+       /* The port is notified of the header region via a mailbox command. */
+       mboxq = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mboxq) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "2001 Unable to allocate memory for issuing "
+                               "SLI_CONFIG_SPECIAL mailbox command\n");
+               return -ENOMEM;
+       }
+
+       /* Post all rpi memory regions to the port. */
+       hdr_tmpl = &mboxq->u.mqe.un.hdr_tmpl;
+       mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG);
+       lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_FCOE,
+                        LPFC_MBOX_OPCODE_FCOE_POST_HDR_TEMPLATE,
+                        sizeof(struct lpfc_mbx_post_hdr_tmpl) -
+                        sizeof(struct mbox_header), LPFC_SLI4_MBX_EMBED);
+       bf_set(lpfc_mbx_post_hdr_tmpl_page_cnt,
+              hdr_tmpl, rpi_page->page_count);
+       bf_set(lpfc_mbx_post_hdr_tmpl_rpi_offset, hdr_tmpl,
+              rpi_page->start_rpi);
+       hdr_tmpl->rpi_paddr_lo = putPaddrLow(rpi_page->dmabuf->phys);
+       hdr_tmpl->rpi_paddr_hi = putPaddrHigh(rpi_page->dmabuf->phys);
+       if (!phba->sli4_hba.intr_enable)
+               rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+       else
+               rc = lpfc_sli_issue_mbox_wait(phba, mboxq, mbox_tmo);
+       shdr = (union lpfc_sli4_cfg_shdr *) &hdr_tmpl->header.cfg_shdr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+       if (rc != MBX_TIMEOUT)
+               mempool_free(mboxq, phba->mbox_mem_pool);
+       if (shdr_status || shdr_add_status || rc) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2514 POST_RPI_HDR mailbox failed with "
+                               "status x%x add_status x%x, mbx status x%x\n",
+                               shdr_status, shdr_add_status, rc);
+               rc = -ENXIO;
+       }
+       return rc;
+}
+
+/**
+ * lpfc_sli4_alloc_rpi - Get an available rpi in the device's range
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to post rpi header templates to the
+ * HBA consistent with the SLI-4 interface spec.  This routine
+ * posts a PAGE_SIZE memory region to the port to hold up to
+ * PAGE_SIZE modulo 64 rpi context headers.
+ *
+ * Returns
+ *     A nonzero rpi defined as rpi_base <= rpi < max_rpi if sucessful
+ *     LPFC_RPI_ALLOC_ERROR if no rpis are available.
+ **/
+int
+lpfc_sli4_alloc_rpi(struct lpfc_hba *phba)
+{
+       int rpi;
+       uint16_t max_rpi, rpi_base, rpi_limit;
+       uint16_t rpi_remaining;
+       struct lpfc_rpi_hdr *rpi_hdr;
+
+       max_rpi = phba->sli4_hba.max_cfg_param.max_rpi;
+       rpi_base = phba->sli4_hba.max_cfg_param.rpi_base;
+       rpi_limit = phba->sli4_hba.next_rpi;
+
+       /*
+        * The valid rpi range is not guaranteed to be zero-based.  Start
+        * the search at the rpi_base as reported by the port.
+        */
+       spin_lock_irq(&phba->hbalock);
+       rpi = find_next_zero_bit(phba->sli4_hba.rpi_bmask, rpi_limit, rpi_base);
+       if (rpi >= rpi_limit || rpi < rpi_base)
+               rpi = LPFC_RPI_ALLOC_ERROR;
+       else {
+               set_bit(rpi, phba->sli4_hba.rpi_bmask);
+               phba->sli4_hba.max_cfg_param.rpi_used++;
+               phba->sli4_hba.rpi_count++;
+       }
+
+       /*
+        * Don't try to allocate more rpi header regions if the device limit
+        * on available rpis max has been exhausted.
+        */
+       if ((rpi == LPFC_RPI_ALLOC_ERROR) &&
+           (phba->sli4_hba.rpi_count >= max_rpi)) {
+               spin_unlock_irq(&phba->hbalock);
+               return rpi;
+       }
+
+       /*
+        * If the driver is running low on rpi resources, allocate another
+        * page now.  Note that the next_rpi value is used because
+        * it represents how many are actually in use whereas max_rpi notes
+        * how many are supported max by the device.
+        */
+       rpi_remaining = phba->sli4_hba.next_rpi - rpi_base -
+               phba->sli4_hba.rpi_count;
+       spin_unlock_irq(&phba->hbalock);
+       if (rpi_remaining < LPFC_RPI_LOW_WATER_MARK) {
+               rpi_hdr = lpfc_sli4_create_rpi_hdr(phba);
+               if (!rpi_hdr) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                                       "2002 Error Could not grow rpi "
+                                       "count\n");
+               } else {
+                       lpfc_sli4_post_rpi_hdr(phba, rpi_hdr);
+               }
+       }
+
+       return rpi;
+}
+
+/**
+ * lpfc_sli4_free_rpi - Release an rpi for reuse.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to release an rpi to the pool of
+ * available rpis maintained by the driver.
+ **/
+void
+lpfc_sli4_free_rpi(struct lpfc_hba *phba, int rpi)
+{
+       spin_lock_irq(&phba->hbalock);
+       clear_bit(rpi, phba->sli4_hba.rpi_bmask);
+       phba->sli4_hba.rpi_count--;
+       phba->sli4_hba.max_cfg_param.rpi_used--;
+       spin_unlock_irq(&phba->hbalock);
+}
+
+/**
+ * lpfc_sli4_remove_rpis - Remove the rpi bitmask region
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to remove the memory region that
+ * provided rpi via a bitmask.
+ **/
+void
+lpfc_sli4_remove_rpis(struct lpfc_hba *phba)
+{
+       kfree(phba->sli4_hba.rpi_bmask);
+}
+
+/**
+ * lpfc_sli4_resume_rpi - Remove the rpi bitmask region
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to remove the memory region that
+ * provided rpi via a bitmask.
+ **/
+int
+lpfc_sli4_resume_rpi(struct lpfc_nodelist *ndlp)
+{
+       LPFC_MBOXQ_t *mboxq;
+       struct lpfc_hba *phba = ndlp->phba;
+       int rc;
+
+       /* The port is notified of the header region via a mailbox command. */
+       mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mboxq)
+               return -ENOMEM;
+
+       /* Post all rpi memory regions to the port. */
+       lpfc_resume_rpi(mboxq, ndlp);
+       rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
+       if (rc == MBX_NOT_FINISHED) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "2010 Resume RPI Mailbox failed "
+                               "status %d, mbxStatus x%x\n", rc,
+                               bf_get(lpfc_mqe_status, &mboxq->u.mqe));
+               mempool_free(mboxq, phba->mbox_mem_pool);
+               return -EIO;
+       }
+       return 0;
+}
+
+/**
+ * lpfc_sli4_init_vpi - Initialize a vpi with the port
+ * @phba: pointer to lpfc hba data structure.
+ * @vpi: vpi value to activate with the port.
+ *
+ * This routine is invoked to activate a vpi with the
+ * port when the host intends to use vports with a
+ * nonzero vpi.
+ *
+ * Returns:
+ *    0 success
+ *    -Evalue otherwise
+ **/
+int
+lpfc_sli4_init_vpi(struct lpfc_hba *phba, uint16_t vpi)
+{
+       LPFC_MBOXQ_t *mboxq;
+       int rc = 0;
+       uint32_t mbox_tmo;
+
+       if (vpi == 0)
+               return -EINVAL;
+       mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mboxq)
+               return -ENOMEM;
+       lpfc_init_vpi(mboxq, vpi);
+       mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_INIT_VPI);
+       rc = lpfc_sli_issue_mbox_wait(phba, mboxq, mbox_tmo);
+       if (rc != MBX_TIMEOUT)
+               mempool_free(mboxq, phba->mbox_mem_pool);
+       if (rc != MBX_SUCCESS) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "2022 INIT VPI Mailbox failed "
+                               "status %d, mbxStatus x%x\n", rc,
+                               bf_get(lpfc_mqe_status, &mboxq->u.mqe));
+               rc = -EIO;
+       }
+       return rc;
+}
+
+/**
+ * lpfc_mbx_cmpl_add_fcf_record - add fcf mbox completion handler.
+ * @phba: pointer to lpfc hba data structure.
+ * @mboxq: Pointer to mailbox object.
+ *
+ * This routine is invoked to manually add a single FCF record. The caller
+ * must pass a completely initialized FCF_Record.  This routine takes
+ * care of the nonembedded mailbox operations.
+ **/
+static void
+lpfc_mbx_cmpl_add_fcf_record(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+       void *virt_addr;
+       union lpfc_sli4_cfg_shdr *shdr;
+       uint32_t shdr_status, shdr_add_status;
+
+       virt_addr = mboxq->sge_array->addr[0];
+       /* The IOCTL status is embedded in the mailbox subheader. */
+       shdr = (union lpfc_sli4_cfg_shdr *) virt_addr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+
+       if ((shdr_status || shdr_add_status) &&
+               (shdr_status != STATUS_FCF_IN_USE))
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                       "2558 ADD_FCF_RECORD mailbox failed with "
+                       "status x%x add_status x%x\n",
+                       shdr_status, shdr_add_status);
+
+       lpfc_sli4_mbox_cmd_free(phba, mboxq);
+}
+
+/**
+ * lpfc_sli4_add_fcf_record - Manually add an FCF Record.
+ * @phba: pointer to lpfc hba data structure.
+ * @fcf_record:  pointer to the initialized fcf record to add.
+ *
+ * This routine is invoked to manually add a single FCF record. The caller
+ * must pass a completely initialized FCF_Record.  This routine takes
+ * care of the nonembedded mailbox operations.
+ **/
+int
+lpfc_sli4_add_fcf_record(struct lpfc_hba *phba, struct fcf_record *fcf_record)
+{
+       int rc = 0;
+       LPFC_MBOXQ_t *mboxq;
+       uint8_t *bytep;
+       void *virt_addr;
+       dma_addr_t phys_addr;
+       struct lpfc_mbx_sge sge;
+       uint32_t alloc_len, req_len;
+       uint32_t fcfindex;
+
+       mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mboxq) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                       "2009 Failed to allocate mbox for ADD_FCF cmd\n");
+               return -ENOMEM;
+       }
+
+       req_len = sizeof(struct fcf_record) + sizeof(union lpfc_sli4_cfg_shdr) +
+                 sizeof(uint32_t);
+
+       /* Allocate DMA memory and set up the non-embedded mailbox command */
+       alloc_len = lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_FCOE,
+                                    LPFC_MBOX_OPCODE_FCOE_ADD_FCF,
+                                    req_len, LPFC_SLI4_MBX_NEMBED);
+       if (alloc_len < req_len) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                       "2523 Allocated DMA memory size (x%x) is "
+                       "less than the requested DMA memory "
+                       "size (x%x)\n", alloc_len, req_len);
+               lpfc_sli4_mbox_cmd_free(phba, mboxq);
+               return -ENOMEM;
+       }
+
+       /*
+        * Get the first SGE entry from the non-embedded DMA memory.  This
+        * routine only uses a single SGE.
+        */
+       lpfc_sli4_mbx_sge_get(mboxq, 0, &sge);
+       phys_addr = getPaddr(sge.pa_hi, sge.pa_lo);
+       if (unlikely(!mboxq->sge_array)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+                               "2526 Failed to get the non-embedded SGE "
+                               "virtual address\n");
+               lpfc_sli4_mbox_cmd_free(phba, mboxq);
+               return -ENOMEM;
+       }
+       virt_addr = mboxq->sge_array->addr[0];
+       /*
+        * Configure the FCF record for FCFI 0.  This is the driver's
+        * hardcoded default and gets used in nonFIP mode.
+        */
+       fcfindex = bf_get(lpfc_fcf_record_fcf_index, fcf_record);
+       bytep = virt_addr + sizeof(union lpfc_sli4_cfg_shdr);
+       lpfc_sli_pcimem_bcopy(&fcfindex, bytep, sizeof(uint32_t));
+
+       /*
+        * Copy the fcf_index and the FCF Record Data. The data starts after
+        * the FCoE header plus word10. The data copy needs to be endian
+        * correct.
+        */
+       bytep += sizeof(uint32_t);
+       lpfc_sli_pcimem_bcopy(fcf_record, bytep, sizeof(struct fcf_record));
+       mboxq->vport = phba->pport;
+       mboxq->mbox_cmpl = lpfc_mbx_cmpl_add_fcf_record;
+       rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
+       if (rc == MBX_NOT_FINISHED) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                       "2515 ADD_FCF_RECORD mailbox failed with "
+                       "status 0x%x\n", rc);
+               lpfc_sli4_mbox_cmd_free(phba, mboxq);
+               rc = -EIO;
+       } else
+               rc = 0;
+
+       return rc;
+}
+
+/**
+ * lpfc_sli4_build_dflt_fcf_record - Build the driver's default FCF Record.
+ * @phba: pointer to lpfc hba data structure.
+ * @fcf_record:  pointer to the fcf record to write the default data.
+ * @fcf_index: FCF table entry index.
+ *
+ * This routine is invoked to build the driver's default FCF record.  The
+ * values used are hardcoded.  This routine handles memory initialization.
+ *
+ **/
+void
+lpfc_sli4_build_dflt_fcf_record(struct lpfc_hba *phba,
+                               struct fcf_record *fcf_record,
+                               uint16_t fcf_index)
+{
+       memset(fcf_record, 0, sizeof(struct fcf_record));
+       fcf_record->max_rcv_size = LPFC_FCOE_MAX_RCV_SIZE;
+       fcf_record->fka_adv_period = LPFC_FCOE_FKA_ADV_PER;
+       fcf_record->fip_priority = LPFC_FCOE_FIP_PRIORITY;
+       bf_set(lpfc_fcf_record_mac_0, fcf_record, phba->fc_map[0]);
+       bf_set(lpfc_fcf_record_mac_1, fcf_record, phba->fc_map[1]);
+       bf_set(lpfc_fcf_record_mac_2, fcf_record, phba->fc_map[2]);
+       bf_set(lpfc_fcf_record_mac_3, fcf_record, LPFC_FCOE_FCF_MAC3);
+       bf_set(lpfc_fcf_record_mac_4, fcf_record, LPFC_FCOE_FCF_MAC4);
+       bf_set(lpfc_fcf_record_mac_5, fcf_record, LPFC_FCOE_FCF_MAC5);
+       bf_set(lpfc_fcf_record_fc_map_0, fcf_record, phba->fc_map[0]);
+       bf_set(lpfc_fcf_record_fc_map_1, fcf_record, phba->fc_map[1]);
+       bf_set(lpfc_fcf_record_fc_map_2, fcf_record, phba->fc_map[2]);
+       bf_set(lpfc_fcf_record_fcf_valid, fcf_record, 1);
+       bf_set(lpfc_fcf_record_fcf_index, fcf_record, fcf_index);
+       bf_set(lpfc_fcf_record_mac_addr_prov, fcf_record,
+               LPFC_FCF_FPMA | LPFC_FCF_SPMA);
+       /* Set the VLAN bit map */
+       if (phba->valid_vlan) {
+               fcf_record->vlan_bitmap[phba->vlan_id / 8]
+                       = 1 << (phba->vlan_id % 8);
+       }
+}
+
+/**
+ * lpfc_sli4_read_fcf_record - Read the driver's default FCF Record.
+ * @phba: pointer to lpfc hba data structure.
+ * @fcf_index: FCF table entry offset.
+ *
+ * This routine is invoked to read up to @fcf_num of FCF record from the
+ * device starting with the given @fcf_index.
+ **/
+int
+lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
+{
+       int rc = 0, error;
+       LPFC_MBOXQ_t *mboxq;
+       void *virt_addr;
+       dma_addr_t phys_addr;
+       uint8_t *bytep;
+       struct lpfc_mbx_sge sge;
+       uint32_t alloc_len, req_len;
+       struct lpfc_mbx_read_fcf_tbl *read_fcf;
+
+       mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mboxq) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2000 Failed to allocate mbox for "
+                               "READ_FCF cmd\n");
+               return -ENOMEM;
+       }
+
+       req_len = sizeof(struct fcf_record) +
+                 sizeof(union lpfc_sli4_cfg_shdr) + 2 * sizeof(uint32_t);
+
+       /* Set up READ_FCF SLI4_CONFIG mailbox-ioctl command */
+       alloc_len = lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_FCOE,
+                        LPFC_MBOX_OPCODE_FCOE_READ_FCF_TABLE, req_len,
+                        LPFC_SLI4_MBX_NEMBED);
+
+       if (alloc_len < req_len) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0291 Allocated DMA memory size (x%x) is "
+                               "less than the requested DMA memory "
+                               "size (x%x)\n", alloc_len, req_len);
+               lpfc_sli4_mbox_cmd_free(phba, mboxq);
+               return -ENOMEM;
+       }
+
+       /* Get the first SGE entry from the non-embedded DMA memory. This
+        * routine only uses a single SGE.
+        */
+       lpfc_sli4_mbx_sge_get(mboxq, 0, &sge);
+       phys_addr = getPaddr(sge.pa_hi, sge.pa_lo);
+       if (unlikely(!mboxq->sge_array)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+                               "2527 Failed to get the non-embedded SGE "
+                               "virtual address\n");
+               lpfc_sli4_mbox_cmd_free(phba, mboxq);
+               return -ENOMEM;
+       }
+       virt_addr = mboxq->sge_array->addr[0];
+       read_fcf = (struct lpfc_mbx_read_fcf_tbl *)virt_addr;
+
+       /* Set up command fields */
+       bf_set(lpfc_mbx_read_fcf_tbl_indx, &read_fcf->u.request, fcf_index);
+       /* Perform necessary endian conversion */
+       bytep = virt_addr + sizeof(union lpfc_sli4_cfg_shdr);
+       lpfc_sli_pcimem_bcopy(bytep, bytep, sizeof(uint32_t));
+       mboxq->vport = phba->pport;
+       mboxq->mbox_cmpl = lpfc_mbx_cmpl_read_fcf_record;
+       rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
+       if (rc == MBX_NOT_FINISHED) {
+               lpfc_sli4_mbox_cmd_free(phba, mboxq);
+               error = -EIO;
+       } else
+               error = 0;
+       return error;
+}
index 883938652a6a4eabb2417cf68b69aa1718492888..7d37eb7459bf05ac4ebe326c2df466e2dfb69a1e 100644 (file)
@@ -29,13 +29,23 @@ typedef enum _lpfc_ctx_cmd {
        LPFC_CTX_HOST
 } lpfc_ctx_cmd;
 
+/* This structure is used to carry the needed response IOCB states */
+struct lpfc_sli4_rspiocb_info {
+       uint8_t hw_status;
+       uint8_t bfield;
+#define LPFC_XB        0x1
+#define LPFC_PV        0x2
+       uint8_t priority;
+       uint8_t reserved;
+};
+
 /* This structure is used to handle IOCB requests / responses */
 struct lpfc_iocbq {
        /* lpfc_iocbqs are used in double linked lists */
        struct list_head list;
        struct list_head clist;
        uint16_t iotag;         /* pre-assigned IO tag */
-       uint16_t rsvd1;
+       uint16_t sli4_xritag;   /* pre-assigned XRI, (OXID) tag. */
 
        IOCB_t iocb;            /* IOCB cmd */
        uint8_t retry;          /* retry counter for IOCB cmd - if needed */
@@ -65,7 +75,7 @@ struct lpfc_iocbq {
                           struct lpfc_iocbq *);
        void (*iocb_cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,
                           struct lpfc_iocbq *);
-
+       struct lpfc_sli4_rspiocb_info sli4_info;
 };
 
 #define SLI_IOCB_RET_IOCB      1       /* Return IOCB if cmd ring full */
@@ -81,14 +91,18 @@ struct lpfc_iocbq {
 typedef struct lpfcMboxq {
        /* MBOXQs are used in single linked lists */
        struct list_head list;  /* ptr to next mailbox command */
-       MAILBOX_t mb;           /* Mailbox cmd */
-       struct lpfc_vport *vport;/* virutal port pointer */
+       union {
+               MAILBOX_t mb;           /* Mailbox cmd */
+               struct lpfc_mqe mqe;
+       } u;
+       struct lpfc_vport *vport;/* virtual port pointer */
        void *context1;         /* caller context information */
        void *context2;         /* caller context information */
 
        void (*mbox_cmpl) (struct lpfc_hba *, struct lpfcMboxq *);
        uint8_t mbox_flag;
-
+       struct lpfc_mcqe mcqe;
+       struct lpfc_mbx_nembed_sge_virt *sge_array;
 } LPFC_MBOXQ_t;
 
 #define MBX_POLL        1      /* poll mailbox till command done, then
@@ -230,10 +244,11 @@ struct lpfc_sli {
 
        /* Additional sli_flags */
 #define LPFC_SLI_MBOX_ACTIVE      0x100        /* HBA mailbox is currently active */
-#define LPFC_SLI2_ACTIVE          0x200        /* SLI2 overlay in firmware is active */
+#define LPFC_SLI_ACTIVE           0x200        /* SLI in firmware is active */
 #define LPFC_PROCESS_LA           0x400        /* Able to process link attention */
 #define LPFC_BLOCK_MGMT_IO        0x800        /* Don't allow mgmt mbx or iocb cmds */
 #define LPFC_MENLO_MAINT          0x1000 /* need for menl fw download */
+#define LPFC_SLI_ASYNC_MBX_BLK    0x2000 /* Async mailbox is blocked */
 
        struct lpfc_sli_ring ring[LPFC_MAX_RING];
        int fcp_ring;           /* ring used for FCP initiator commands */
@@ -261,6 +276,8 @@ struct lpfc_sli {
 
 #define LPFC_MBOX_TMO           30     /* Sec tmo for outstanding mbox
                                           command */
+#define LPFC_MBOX_SLI4_CONFIG_TMO 60   /* Sec tmo for outstanding mbox
+                                          command */
 #define LPFC_MBOX_TMO_FLASH_CMD 300     /* Sec tmo for outstanding FLASH write
                                         * or erase cmds. This is especially
                                         * long because of the potential of
diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h
new file mode 100644 (file)
index 0000000..5196b46
--- /dev/null
@@ -0,0 +1,467 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for         *
+ * Fibre Channel Host Bus Adapters.                                *
+ * Copyright (C) 2009 Emulex.  All rights reserved.                *
+ * EMULEX and SLI are trademarks of Emulex.                        *
+ * www.emulex.com                                                  *
+ *                                                                 *
+ * 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 will be useful. *
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND          *
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,  *
+ * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE      *
+ * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
+ * TO BE LEGALLY INVALID.  See the GNU General Public License for  *
+ * more details, a copy of which can be found in the file COPYING  *
+ * included with this package.                                     *
+ *******************************************************************/
+
+#define LPFC_ACTIVE_MBOX_WAIT_CNT               100
+#define LPFC_RELEASE_NOTIFICATION_INTERVAL     32
+#define LPFC_GET_QE_REL_INT                    32
+#define LPFC_RPI_LOW_WATER_MARK                        10
+/* Number of SGL entries can be posted in a 4KB nonembedded mbox command */
+#define LPFC_NEMBED_MBOX_SGL_CNT               254
+
+/* Multi-queue arrangement for fast-path FCP work queues */
+#define LPFC_FN_EQN_MAX       8
+#define LPFC_SP_EQN_DEF       1
+#define LPFC_FP_EQN_DEF       1
+#define LPFC_FP_EQN_MIN       1
+#define LPFC_FP_EQN_MAX       (LPFC_FN_EQN_MAX - LPFC_SP_EQN_DEF)
+
+#define LPFC_FN_WQN_MAX       32
+#define LPFC_SP_WQN_DEF       1
+#define LPFC_FP_WQN_DEF       4
+#define LPFC_FP_WQN_MIN       1
+#define LPFC_FP_WQN_MAX       (LPFC_FN_WQN_MAX - LPFC_SP_WQN_DEF)
+
+/*
+ * Provide the default FCF Record attributes used by the driver
+ * when nonFIP mode is configured and there is no other default
+ * FCF Record attributes.
+ */
+#define LPFC_FCOE_FCF_DEF_INDEX        0
+#define LPFC_FCOE_FCF_GET_FIRST        0xFFFF
+#define LPFC_FCOE_FCF_NEXT_NONE        0xFFFF
+
+/* First 3 bytes of default FCF MAC is specified by FC_MAP */
+#define LPFC_FCOE_FCF_MAC3     0xFF
+#define LPFC_FCOE_FCF_MAC4     0xFF
+#define LPFC_FCOE_FCF_MAC5     0xFE
+#define LPFC_FCOE_FCF_MAP0     0x0E
+#define LPFC_FCOE_FCF_MAP1     0xFC
+#define LPFC_FCOE_FCF_MAP2     0x00
+#define LPFC_FCOE_MAX_RCV_SIZE 0x5AC
+#define LPFC_FCOE_FKA_ADV_PER  0
+#define LPFC_FCOE_FIP_PRIORITY 0x80
+
+enum lpfc_sli4_queue_type {
+       LPFC_EQ,
+       LPFC_GCQ,
+       LPFC_MCQ,
+       LPFC_WCQ,
+       LPFC_RCQ,
+       LPFC_MQ,
+       LPFC_WQ,
+       LPFC_HRQ,
+       LPFC_DRQ
+};
+
+/* The queue sub-type defines the functional purpose of the queue */
+enum lpfc_sli4_queue_subtype {
+       LPFC_NONE,
+       LPFC_MBOX,
+       LPFC_FCP,
+       LPFC_ELS,
+       LPFC_USOL
+};
+
+union sli4_qe {
+       void *address;
+       struct lpfc_eqe *eqe;
+       struct lpfc_cqe *cqe;
+       struct lpfc_mcqe *mcqe;
+       struct lpfc_wcqe_complete *wcqe_complete;
+       struct lpfc_wcqe_release *wcqe_release;
+       struct sli4_wcqe_xri_aborted *wcqe_xri_aborted;
+       struct lpfc_rcqe_complete *rcqe_complete;
+       struct lpfc_mqe *mqe;
+       union  lpfc_wqe *wqe;
+       struct lpfc_rqe *rqe;
+};
+
+struct lpfc_queue {
+       struct list_head list;
+       enum lpfc_sli4_queue_type type;
+       enum lpfc_sli4_queue_subtype subtype;
+       struct lpfc_hba *phba;
+       struct list_head child_list;
+       uint32_t entry_count;   /* Number of entries to support on the queue */
+       uint32_t entry_size;    /* Size of each queue entry. */
+       uint32_t queue_id;      /* Queue ID assigned by the hardware */
+       struct list_head page_list;
+       uint32_t page_count;    /* Number of pages allocated for this queue */
+
+       uint32_t host_index;    /* The host's index for putting or getting */
+       uint32_t hba_index;     /* The last known hba index for get or put */
+       union sli4_qe qe[1];    /* array to index entries (must be last) */
+};
+
+struct lpfc_cq_event {
+       struct list_head list;
+       union {
+               struct lpfc_mcqe                mcqe_cmpl;
+               struct lpfc_acqe_link           acqe_link;
+               struct lpfc_acqe_fcoe           acqe_fcoe;
+               struct lpfc_acqe_dcbx           acqe_dcbx;
+               struct lpfc_rcqe                rcqe_cmpl;
+               struct sli4_wcqe_xri_aborted    wcqe_axri;
+       } cqe;
+};
+
+struct lpfc_sli4_link {
+       uint8_t speed;
+       uint8_t duplex;
+       uint8_t status;
+       uint8_t physical;
+       uint8_t fault;
+};
+
+struct lpfc_fcf {
+       uint8_t  fabric_name[8];
+       uint8_t  mac_addr[6];
+       uint16_t fcf_indx;
+       uint16_t fcfi;
+       uint32_t fcf_flag;
+#define FCF_AVAILABLE  0x01 /* FCF available for discovery */
+#define FCF_REGISTERED 0x02 /* FCF registered with FW */
+#define FCF_DISCOVERED 0x04 /* FCF discovery started  */
+#define FCF_BOOT_ENABLE 0x08 /* Boot bios use this FCF */
+#define FCF_IN_USE     0x10 /* Atleast one discovery completed */
+#define FCF_VALID_VLAN 0x20 /* Use the vlan id specified */
+       uint32_t priority;
+       uint32_t addr_mode;
+       uint16_t vlan_id;
+};
+
+#define LPFC_REGION23_SIGNATURE "RG23"
+#define LPFC_REGION23_VERSION  1
+#define LPFC_REGION23_LAST_REC  0xff
+struct lpfc_fip_param_hdr {
+       uint8_t type;
+#define FCOE_PARAM_TYPE                0xA0
+       uint8_t length;
+#define FCOE_PARAM_LENGTH      2
+       uint8_t parm_version;
+#define FIPP_VERSION           0x01
+       uint8_t parm_flags;
+#define        lpfc_fip_param_hdr_fipp_mode_SHIFT      6
+#define        lpfc_fip_param_hdr_fipp_mode_MASK       0x3
+#define lpfc_fip_param_hdr_fipp_mode_WORD      parm_flags
+#define        FIPP_MODE_ON                            0x2
+#define        FIPP_MODE_OFF                           0x0
+#define FIPP_VLAN_VALID                                0x1
+};
+
+struct lpfc_fcoe_params {
+       uint8_t fc_map[3];
+       uint8_t reserved1;
+       uint16_t vlan_tag;
+       uint8_t reserved[2];
+};
+
+struct lpfc_fcf_conn_hdr {
+       uint8_t type;
+#define FCOE_CONN_TBL_TYPE             0xA1
+       uint8_t length;   /* words */
+       uint8_t reserved[2];
+};
+
+struct lpfc_fcf_conn_rec {
+       uint16_t flags;
+#define        FCFCNCT_VALID           0x0001
+#define        FCFCNCT_BOOT            0x0002
+#define        FCFCNCT_PRIMARY         0x0004   /* if not set, Secondary */
+#define        FCFCNCT_FBNM_VALID      0x0008
+#define        FCFCNCT_SWNM_VALID      0x0010
+#define        FCFCNCT_VLAN_VALID      0x0020
+#define        FCFCNCT_AM_VALID        0x0040
+#define        FCFCNCT_AM_PREFERRED    0x0080   /* if not set, AM Required */
+#define        FCFCNCT_AM_SPMA         0x0100   /* if not set, FPMA */
+
+       uint16_t vlan_tag;
+       uint8_t fabric_name[8];
+       uint8_t switch_name[8];
+};
+
+struct lpfc_fcf_conn_entry {
+       struct list_head list;
+       struct lpfc_fcf_conn_rec conn_rec;
+};
+
+/*
+ * Define the host's bootstrap mailbox.  This structure contains
+ * the member attributes needed to create, use, and destroy the
+ * bootstrap mailbox region.
+ *
+ * The macro definitions for the bmbx data structure are defined
+ * in lpfc_hw4.h with the register definition.
+ */
+struct lpfc_bmbx {
+       struct lpfc_dmabuf *dmabuf;
+       struct dma_address dma_address;
+       void *avirt;
+       dma_addr_t aphys;
+       uint32_t bmbx_size;
+};
+
+#define LPFC_EQE_SIZE LPFC_EQE_SIZE_4
+
+#define LPFC_EQE_SIZE_4B       4
+#define LPFC_EQE_SIZE_16B      16
+#define LPFC_CQE_SIZE          16
+#define LPFC_WQE_SIZE          64
+#define LPFC_MQE_SIZE          256
+#define LPFC_RQE_SIZE          8
+
+#define LPFC_EQE_DEF_COUNT     1024
+#define LPFC_CQE_DEF_COUNT      256
+#define LPFC_WQE_DEF_COUNT      64
+#define LPFC_MQE_DEF_COUNT      16
+#define LPFC_RQE_DEF_COUNT     512
+
+#define LPFC_QUEUE_NOARM       false
+#define LPFC_QUEUE_REARM       true
+
+
+/*
+ * SLI4 CT field defines
+ */
+#define SLI4_CT_RPI 0
+#define SLI4_CT_VPI 1
+#define SLI4_CT_VFI 2
+#define SLI4_CT_FCFI 3
+
+#define LPFC_SLI4_MAX_SEGMENT_SIZE 0x10000
+
+/*
+ * SLI4 specific data structures
+ */
+struct lpfc_max_cfg_param {
+       uint16_t max_xri;
+       uint16_t xri_base;
+       uint16_t xri_used;
+       uint16_t max_rpi;
+       uint16_t rpi_base;
+       uint16_t rpi_used;
+       uint16_t max_vpi;
+       uint16_t vpi_base;
+       uint16_t vpi_used;
+       uint16_t max_vfi;
+       uint16_t vfi_base;
+       uint16_t vfi_used;
+       uint16_t max_fcfi;
+       uint16_t fcfi_base;
+       uint16_t fcfi_used;
+       uint16_t max_eq;
+       uint16_t max_rq;
+       uint16_t max_cq;
+       uint16_t max_wq;
+};
+
+struct lpfc_hba;
+/* SLI4 HBA multi-fcp queue handler struct */
+struct lpfc_fcp_eq_hdl {
+       uint32_t idx;
+       struct lpfc_hba *phba;
+};
+
+/* SLI4 HBA data structure entries */
+struct lpfc_sli4_hba {
+       void __iomem *conf_regs_memmap_p; /* Kernel memory mapped address for
+                                            PCI BAR0, config space registers */
+       void __iomem *ctrl_regs_memmap_p; /* Kernel memory mapped address for
+                                            PCI BAR1, control registers */
+       void __iomem *drbl_regs_memmap_p; /* Kernel memory mapped address for
+                                            PCI BAR2, doorbell registers */
+       /* BAR0 PCI config space register memory map */
+       void __iomem *UERRLOregaddr; /* Address to UERR_STATUS_LO register */
+       void __iomem *UERRHIregaddr; /* Address to UERR_STATUS_HI register */
+       void __iomem *ONLINE0regaddr; /* Address to components of internal UE */
+       void __iomem *ONLINE1regaddr; /* Address to components of internal UE */
+#define LPFC_ONLINE_NERR       0xFFFFFFFF
+       void __iomem *SCRATCHPADregaddr; /* Address to scratchpad register */
+       /* BAR1 FCoE function CSR register memory map */
+       void __iomem *STAregaddr;    /* Address to HST_STATE register */
+       void __iomem *ISRregaddr;    /* Address to HST_ISR register */
+       void __iomem *IMRregaddr;    /* Address to HST_IMR register */
+       void __iomem *ISCRregaddr;   /* Address to HST_ISCR register */
+       /* BAR2 VF-0 doorbell register memory map */
+       void __iomem *RQDBregaddr;   /* Address to RQ_DOORBELL register */
+       void __iomem *WQDBregaddr;   /* Address to WQ_DOORBELL register */
+       void __iomem *EQCQDBregaddr; /* Address to EQCQ_DOORBELL register */
+       void __iomem *MQDBregaddr;   /* Address to MQ_DOORBELL register */
+       void __iomem *BMBXregaddr;   /* Address to BootStrap MBX register */
+
+       struct msix_entry *msix_entries;
+       uint32_t cfg_eqn;
+       struct lpfc_fcp_eq_hdl *fcp_eq_hdl; /* FCP per-WQ handle */
+       /* Pointers to the constructed SLI4 queues */
+       struct lpfc_queue **fp_eq; /* Fast-path event queue */
+       struct lpfc_queue *sp_eq;  /* Slow-path event queue */
+       struct lpfc_queue **fcp_wq;/* Fast-path FCP work queue */
+       struct lpfc_queue *mbx_wq; /* Slow-path MBOX work queue */
+       struct lpfc_queue *els_wq; /* Slow-path ELS work queue */
+       struct lpfc_queue *hdr_rq; /* Slow-path Header Receive queue */
+       struct lpfc_queue *dat_rq; /* Slow-path Data Receive queue */
+       struct lpfc_queue **fcp_cq;/* Fast-path FCP compl queue */
+       struct lpfc_queue *mbx_cq; /* Slow-path mailbox complete queue */
+       struct lpfc_queue *els_cq; /* Slow-path ELS response complete queue */
+       struct lpfc_queue *rxq_cq; /* Slow-path unsolicited complete queue */
+
+       /* Setup information for various queue parameters */
+       int eq_esize;
+       int eq_ecount;
+       int cq_esize;
+       int cq_ecount;
+       int wq_esize;
+       int wq_ecount;
+       int mq_esize;
+       int mq_ecount;
+       int rq_esize;
+       int rq_ecount;
+#define LPFC_SP_EQ_MAX_INTR_SEC         10000
+#define LPFC_FP_EQ_MAX_INTR_SEC         10000
+
+       uint32_t intr_enable;
+       struct lpfc_bmbx bmbx;
+       struct lpfc_max_cfg_param max_cfg_param;
+       uint16_t next_xri; /* last_xri - max_cfg_param.xri_base = used */
+       uint16_t next_rpi;
+       uint16_t scsi_xri_max;
+       uint16_t scsi_xri_cnt;
+       struct list_head lpfc_free_sgl_list;
+       struct list_head lpfc_sgl_list;
+       struct lpfc_sglq **lpfc_els_sgl_array;
+       struct list_head lpfc_abts_els_sgl_list;
+       struct lpfc_scsi_buf **lpfc_scsi_psb_array;
+       struct list_head lpfc_abts_scsi_buf_list;
+       uint32_t total_sglq_bufs;
+       struct lpfc_sglq **lpfc_sglq_active_list;
+       struct list_head lpfc_rpi_hdr_list;
+       unsigned long *rpi_bmask;
+       uint16_t rpi_count;
+       struct lpfc_sli4_flags sli4_flags;
+       struct list_head sp_rspiocb_work_queue;
+       struct list_head sp_cqe_event_pool;
+       struct list_head sp_asynce_work_queue;
+       struct list_head sp_fcp_xri_aborted_work_queue;
+       struct list_head sp_els_xri_aborted_work_queue;
+       struct list_head sp_unsol_work_queue;
+       struct lpfc_sli4_link link_state;
+       spinlock_t abts_scsi_buf_list_lock; /* list of aborted SCSI IOs */
+       spinlock_t abts_sgl_list_lock; /* list of aborted els IOs */
+};
+
+enum lpfc_sge_type {
+       GEN_BUFF_TYPE,
+       SCSI_BUFF_TYPE
+};
+
+struct lpfc_sglq {
+       /* lpfc_sglqs are used in double linked lists */
+       struct list_head list;
+       struct list_head clist;
+       enum lpfc_sge_type buff_type; /* is this a scsi sgl */
+       uint16_t iotag;         /* pre-assigned IO tag */
+       uint16_t sli4_xritag;   /* pre-assigned XRI, (OXID) tag. */
+       struct sli4_sge *sgl;   /* pre-assigned SGL */
+       void *virt;             /* virtual address. */
+       dma_addr_t phys;        /* physical address */
+};
+
+struct lpfc_rpi_hdr {
+       struct list_head list;
+       uint32_t len;
+       struct lpfc_dmabuf *dmabuf;
+       uint32_t page_count;
+       uint32_t start_rpi;
+};
+
+/*
+ * SLI4 specific function prototypes
+ */
+int lpfc_pci_function_reset(struct lpfc_hba *);
+int lpfc_sli4_hba_setup(struct lpfc_hba *);
+int lpfc_sli4_hba_down(struct lpfc_hba *);
+int lpfc_sli4_config(struct lpfc_hba *, struct lpfcMboxq *, uint8_t,
+                    uint8_t, uint32_t, bool);
+void lpfc_sli4_mbox_cmd_free(struct lpfc_hba *, struct lpfcMboxq *);
+void lpfc_sli4_mbx_sge_set(struct lpfcMboxq *, uint32_t, dma_addr_t, uint32_t);
+void lpfc_sli4_mbx_sge_get(struct lpfcMboxq *, uint32_t,
+                          struct lpfc_mbx_sge *);
+
+void lpfc_sli4_hba_reset(struct lpfc_hba *);
+struct lpfc_queue *lpfc_sli4_queue_alloc(struct lpfc_hba *, uint32_t,
+                       uint32_t);
+void lpfc_sli4_queue_free(struct lpfc_queue *);
+uint32_t lpfc_eq_create(struct lpfc_hba *, struct lpfc_queue *, uint16_t);
+uint32_t lpfc_cq_create(struct lpfc_hba *, struct lpfc_queue *,
+                       struct lpfc_queue *, uint32_t, uint32_t);
+uint32_t lpfc_mq_create(struct lpfc_hba *, struct lpfc_queue *,
+                       struct lpfc_queue *, uint32_t);
+uint32_t lpfc_wq_create(struct lpfc_hba *, struct lpfc_queue *,
+                       struct lpfc_queue *, uint32_t);
+uint32_t lpfc_rq_create(struct lpfc_hba *, struct lpfc_queue *,
+                       struct lpfc_queue *, struct lpfc_queue *, uint32_t);
+uint32_t lpfc_eq_destroy(struct lpfc_hba *, struct lpfc_queue *);
+uint32_t lpfc_cq_destroy(struct lpfc_hba *, struct lpfc_queue *);
+uint32_t lpfc_mq_destroy(struct lpfc_hba *, struct lpfc_queue *);
+uint32_t lpfc_wq_destroy(struct lpfc_hba *, struct lpfc_queue *);
+uint32_t lpfc_rq_destroy(struct lpfc_hba *, struct lpfc_queue *,
+                        struct lpfc_queue *);
+int lpfc_sli4_queue_setup(struct lpfc_hba *);
+void lpfc_sli4_queue_unset(struct lpfc_hba *);
+int lpfc_sli4_post_sgl(struct lpfc_hba *, dma_addr_t, dma_addr_t, uint16_t);
+int lpfc_sli4_repost_scsi_sgl_list(struct lpfc_hba *);
+int lpfc_sli4_remove_all_sgl_pages(struct lpfc_hba *);
+uint16_t lpfc_sli4_next_xritag(struct lpfc_hba *);
+int lpfc_sli4_post_async_mbox(struct lpfc_hba *);
+int lpfc_sli4_post_sgl_list(struct lpfc_hba *phba);
+int lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *, struct list_head *, int);
+struct lpfc_cq_event *__lpfc_sli4_cq_event_alloc(struct lpfc_hba *);
+struct lpfc_cq_event *lpfc_sli4_cq_event_alloc(struct lpfc_hba *);
+void __lpfc_sli4_cq_event_release(struct lpfc_hba *, struct lpfc_cq_event *);
+void lpfc_sli4_cq_event_release(struct lpfc_hba *, struct lpfc_cq_event *);
+int lpfc_sli4_init_rpi_hdrs(struct lpfc_hba *);
+int lpfc_sli4_post_rpi_hdr(struct lpfc_hba *, struct lpfc_rpi_hdr *);
+int lpfc_sli4_post_all_rpi_hdrs(struct lpfc_hba *);
+struct lpfc_rpi_hdr *lpfc_sli4_create_rpi_hdr(struct lpfc_hba *);
+void lpfc_sli4_remove_rpi_hdrs(struct lpfc_hba *);
+int lpfc_sli4_alloc_rpi(struct lpfc_hba *);
+void lpfc_sli4_free_rpi(struct lpfc_hba *, int);
+void lpfc_sli4_remove_rpis(struct lpfc_hba *);
+void lpfc_sli4_async_event_proc(struct lpfc_hba *);
+int lpfc_sli4_resume_rpi(struct lpfc_nodelist *);
+void lpfc_sli4_fcp_xri_abort_event_proc(struct lpfc_hba *);
+void lpfc_sli4_els_xri_abort_event_proc(struct lpfc_hba *);
+void lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *,
+                              struct sli4_wcqe_xri_aborted *);
+void lpfc_sli4_els_xri_aborted(struct lpfc_hba *,
+                              struct sli4_wcqe_xri_aborted *);
+int lpfc_sli4_brdreset(struct lpfc_hba *);
+int lpfc_sli4_add_fcf_record(struct lpfc_hba *, struct fcf_record *);
+void lpfc_sli_remove_dflt_fcf(struct lpfc_hba *);
+int lpfc_sli4_get_els_iocb_cnt(struct lpfc_hba *);
+int lpfc_sli4_init_vpi(struct lpfc_hba *, uint16_t);
+uint32_t lpfc_sli4_cq_release(struct lpfc_queue *, bool);
+uint32_t lpfc_sli4_eq_release(struct lpfc_queue *, bool);
+void lpfc_sli4_fcfi_unreg(struct lpfc_hba *, uint16_t);
+int lpfc_sli4_read_fcf_record(struct lpfc_hba *, uint16_t);
+void lpfc_mbx_cmpl_read_fcf_record(struct lpfc_hba *, LPFC_MBOXQ_t *);
+int lpfc_sli4_post_status_check(struct lpfc_hba *);
+uint8_t lpfc_sli4_mbox_opcode_get(struct lpfc_hba *, struct lpfcMboxq *);
+
index e599519e3078c214be69fd3ad415cc6648b653c2..6b8a148f0a55ca7f001dc87586972d9ac6b0ce65 100644 (file)
@@ -18,7 +18,7 @@
  * included with this package.                                     *
  *******************************************************************/
 
-#define LPFC_DRIVER_VERSION "8.3.1"
+#define LPFC_DRIVER_VERSION "8.3.2"
 
 #define LPFC_DRIVER_NAME               "lpfc"
 #define LPFC_SP_DRIVER_HANDLER_NAME    "lpfc:sp"
index 917ad56b0aff33c358e9632630142c75003f53e9..a6313ee84ac50356f289f02d28cc0c1788ba47d5 100644 (file)
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport_fc.h>
+#include "lpfc_hw4.h"
 #include "lpfc_hw.h"
 #include "lpfc_sli.h"
+#include "lpfc_sli4.h"
 #include "lpfc_nl.h"
 #include "lpfc_disc.h"
 #include "lpfc_scsi.h"
@@ -89,6 +91,8 @@ lpfc_alloc_vpi(struct lpfc_hba *phba)
                vpi = 0;
        else
                set_bit(vpi, phba->vpi_bmask);
+       if (phba->sli_rev == LPFC_SLI_REV4)
+               phba->sli4_hba.max_cfg_param.vpi_used++;
        spin_unlock_irq(&phba->hbalock);
        return vpi;
 }
@@ -96,8 +100,12 @@ lpfc_alloc_vpi(struct lpfc_hba *phba)
 static void
 lpfc_free_vpi(struct lpfc_hba *phba, int vpi)
 {
+       if (vpi == 0)
+               return;
        spin_lock_irq(&phba->hbalock);
        clear_bit(vpi, phba->vpi_bmask);
+       if (phba->sli_rev == LPFC_SLI_REV4)
+               phba->sli4_hba.max_cfg_param.vpi_used--;
        spin_unlock_irq(&phba->hbalock);
 }
 
@@ -113,7 +121,7 @@ lpfc_vport_sparm(struct lpfc_hba *phba, struct lpfc_vport *vport)
        if (!pmb) {
                return -ENOMEM;
        }
-       mb = &pmb->mb;
+       mb = &pmb->u.mb;
 
        lpfc_read_sparam(phba, pmb, vport->vpi);
        /*
@@ -243,23 +251,22 @@ static void lpfc_discovery_wait(struct lpfc_vport *vport)
                    (vport->fc_flag & wait_flags)  ||
                    ((vport->port_state > LPFC_VPORT_FAILED) &&
                     (vport->port_state < LPFC_VPORT_READY))) {
-                       lpfc_printf_log(phba, KERN_INFO, LOG_VPORT,
+                       lpfc_printf_vlog(vport, KERN_INFO, LOG_VPORT,
                                        "1833 Vport discovery quiesce Wait:"
-                                       " vpi x%x state x%x fc_flags x%x"
+                                       " state x%x fc_flags x%x"
                                        " num_nodes x%x, waiting 1000 msecs"
                                        " total wait msecs x%x\n",
-                                       vport->vpi, vport->port_state,
-                                       vport->fc_flag, vport->num_disc_nodes,
+                                       vport->port_state, vport->fc_flag,
+                                       vport->num_disc_nodes,
                                        jiffies_to_msecs(jiffies - start_time));
                        msleep(1000);
                } else {
                        /* Base case.  Wait variants satisfied.  Break out */
-                       lpfc_printf_log(phba, KERN_INFO, LOG_VPORT,
+                       lpfc_printf_vlog(vport, KERN_INFO, LOG_VPORT,
                                         "1834 Vport discovery quiesced:"
-                                        " vpi x%x state x%x fc_flags x%x"
+                                        " state x%x fc_flags x%x"
                                         " wait msecs x%x\n",
-                                        vport->vpi, vport->port_state,
-                                        vport->fc_flag,
+                                        vport->port_state, vport->fc_flag,
                                         jiffies_to_msecs(jiffies
                                                - start_time));
                        break;
@@ -267,12 +274,10 @@ static void lpfc_discovery_wait(struct lpfc_vport *vport)
        }
 
        if (time_after(jiffies, wait_time_max))
-               lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
                                "1835 Vport discovery quiesce failed:"
-                               " vpi x%x state x%x fc_flags x%x"
-                               " wait msecs x%x\n",
-                               vport->vpi, vport->port_state,
-                               vport->fc_flag,
+                               " state x%x fc_flags x%x wait msecs x%x\n",
+                               vport->port_state, vport->fc_flag,
                                jiffies_to_msecs(jiffies - start_time));
 }
 
@@ -308,6 +313,21 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
                goto error_out;
        }
 
+       /*
+        * In SLI4, the vpi must be activated before it can be used
+        * by the port.
+        */
+       if (phba->sli_rev == LPFC_SLI_REV4) {
+               rc = lpfc_sli4_init_vpi(phba, vpi);
+               if (rc) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
+                                       "1838 Failed to INIT_VPI on vpi %d "
+                                       "status %d\n", vpi, rc);
+                       rc = VPORT_NORESOURCES;
+                       lpfc_free_vpi(phba, vpi);
+                       goto error_out;
+               }
+       }
 
        /* Assign an unused board number */
        if ((instance = lpfc_get_instance()) < 0) {
@@ -535,6 +555,16 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
                                 "physical host\n");
                return VPORT_ERROR;
        }
+
+       /* If the vport is a static vport fail the deletion. */
+       if ((vport->vport_flag & STATIC_VPORT) &&
+               !(phba->pport->load_flag & FC_UNLOADING)) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
+                                "1837 vport_delete failed: Cannot delete "
+                                "static vport.\n");
+               return VPORT_ERROR;
+       }
+
        /*
         * If we are not unloading the driver then prevent the vport_delete
         * from happening until after this vport's discovery is finished.
@@ -710,7 +740,7 @@ lpfc_create_vport_work_array(struct lpfc_hba *phba)
        struct lpfc_vport *port_iterator;
        struct lpfc_vport **vports;
        int index = 0;
-       vports = kzalloc((phba->max_vpi + 1) * sizeof(struct lpfc_vport *),
+       vports = kzalloc((phba->max_vports + 1) * sizeof(struct lpfc_vport *),
                         GFP_KERNEL);
        if (vports == NULL)
                return NULL;
@@ -734,7 +764,7 @@ lpfc_destroy_vport_work_array(struct lpfc_hba *phba, struct lpfc_vport **vports)
        int i;
        if (vports == NULL)
                return;
-       for (i=0; vports[i] != NULL && i <= phba->max_vpi; i++)
+       for (i = 0; vports[i] != NULL && i <= phba->max_vports; i++)
                scsi_host_put(lpfc_shost_from_vport(vports[i]));
        kfree(vports);
 }
index 36b1d1052ba1281eb6e33d8fe154d7fce87aacf1..286c185fa9e40a5b7347de0dc99b06c4d2f0e617 100644 (file)
@@ -61,6 +61,7 @@
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsi_transport_sas.h>
 #include <scsi/scsi_dbg.h>
+#include <scsi/scsi_eh.h>
 
 #include "mpt2sas_debug.h"
 
 #define MPT2SAS_DRIVER_NAME            "mpt2sas"
 #define MPT2SAS_AUTHOR "LSI Corporation <DL-MPTFusionLinux@lsi.com>"
 #define MPT2SAS_DESCRIPTION    "LSI MPT Fusion SAS 2.0 Device Driver"
-#define MPT2SAS_DRIVER_VERSION         "01.100.02.00"
+#define MPT2SAS_DRIVER_VERSION         "01.100.03.00"
 #define MPT2SAS_MAJOR_VERSION          01
 #define MPT2SAS_MINOR_VERSION          100
-#define MPT2SAS_BUILD_VERSION          02
+#define MPT2SAS_BUILD_VERSION          03
 #define MPT2SAS_RELEASE_VERSION                00
 
 /*
index ba6ab170bdf02ca5fc75e2b8a6b22bce943b23c1..14e473d1fa7b18193e0673d86dec3bfc8737aa84 100644 (file)
@@ -473,7 +473,7 @@ _ctl_poll(struct file *filep, poll_table *wait)
 }
 
 /**
- * _ctl_do_task_abort - assign an active smid to the abort_task
+ * _ctl_set_task_mid - assign an active smid to tm request
  * @ioc: per adapter object
  * @karg - (struct mpt2_ioctl_command)
  * @tm_request - pointer to mf from user space
@@ -482,7 +482,7 @@ _ctl_poll(struct file *filep, poll_table *wait)
  * during failure, the reply frame is filled.
  */
 static int
-_ctl_do_task_abort(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg,
+_ctl_set_task_mid(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg,
     Mpi2SCSITaskManagementRequest_t *tm_request)
 {
        u8 found = 0;
@@ -494,6 +494,14 @@ _ctl_do_task_abort(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg,
        Mpi2SCSITaskManagementReply_t *tm_reply;
        u32 sz;
        u32 lun;
+       char *desc = NULL;
+
+       if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK)
+               desc = "abort_task";
+       else if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK)
+               desc = "query_task";
+       else
+               return 0;
 
        lun = scsilun_to_int((struct scsi_lun *)tm_request->LUN);
 
@@ -517,13 +525,13 @@ _ctl_do_task_abort(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg,
        spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
 
        if (!found) {
-               dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "ABORT_TASK: "
-                   "DevHandle(0x%04x), lun(%d), no active mid!!\n", ioc->name,
-                   tm_request->DevHandle, lun));
+               dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+                   "handle(0x%04x), lun(%d), no active mid!!\n", ioc->name,
+                   desc, tm_request->DevHandle, lun));
                tm_reply = ioc->ctl_cmds.reply;
                tm_reply->DevHandle = tm_request->DevHandle;
                tm_reply->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
-               tm_reply->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK;
+               tm_reply->TaskType = tm_request->TaskType;
                tm_reply->MsgLength = sizeof(Mpi2SCSITaskManagementReply_t)/4;
                tm_reply->VP_ID = tm_request->VP_ID;
                tm_reply->VF_ID = tm_request->VF_ID;
@@ -535,9 +543,9 @@ _ctl_do_task_abort(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg,
                return 1;
        }
 
-       dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "ABORT_TASK: "
-           "DevHandle(0x%04x), lun(%d), smid(%d)\n", ioc->name,
-           tm_request->DevHandle, lun, tm_request->TaskMID));
+       dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+           "handle(0x%04x), lun(%d), task_mid(%d)\n", ioc->name,
+           desc, tm_request->DevHandle, lun, tm_request->TaskMID));
        return 0;
 }
 
@@ -739,8 +747,10 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc,
                    (Mpi2SCSITaskManagementRequest_t *)mpi_request;
 
                if (tm_request->TaskType ==
-                   MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
-                       if (_ctl_do_task_abort(ioc, &karg, tm_request)) {
+                   MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK ||
+                   tm_request->TaskType ==
+                   MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK) {
+                       if (_ctl_set_task_mid(ioc, &karg, tm_request)) {
                                mpt2sas_base_free_smid(ioc, smid);
                                goto out;
                        }
index e3a7967259e75fe5cfac5f5101a4e8f2912992f6..2a01a5f2a84dddc4d105d5ef88030abe1adc21ab 100644 (file)
@@ -197,12 +197,12 @@ static struct pci_device_id scsih_pci_table[] = {
 MODULE_DEVICE_TABLE(pci, scsih_pci_table);
 
 /**
- * scsih_set_debug_level - global setting of ioc->logging_level.
+ * _scsih_set_debug_level - global setting of ioc->logging_level.
  *
  * Note: The logging levels are defined in mpt2sas_debug.h.
  */
 static int
-scsih_set_debug_level(const char *val, struct kernel_param *kp)
+_scsih_set_debug_level(const char *val, struct kernel_param *kp)
 {
        int ret = param_set_int(val, kp);
        struct MPT2SAS_ADAPTER *ioc;
@@ -215,7 +215,7 @@ scsih_set_debug_level(const char *val, struct kernel_param *kp)
                ioc->logging_level = logging_level;
        return 0;
 }
-module_param_call(logging_level, scsih_set_debug_level, param_get_int,
+module_param_call(logging_level, _scsih_set_debug_level, param_get_int,
     &logging_level, 0644);
 
 /**
@@ -883,6 +883,41 @@ _scsih_scsi_lookup_find_by_target(struct MPT2SAS_ADAPTER *ioc, int id,
        return found;
 }
 
+/**
+ * _scsih_scsi_lookup_find_by_lun - search for matching channel:id:lun
+ * @ioc: per adapter object
+ * @id: target id
+ * @lun: lun number
+ * @channel: channel
+ * Context: This function will acquire ioc->scsi_lookup_lock.
+ *
+ * This will search for a matching channel:id:lun in the scsi_lookup array,
+ * returning 1 if found.
+ */
+static u8
+_scsih_scsi_lookup_find_by_lun(struct MPT2SAS_ADAPTER *ioc, int id,
+    unsigned int lun, int channel)
+{
+       u8 found;
+       unsigned long   flags;
+       int i;
+
+       spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+       found = 0;
+       for (i = 0 ; i < ioc->request_depth; i++) {
+               if (ioc->scsi_lookup[i].scmd &&
+                   (ioc->scsi_lookup[i].scmd->device->id == id &&
+                   ioc->scsi_lookup[i].scmd->device->channel == channel &&
+                   ioc->scsi_lookup[i].scmd->device->lun == lun)) {
+                       found = 1;
+                       goto out;
+               }
+       }
+ out:
+       spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+       return found;
+}
+
 /**
  * _scsih_get_chain_buffer_dma - obtain block of chains (dma address)
  * @ioc: per adapter object
@@ -1047,14 +1082,14 @@ _scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc,
 }
 
 /**
- * scsih_change_queue_depth - setting device queue depth
+ * _scsih_change_queue_depth - setting device queue depth
  * @sdev: scsi device struct
  * @qdepth: requested queue depth
  *
  * Returns queue depth.
  */
 static int
-scsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
+_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
 {
        struct Scsi_Host *shost = sdev->host;
        int max_depth;
@@ -1079,14 +1114,14 @@ scsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
 }
 
 /**
- * scsih_change_queue_depth - changing device queue tag type
+ * _scsih_change_queue_depth - changing device queue tag type
  * @sdev: scsi device struct
  * @tag_type: requested tag type
  *
  * Returns queue tag type.
  */
 static int
-scsih_change_queue_type(struct scsi_device *sdev, int tag_type)
+_scsih_change_queue_type(struct scsi_device *sdev, int tag_type)
 {
        if (sdev->tagged_supported) {
                scsi_set_tag_type(sdev, tag_type);
@@ -1101,14 +1136,14 @@ scsih_change_queue_type(struct scsi_device *sdev, int tag_type)
 }
 
 /**
- * scsih_target_alloc - target add routine
+ * _scsih_target_alloc - target add routine
  * @starget: scsi target struct
  *
  * Returns 0 if ok. Any other return is assumed to be an error and
  * the device is ignored.
  */
 static int
-scsih_target_alloc(struct scsi_target *starget)
+_scsih_target_alloc(struct scsi_target *starget)
 {
        struct Scsi_Host *shost = dev_to_shost(&starget->dev);
        struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
@@ -1163,13 +1198,13 @@ scsih_target_alloc(struct scsi_target *starget)
 }
 
 /**
- * scsih_target_destroy - target destroy routine
+ * _scsih_target_destroy - target destroy routine
  * @starget: scsi target struct
  *
  * Returns nothing.
  */
 static void
-scsih_target_destroy(struct scsi_target *starget)
+_scsih_target_destroy(struct scsi_target *starget)
 {
        struct Scsi_Host *shost = dev_to_shost(&starget->dev);
        struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
@@ -1212,14 +1247,14 @@ scsih_target_destroy(struct scsi_target *starget)
 }
 
 /**
- * scsih_slave_alloc - device add routine
+ * _scsih_slave_alloc - device add routine
  * @sdev: scsi device struct
  *
  * Returns 0 if ok. Any other return is assumed to be an error and
  * the device is ignored.
  */
 static int
-scsih_slave_alloc(struct scsi_device *sdev)
+_scsih_slave_alloc(struct scsi_device *sdev)
 {
        struct Scsi_Host *shost;
        struct MPT2SAS_ADAPTER *ioc;
@@ -1273,13 +1308,13 @@ scsih_slave_alloc(struct scsi_device *sdev)
 }
 
 /**
- * scsih_slave_destroy - device destroy routine
+ * _scsih_slave_destroy - device destroy routine
  * @sdev: scsi device struct
  *
  * Returns nothing.
  */
 static void
-scsih_slave_destroy(struct scsi_device *sdev)
+_scsih_slave_destroy(struct scsi_device *sdev)
 {
        struct MPT2SAS_TARGET *sas_target_priv_data;
        struct scsi_target *starget;
@@ -1295,13 +1330,13 @@ scsih_slave_destroy(struct scsi_device *sdev)
 }
 
 /**
- * scsih_display_sata_capabilities - sata capabilities
+ * _scsih_display_sata_capabilities - sata capabilities
  * @ioc: per adapter object
  * @sas_device: the sas_device object
  * @sdev: scsi device struct
  */
 static void
-scsih_display_sata_capabilities(struct MPT2SAS_ADAPTER *ioc,
+_scsih_display_sata_capabilities(struct MPT2SAS_ADAPTER *ioc,
     struct _sas_device *sas_device, struct scsi_device *sdev)
 {
        Mpi2ConfigReply_t mpi_reply;
@@ -1401,14 +1436,14 @@ _scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc,
 }
 
 /**
- * scsih_slave_configure - device configure routine.
+ * _scsih_slave_configure - device configure routine.
  * @sdev: scsi device struct
  *
  * Returns 0 if ok. Any other return is assumed to be an error and
  * the device is ignored.
  */
 static int
-scsih_slave_configure(struct scsi_device *sdev)
+_scsih_slave_configure(struct scsi_device *sdev)
 {
        struct Scsi_Host *shost = sdev->host;
        struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
@@ -1489,7 +1524,7 @@ scsih_slave_configure(struct scsi_device *sdev)
                    r_level, raid_device->handle,
                    (unsigned long long)raid_device->wwid,
                    raid_device->num_pds, ds);
-               scsih_change_queue_depth(sdev, qdepth);
+               _scsih_change_queue_depth(sdev, qdepth);
                return 0;
        }
 
@@ -1532,10 +1567,10 @@ scsih_slave_configure(struct scsi_device *sdev)
                    sas_device->slot);
 
                if (!ssp_target)
-                       scsih_display_sata_capabilities(ioc, sas_device, sdev);
+                       _scsih_display_sata_capabilities(ioc, sas_device, sdev);
        }
 
-       scsih_change_queue_depth(sdev, qdepth);
+       _scsih_change_queue_depth(sdev, qdepth);
 
        if (ssp_target)
                sas_read_port_mode_page(sdev);
@@ -1543,7 +1578,7 @@ scsih_slave_configure(struct scsi_device *sdev)
 }
 
 /**
- * scsih_bios_param - fetch head, sector, cylinder info for a disk
+ * _scsih_bios_param - fetch head, sector, cylinder info for a disk
  * @sdev: scsi device struct
  * @bdev: pointer to block device context
  * @capacity: device size (in 512 byte sectors)
@@ -1555,7 +1590,7 @@ scsih_slave_configure(struct scsi_device *sdev)
  * Return nothing.
  */
 static int
-scsih_bios_param(struct scsi_device *sdev, struct block_device *bdev,
+_scsih_bios_param(struct scsi_device *sdev, struct block_device *bdev,
     sector_t capacity, int params[])
 {
        int             heads;
@@ -1636,7 +1671,7 @@ _scsih_response_code(struct MPT2SAS_ADAPTER *ioc, u8 response_code)
 }
 
 /**
- * scsih_tm_done - tm completion routine
+ * _scsih_tm_done - tm completion routine
  * @ioc: per adapter object
  * @smid: system request message index
  * @VF_ID: virtual function id
@@ -1648,7 +1683,7 @@ _scsih_response_code(struct MPT2SAS_ADAPTER *ioc, u8 response_code)
  * Return nothing.
  */
 static void
-scsih_tm_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
+_scsih_tm_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
 {
        MPI2DefaultReply_t *mpi_reply;
 
@@ -1823,13 +1858,13 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun,
 }
 
 /**
- * scsih_abort - eh threads main abort routine
+ * _scsih_abort - eh threads main abort routine
  * @sdev: scsi device struct
  *
  * Returns SUCCESS if command aborted else FAILED
  */
 static int
-scsih_abort(struct scsi_cmnd *scmd)
+_scsih_abort(struct scsi_cmnd *scmd)
 {
        struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
        struct MPT2SAS_DEVICE *sas_device_priv_data;
@@ -1889,15 +1924,86 @@ scsih_abort(struct scsi_cmnd *scmd)
        return r;
 }
 
+/**
+ * _scsih_dev_reset - eh threads main device reset routine
+ * @sdev: scsi device struct
+ *
+ * Returns SUCCESS if command aborted else FAILED
+ */
+static int
+_scsih_dev_reset(struct scsi_cmnd *scmd)
+{
+       struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
+       struct MPT2SAS_DEVICE *sas_device_priv_data;
+       struct _sas_device *sas_device;
+       unsigned long flags;
+       u16     handle;
+       int r;
+
+       printk(MPT2SAS_INFO_FMT "attempting device reset! scmd(%p)\n",
+           ioc->name, scmd);
+       scsi_print_command(scmd);
+
+       sas_device_priv_data = scmd->device->hostdata;
+       if (!sas_device_priv_data || !sas_device_priv_data->sas_target) {
+               printk(MPT2SAS_INFO_FMT "device been deleted! scmd(%p)\n",
+                   ioc->name, scmd);
+               scmd->result = DID_NO_CONNECT << 16;
+               scmd->scsi_done(scmd);
+               r = SUCCESS;
+               goto out;
+       }
+
+       /* for hidden raid components obtain the volume_handle */
+       handle = 0;
+       if (sas_device_priv_data->sas_target->flags &
+           MPT_TARGET_FLAGS_RAID_COMPONENT) {
+               spin_lock_irqsave(&ioc->sas_device_lock, flags);
+               sas_device = _scsih_sas_device_find_by_handle(ioc,
+                  sas_device_priv_data->sas_target->handle);
+               if (sas_device)
+                       handle = sas_device->volume_handle;
+               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+       } else
+               handle = sas_device_priv_data->sas_target->handle;
+
+       if (!handle) {
+               scmd->result = DID_RESET << 16;
+               r = FAILED;
+               goto out;
+       }
+
+       mutex_lock(&ioc->tm_cmds.mutex);
+       mpt2sas_scsih_issue_tm(ioc, handle, 0,
+           MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, scmd->device->lun,
+           30);
+
+       /*
+        *  sanity check see whether all commands to this device been
+        *  completed
+        */
+       if (_scsih_scsi_lookup_find_by_lun(ioc, scmd->device->id,
+           scmd->device->lun, scmd->device->channel))
+               r = FAILED;
+       else
+               r = SUCCESS;
+       ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
+       mutex_unlock(&ioc->tm_cmds.mutex);
+
+ out:
+       printk(MPT2SAS_INFO_FMT "device reset: %s scmd(%p)\n",
+           ioc->name, ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+       return r;
+}
 
 /**
- * scsih_dev_reset - eh threads main device reset routine
+ * _scsih_target_reset - eh threads main target reset routine
  * @sdev: scsi device struct
  *
  * Returns SUCCESS if command aborted else FAILED
  */
 static int
-scsih_dev_reset(struct scsi_cmnd *scmd)
+_scsih_target_reset(struct scsi_cmnd *scmd)
 {
        struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
        struct MPT2SAS_DEVICE *sas_device_priv_data;
@@ -1912,7 +2018,7 @@ scsih_dev_reset(struct scsi_cmnd *scmd)
 
        sas_device_priv_data = scmd->device->hostdata;
        if (!sas_device_priv_data || !sas_device_priv_data->sas_target) {
-               printk(MPT2SAS_INFO_FMT "device been deleted! scmd(%p)\n",
+               printk(MPT2SAS_INFO_FMT "target been deleted! scmd(%p)\n",
                    ioc->name, scmd);
                scmd->result = DID_NO_CONNECT << 16;
                scmd->scsi_done(scmd);
@@ -1962,13 +2068,13 @@ scsih_dev_reset(struct scsi_cmnd *scmd)
 }
 
 /**
- * scsih_abort - eh threads main host reset routine
+ * _scsih_abort - eh threads main host reset routine
  * @sdev: scsi device struct
  *
  * Returns SUCCESS if command aborted else FAILED
  */
 static int
-scsih_host_reset(struct scsi_cmnd *scmd)
+_scsih_host_reset(struct scsi_cmnd *scmd)
 {
        struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
        int r, retval;
@@ -2390,7 +2496,107 @@ mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
 }
 
 /**
- * scsih_qcmd - main scsi request entry point
+ * _scsih_setup_eedp - setup MPI request for EEDP transfer
+ * @scmd: pointer to scsi command object
+ * @mpi_request: pointer to the SCSI_IO reqest message frame
+ *
+ * Supporting protection 1 and 3.
+ *
+ * Returns nothing
+ */
+static void
+_scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request)
+{
+       u16 eedp_flags;
+       unsigned char prot_op = scsi_get_prot_op(scmd);
+       unsigned char prot_type = scsi_get_prot_type(scmd);
+
+       if (prot_type == SCSI_PROT_DIF_TYPE0 ||
+          prot_type == SCSI_PROT_DIF_TYPE2 ||
+          prot_op == SCSI_PROT_NORMAL)
+               return;
+
+       if (prot_op ==  SCSI_PROT_READ_STRIP)
+               eedp_flags = MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP;
+       else if (prot_op ==  SCSI_PROT_WRITE_INSERT)
+               eedp_flags = MPI2_SCSIIO_EEDPFLAGS_INSERT_OP;
+       else
+               return;
+
+       mpi_request->EEDPBlockSize = scmd->device->sector_size;
+
+       switch (prot_type) {
+       case SCSI_PROT_DIF_TYPE1:
+
+               /*
+               * enable ref/guard checking
+               * auto increment ref tag
+               */
+               mpi_request->EEDPFlags = eedp_flags |
+                   MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG |
+                   MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG |
+                   MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
+               mpi_request->CDB.EEDP32.PrimaryReferenceTag =
+                   cpu_to_be32(scsi_get_lba(scmd));
+
+               break;
+
+       case SCSI_PROT_DIF_TYPE3:
+
+               /*
+               * enable guard checking
+               */
+               mpi_request->EEDPFlags = eedp_flags |
+                   MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
+
+               break;
+       }
+}
+
+/**
+ * _scsih_eedp_error_handling - return sense code for EEDP errors
+ * @scmd: pointer to scsi command object
+ * @ioc_status: ioc status
+ *
+ * Returns nothing
+ */
+static void
+_scsih_eedp_error_handling(struct scsi_cmnd *scmd, u16 ioc_status)
+{
+       u8 ascq;
+       u8 sk;
+       u8 host_byte;
+
+       switch (ioc_status) {
+       case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
+               ascq = 0x01;
+               break;
+       case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
+               ascq = 0x02;
+               break;
+       case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
+               ascq = 0x03;
+               break;
+       default:
+               ascq = 0x00;
+               break;
+       }
+
+       if (scmd->sc_data_direction == DMA_TO_DEVICE) {
+               sk = ILLEGAL_REQUEST;
+               host_byte = DID_ABORT;
+       } else {
+               sk = ABORTED_COMMAND;
+               host_byte = DID_OK;
+       }
+
+       scsi_build_sense_buffer(0, scmd->sense_buffer, sk, 0x10, ascq);
+       scmd->result = DRIVER_SENSE << 24 | (host_byte << 16) |
+           SAM_STAT_CHECK_CONDITION;
+}
+
+/**
+ * _scsih_qcmd - main scsi request entry point
  * @scmd: pointer to scsi command object
  * @done: function pointer to be invoked on completion
  *
@@ -2401,7 +2607,7 @@ mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
  * SCSI_MLQUEUE_HOST_BUSY if the entire host queue is full
  */
 static int
-scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
+_scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
 {
        struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
        struct MPT2SAS_DEVICE *sas_device_priv_data;
@@ -2470,6 +2676,7 @@ scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
        }
        mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
        memset(mpi_request, 0, sizeof(Mpi2SCSIIORequest_t));
+       _scsih_setup_eedp(scmd, mpi_request);
        mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
        if (sas_device_priv_data->sas_target->flags &
            MPT_TARGET_FLAGS_RAID_COMPONENT)
@@ -2604,6 +2811,15 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
        case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
                desc_ioc_state = "scsi ext terminated";
                break;
+       case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
+               desc_ioc_state = "eedp guard error";
+               break;
+       case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
+               desc_ioc_state = "eedp ref tag error";
+               break;
+       case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
+               desc_ioc_state = "eedp app tag error";
+               break;
        default:
                desc_ioc_state = "unknown";
                break;
@@ -2783,7 +2999,7 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle)
 }
 
 /**
- * scsih_io_done - scsi request callback
+ * _scsih_io_done - scsi request callback
  * @ioc: per adapter object
  * @smid: system request message index
  * @VF_ID: virtual function id
@@ -2794,7 +3010,7 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle)
  * Return nothing.
  */
 static void
-scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
+_scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
 {
        Mpi2SCSIIORequest_t *mpi_request;
        Mpi2SCSIIOReply_t *mpi_reply;
@@ -2939,6 +3155,11 @@ scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
                        scmd->result = DID_RESET << 16;
                break;
 
+       case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
+       case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
+       case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
+               _scsih_eedp_error_handling(scmd, ioc_status);
+               break;
        case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR:
        case MPI2_IOCSTATUS_INVALID_FUNCTION:
        case MPI2_IOCSTATUS_INVALID_SGL:
@@ -5130,18 +5351,19 @@ static struct scsi_host_template scsih_driver_template = {
        .module                         = THIS_MODULE,
        .name                           = "Fusion MPT SAS Host",
        .proc_name                      = MPT2SAS_DRIVER_NAME,
-       .queuecommand                   = scsih_qcmd,
-       .target_alloc                   = scsih_target_alloc,
-       .slave_alloc                    = scsih_slave_alloc,
-       .slave_configure                = scsih_slave_configure,
-       .target_destroy                 = scsih_target_destroy,
-       .slave_destroy                  = scsih_slave_destroy,
-       .change_queue_depth             = scsih_change_queue_depth,
-       .change_queue_type              = scsih_change_queue_type,
-       .eh_abort_handler               = scsih_abort,
-       .eh_device_reset_handler        = scsih_dev_reset,
-       .eh_host_reset_handler          = scsih_host_reset,
-       .bios_param                     = scsih_bios_param,
+       .queuecommand                   = _scsih_qcmd,
+       .target_alloc                   = _scsih_target_alloc,
+       .slave_alloc                    = _scsih_slave_alloc,
+       .slave_configure                = _scsih_slave_configure,
+       .target_destroy                 = _scsih_target_destroy,
+       .slave_destroy                  = _scsih_slave_destroy,
+       .change_queue_depth             = _scsih_change_queue_depth,
+       .change_queue_type              = _scsih_change_queue_type,
+       .eh_abort_handler               = _scsih_abort,
+       .eh_device_reset_handler        = _scsih_dev_reset,
+       .eh_target_reset_handler        = _scsih_target_reset,
+       .eh_host_reset_handler          = _scsih_host_reset,
+       .bios_param                     = _scsih_bios_param,
        .can_queue                      = 1,
        .this_id                        = -1,
        .sg_tablesize                   = MPT2SAS_SG_DEPTH,
@@ -5228,13 +5450,13 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
 }
 
 /**
- * scsih_remove - detach and remove add host
+ * _scsih_remove - detach and remove add host
  * @pdev: PCI device struct
  *
  * Return nothing.
  */
 static void __devexit
-scsih_remove(struct pci_dev *pdev)
+_scsih_remove(struct pci_dev *pdev)
 {
        struct Scsi_Host *shost = pci_get_drvdata(pdev);
        struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
@@ -5442,14 +5664,14 @@ _scsih_probe_devices(struct MPT2SAS_ADAPTER *ioc)
 }
 
 /**
- * scsih_probe - attach and add scsi host
+ * _scsih_probe - attach and add scsi host
  * @pdev: PCI device struct
  * @id: pci device id
  *
  * Returns 0 success, anything else error.
  */
 static int
-scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+_scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
        struct MPT2SAS_ADAPTER *ioc;
        struct Scsi_Host *shost;
@@ -5503,6 +5725,9 @@ scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                goto out_add_shost_fail;
        }
 
+       scsi_host_set_prot(shost, SHOST_DIF_TYPE1_PROTECTION
+           | SHOST_DIF_TYPE3_PROTECTION);
+
        /* event thread */
        snprintf(ioc->firmware_event_name, sizeof(ioc->firmware_event_name),
            "fw_event%d", ioc->id);
@@ -5536,14 +5761,14 @@ scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
 #ifdef CONFIG_PM
 /**
- * scsih_suspend - power management suspend main entry point
+ * _scsih_suspend - power management suspend main entry point
  * @pdev: PCI device struct
  * @state: PM state change to (usually PCI_D3)
  *
  * Returns 0 success, anything else error.
  */
 static int
-scsih_suspend(struct pci_dev *pdev, pm_message_t state)
+_scsih_suspend(struct pci_dev *pdev, pm_message_t state)
 {
        struct Scsi_Host *shost = pci_get_drvdata(pdev);
        struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
@@ -5564,13 +5789,13 @@ scsih_suspend(struct pci_dev *pdev, pm_message_t state)
 }
 
 /**
- * scsih_resume - power management resume main entry point
+ * _scsih_resume - power management resume main entry point
  * @pdev: PCI device struct
  *
  * Returns 0 success, anything else error.
  */
 static int
-scsih_resume(struct pci_dev *pdev)
+_scsih_resume(struct pci_dev *pdev)
 {
        struct Scsi_Host *shost = pci_get_drvdata(pdev);
        struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
@@ -5599,22 +5824,22 @@ scsih_resume(struct pci_dev *pdev)
 static struct pci_driver scsih_driver = {
        .name           = MPT2SAS_DRIVER_NAME,
        .id_table       = scsih_pci_table,
-       .probe          = scsih_probe,
-       .remove         = __devexit_p(scsih_remove),
+       .probe          = _scsih_probe,
+       .remove         = __devexit_p(_scsih_remove),
 #ifdef CONFIG_PM
-       .suspend        = scsih_suspend,
-       .resume         = scsih_resume,
+       .suspend        = _scsih_suspend,
+       .resume         = _scsih_resume,
 #endif
 };
 
 
 /**
- * scsih_init - main entry point for this driver.
+ * _scsih_init - main entry point for this driver.
  *
  * Returns 0 success, anything else error.
  */
 static int __init
-scsih_init(void)
+_scsih_init(void)
 {
        int error;
 
@@ -5630,10 +5855,10 @@ scsih_init(void)
        mpt2sas_base_initialize_callback_handler();
 
         /* queuecommand callback hander */
-       scsi_io_cb_idx = mpt2sas_base_register_callback_handler(scsih_io_done);
+       scsi_io_cb_idx = mpt2sas_base_register_callback_handler(_scsih_io_done);
 
        /* task managment callback handler */
-       tm_cb_idx = mpt2sas_base_register_callback_handler(scsih_tm_done);
+       tm_cb_idx = mpt2sas_base_register_callback_handler(_scsih_tm_done);
 
        /* base internal commands callback handler */
        base_cb_idx = mpt2sas_base_register_callback_handler(mpt2sas_base_done);
@@ -5659,12 +5884,12 @@ scsih_init(void)
 }
 
 /**
- * scsih_exit - exit point for this driver (when it is a module).
+ * _scsih_exit - exit point for this driver (when it is a module).
  *
  * Returns 0 success, anything else error.
  */
 static void __exit
-scsih_exit(void)
+_scsih_exit(void)
 {
        printk(KERN_INFO "mpt2sas version %s unloading\n",
            MPT2SAS_DRIVER_VERSION);
@@ -5682,5 +5907,5 @@ scsih_exit(void)
        mpt2sas_ctl_exit();
 }
 
-module_init(scsih_init);
-module_exit(scsih_exit);
+module_init(_scsih_init);
+module_exit(_scsih_exit);
index 5c65da519e39af511334816c18977912a4920637..686695b155c7af9899864c5d3103e3cfd49624b8 100644 (file)
@@ -264,7 +264,7 @@ struct rep_manu_reply{
 };
 
 /**
- * transport_expander_report_manufacture - obtain SMP report_manufacture
+ * _transport_expander_report_manufacture - obtain SMP report_manufacture
  * @ioc: per adapter object
  * @sas_address: expander sas address
  * @edev: the sas_expander_device object
@@ -274,7 +274,7 @@ struct rep_manu_reply{
  * Returns 0 for success, non-zero for failure.
  */
 static int
-transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc,
+_transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc,
     u64 sas_address, struct sas_expander_device *edev)
 {
        Mpi2SmpPassthroughRequest_t *mpi_request;
@@ -578,7 +578,7 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle,
            MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER ||
            mpt2sas_port->remote_identify.device_type ==
            MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER)
-               transport_expander_report_manufacture(ioc,
+               _transport_expander_report_manufacture(ioc,
                    mpt2sas_port->remote_identify.sas_address,
                    rphy_to_expander_device(rphy));
 
@@ -852,7 +852,7 @@ rphy_to_ioc(struct sas_rphy *rphy)
 }
 
 /**
- * transport_get_linkerrors -
+ * _transport_get_linkerrors -
  * @phy: The sas phy object
  *
  * Only support sas_host direct attached phys.
@@ -860,7 +860,7 @@ rphy_to_ioc(struct sas_rphy *rphy)
  *
  */
 static int
-transport_get_linkerrors(struct sas_phy *phy)
+_transport_get_linkerrors(struct sas_phy *phy)
 {
        struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy);
        struct _sas_phy *mpt2sas_phy;
@@ -903,14 +903,14 @@ transport_get_linkerrors(struct sas_phy *phy)
 }
 
 /**
- * transport_get_enclosure_identifier -
+ * _transport_get_enclosure_identifier -
  * @phy: The sas phy object
  *
  * Obtain the enclosure logical id for an expander.
  * Returns 0 for success, non-zero for failure.
  */
 static int
-transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier)
+_transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier)
 {
        struct MPT2SAS_ADAPTER *ioc = rphy_to_ioc(rphy);
        struct _sas_node *sas_expander;
@@ -929,13 +929,13 @@ transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier)
 }
 
 /**
- * transport_get_bay_identifier -
+ * _transport_get_bay_identifier -
  * @phy: The sas phy object
  *
  * Returns the slot id for a device that resides inside an enclosure.
  */
 static int
-transport_get_bay_identifier(struct sas_rphy *rphy)
+_transport_get_bay_identifier(struct sas_rphy *rphy)
 {
        struct MPT2SAS_ADAPTER *ioc = rphy_to_ioc(rphy);
        struct _sas_device *sas_device;
@@ -953,7 +953,7 @@ transport_get_bay_identifier(struct sas_rphy *rphy)
 }
 
 /**
- * transport_phy_reset -
+ * _transport_phy_reset -
  * @phy: The sas phy object
  * @hard_reset:
  *
@@ -961,7 +961,7 @@ transport_get_bay_identifier(struct sas_rphy *rphy)
  * Returns 0 for success, non-zero for failure.
  */
 static int
-transport_phy_reset(struct sas_phy *phy, int hard_reset)
+_transport_phy_reset(struct sas_phy *phy, int hard_reset)
 {
        struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy);
        struct _sas_phy *mpt2sas_phy;
@@ -1002,7 +1002,7 @@ transport_phy_reset(struct sas_phy *phy, int hard_reset)
 }
 
 /**
- * transport_smp_handler - transport portal for smp passthru
+ * _transport_smp_handler - transport portal for smp passthru
  * @shost: shost object
  * @rphy: sas transport rphy object
  * @req:
@@ -1012,7 +1012,7 @@ transport_phy_reset(struct sas_phy *phy, int hard_reset)
  *           smp_rep_general /sys/class/bsg/expander-5:0
  */
 static int
-transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
+_transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
     struct request *req)
 {
        struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
@@ -1200,11 +1200,11 @@ transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
 }
 
 struct sas_function_template mpt2sas_transport_functions = {
-       .get_linkerrors         = transport_get_linkerrors,
-       .get_enclosure_identifier = transport_get_enclosure_identifier,
-       .get_bay_identifier     = transport_get_bay_identifier,
-       .phy_reset              = transport_phy_reset,
-       .smp_handler            = transport_smp_handler,
+       .get_linkerrors         = _transport_get_linkerrors,
+       .get_enclosure_identifier = _transport_get_enclosure_identifier,
+       .get_bay_identifier     = _transport_get_bay_identifier,
+       .phy_reset              = _transport_phy_reset,
+       .smp_handler            = _transport_smp_handler,
 };
 
 struct scsi_transport_template *mpt2sas_transport_template;
diff --git a/drivers/scsi/mvsas.c b/drivers/scsi/mvsas.c
deleted file mode 100644 (file)
index e4acebd..0000000
+++ /dev/null
@@ -1,3222 +0,0 @@
-/*
-       mvsas.c - Marvell 88SE6440 SAS/SATA support
-
-       Copyright 2007 Red Hat, Inc.
-       Copyright 2008 Marvell. <kewei@marvell.com>
-
-       This program is free software; you can redistribute it and/or
-       modify it under the terms of the GNU General Public License as
-       published by the Free Software Foundation; either version 2,
-       or (at your option) any later version.
-
-       This program is distributed in the hope that it will be useful,
-       but WITHOUT ANY WARRANTY; without even the implied warranty
-       of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-       See the GNU General Public License for more details.
-
-       You should have received a copy of the GNU General Public
-       License along with this program; see the file COPYING.  If not,
-       write to the Free Software Foundation, 675 Mass Ave, Cambridge,
-       MA 02139, USA.
-
-       ---------------------------------------------------------------
-
-       Random notes:
-       * hardware supports controlling the endian-ness of data
-         structures.  this permits elimination of all the le32_to_cpu()
-         and cpu_to_le32() conversions.
-
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#include <linux/delay.h>
-#include <linux/dma-mapping.h>
-#include <linux/ctype.h>
-#include <scsi/libsas.h>
-#include <scsi/scsi_tcq.h>
-#include <scsi/sas_ata.h>
-#include <asm/io.h>
-
-#define DRV_NAME       "mvsas"
-#define DRV_VERSION    "0.5.2"
-#define _MV_DUMP       0
-#define MVS_DISABLE_NVRAM
-#define MVS_DISABLE_MSI
-
-#define mr32(reg)      readl(regs + MVS_##reg)
-#define mw32(reg,val)  writel((val), regs + MVS_##reg)
-#define mw32_f(reg,val)        do {                    \
-       writel((val), regs + MVS_##reg);        \
-       readl(regs + MVS_##reg);                \
-       } while (0)
-
-#define MVS_ID_NOT_MAPPED      0x7f
-#define MVS_CHIP_SLOT_SZ       (1U << mvi->chip->slot_width)
-
-/* offset for D2H FIS in the Received FIS List Structure */
-#define SATA_RECEIVED_D2H_FIS(reg_set) \
-       ((void *) mvi->rx_fis + 0x400 + 0x100 * reg_set + 0x40)
-#define SATA_RECEIVED_PIO_FIS(reg_set) \
-       ((void *) mvi->rx_fis + 0x400 + 0x100 * reg_set + 0x20)
-#define UNASSOC_D2H_FIS(id)            \
-       ((void *) mvi->rx_fis + 0x100 * id)
-
-#define for_each_phy(__lseq_mask, __mc, __lseq, __rest)                        \
-       for ((__mc) = (__lseq_mask), (__lseq) = 0;                      \
-                                       (__mc) != 0 && __rest;          \
-                                       (++__lseq), (__mc) >>= 1)
-
-/* driver compile-time configuration */
-enum driver_configuration {
-       MVS_TX_RING_SZ          = 1024, /* TX ring size (12-bit) */
-       MVS_RX_RING_SZ          = 1024, /* RX ring size (12-bit) */
-                                       /* software requires power-of-2
-                                          ring size */
-
-       MVS_SLOTS               = 512,  /* command slots */
-       MVS_SLOT_BUF_SZ         = 8192, /* cmd tbl + IU + status + PRD */
-       MVS_SSP_CMD_SZ          = 64,   /* SSP command table buffer size */
-       MVS_ATA_CMD_SZ          = 96,   /* SATA command table buffer size */
-       MVS_OAF_SZ              = 64,   /* Open address frame buffer size */
-
-       MVS_RX_FIS_COUNT        = 17,   /* Optional rx'd FISs (max 17) */
-
-       MVS_QUEUE_SIZE          = 30,   /* Support Queue depth */
-       MVS_CAN_QUEUE           = MVS_SLOTS - 1,        /* SCSI Queue depth */
-};
-
-/* unchangeable hardware details */
-enum hardware_details {
-       MVS_MAX_PHYS            = 8,    /* max. possible phys */
-       MVS_MAX_PORTS           = 8,    /* max. possible ports */
-       MVS_RX_FISL_SZ          = 0x400 + (MVS_RX_FIS_COUNT * 0x100),
-};
-
-/* peripheral registers (BAR2) */
-enum peripheral_registers {
-       SPI_CTL                 = 0x10, /* EEPROM control */
-       SPI_CMD                 = 0x14, /* EEPROM command */
-       SPI_DATA                = 0x18, /* EEPROM data */
-};
-
-enum peripheral_register_bits {
-       TWSI_RDY                = (1U << 7),    /* EEPROM interface ready */
-       TWSI_RD                 = (1U << 4),    /* EEPROM read access */
-
-       SPI_ADDR_MASK           = 0x3ffff,      /* bits 17:0 */
-};
-
-/* enhanced mode registers (BAR4) */
-enum hw_registers {
-       MVS_GBL_CTL             = 0x04,  /* global control */
-       MVS_GBL_INT_STAT        = 0x08,  /* global irq status */
-       MVS_GBL_PI              = 0x0C,  /* ports implemented bitmask */
-       MVS_GBL_PORT_TYPE       = 0xa0,  /* port type */
-
-       MVS_CTL                 = 0x100, /* SAS/SATA port configuration */
-       MVS_PCS                 = 0x104, /* SAS/SATA port control/status */
-       MVS_CMD_LIST_LO         = 0x108, /* cmd list addr */
-       MVS_CMD_LIST_HI         = 0x10C,
-       MVS_RX_FIS_LO           = 0x110, /* RX FIS list addr */
-       MVS_RX_FIS_HI           = 0x114,
-
-       MVS_TX_CFG              = 0x120, /* TX configuration */
-       MVS_TX_LO               = 0x124, /* TX (delivery) ring addr */
-       MVS_TX_HI               = 0x128,
-
-       MVS_TX_PROD_IDX         = 0x12C, /* TX producer pointer */
-       MVS_TX_CONS_IDX         = 0x130, /* TX consumer pointer (RO) */
-       MVS_RX_CFG              = 0x134, /* RX configuration */
-       MVS_RX_LO               = 0x138, /* RX (completion) ring addr */
-       MVS_RX_HI               = 0x13C,
-       MVS_RX_CONS_IDX         = 0x140, /* RX consumer pointer (RO) */
-
-       MVS_INT_COAL            = 0x148, /* Int coalescing config */
-       MVS_INT_COAL_TMOUT      = 0x14C, /* Int coalescing timeout */
-       MVS_INT_STAT            = 0x150, /* Central int status */
-       MVS_INT_MASK            = 0x154, /* Central int enable */
-       MVS_INT_STAT_SRS        = 0x158, /* SATA register set status */
-       MVS_INT_MASK_SRS        = 0x15C,
-
-                                        /* ports 1-3 follow after this */
-       MVS_P0_INT_STAT         = 0x160, /* port0 interrupt status */
-       MVS_P0_INT_MASK         = 0x164, /* port0 interrupt mask */
-       MVS_P4_INT_STAT         = 0x200, /* Port 4 interrupt status */
-       MVS_P4_INT_MASK         = 0x204, /* Port 4 interrupt enable mask */
-
-                                        /* ports 1-3 follow after this */
-       MVS_P0_SER_CTLSTAT      = 0x180, /* port0 serial control/status */
-       MVS_P4_SER_CTLSTAT      = 0x220, /* port4 serial control/status */
-
-       MVS_CMD_ADDR            = 0x1B8, /* Command register port (addr) */
-       MVS_CMD_DATA            = 0x1BC, /* Command register port (data) */
-
-                                        /* ports 1-3 follow after this */
-       MVS_P0_CFG_ADDR         = 0x1C0, /* port0 phy register address */
-       MVS_P0_CFG_DATA         = 0x1C4, /* port0 phy register data */
-       MVS_P4_CFG_ADDR         = 0x230, /* Port 4 config address */
-       MVS_P4_CFG_DATA         = 0x234, /* Port 4 config data */
-
-                                        /* ports 1-3 follow after this */
-       MVS_P0_VSR_ADDR         = 0x1E0, /* port0 VSR address */
-       MVS_P0_VSR_DATA         = 0x1E4, /* port0 VSR data */
-       MVS_P4_VSR_ADDR         = 0x250, /* port 4 VSR addr */
-       MVS_P4_VSR_DATA         = 0x254, /* port 4 VSR data */
-};
-
-enum hw_register_bits {
-       /* MVS_GBL_CTL */
-       INT_EN                  = (1U << 1),    /* Global int enable */
-       HBA_RST                 = (1U << 0),    /* HBA reset */
-
-       /* MVS_GBL_INT_STAT */
-       INT_XOR                 = (1U << 4),    /* XOR engine event */
-       INT_SAS_SATA            = (1U << 0),    /* SAS/SATA event */
-
-       /* MVS_GBL_PORT_TYPE */                 /* shl for ports 1-3 */
-       SATA_TARGET             = (1U << 16),   /* port0 SATA target enable */
-       MODE_AUTO_DET_PORT7 = (1U << 15),       /* port0 SAS/SATA autodetect */
-       MODE_AUTO_DET_PORT6 = (1U << 14),
-       MODE_AUTO_DET_PORT5 = (1U << 13),
-       MODE_AUTO_DET_PORT4 = (1U << 12),
-       MODE_AUTO_DET_PORT3 = (1U << 11),
-       MODE_AUTO_DET_PORT2 = (1U << 10),
-       MODE_AUTO_DET_PORT1 = (1U << 9),
-       MODE_AUTO_DET_PORT0 = (1U << 8),
-       MODE_AUTO_DET_EN    =   MODE_AUTO_DET_PORT0 | MODE_AUTO_DET_PORT1 |
-                               MODE_AUTO_DET_PORT2 | MODE_AUTO_DET_PORT3 |
-                               MODE_AUTO_DET_PORT4 | MODE_AUTO_DET_PORT5 |
-                               MODE_AUTO_DET_PORT6 | MODE_AUTO_DET_PORT7,
-       MODE_SAS_PORT7_MASK = (1U << 7),  /* port0 SAS(1), SATA(0) mode */
-       MODE_SAS_PORT6_MASK = (1U << 6),
-       MODE_SAS_PORT5_MASK = (1U << 5),
-       MODE_SAS_PORT4_MASK = (1U << 4),
-       MODE_SAS_PORT3_MASK = (1U << 3),
-       MODE_SAS_PORT2_MASK = (1U << 2),
-       MODE_SAS_PORT1_MASK = (1U << 1),
-       MODE_SAS_PORT0_MASK = (1U << 0),
-       MODE_SAS_SATA   =       MODE_SAS_PORT0_MASK | MODE_SAS_PORT1_MASK |
-                               MODE_SAS_PORT2_MASK | MODE_SAS_PORT3_MASK |
-                               MODE_SAS_PORT4_MASK | MODE_SAS_PORT5_MASK |
-                               MODE_SAS_PORT6_MASK | MODE_SAS_PORT7_MASK,
-
-                               /* SAS_MODE value may be
-                                * dictated (in hw) by values
-                                * of SATA_TARGET & AUTO_DET
-                                */
-
-       /* MVS_TX_CFG */
-       TX_EN                   = (1U << 16),   /* Enable TX */
-       TX_RING_SZ_MASK         = 0xfff,        /* TX ring size, bits 11:0 */
-
-       /* MVS_RX_CFG */
-       RX_EN                   = (1U << 16),   /* Enable RX */
-       RX_RING_SZ_MASK         = 0xfff,        /* RX ring size, bits 11:0 */
-
-       /* MVS_INT_COAL */
-       COAL_EN                 = (1U << 16),   /* Enable int coalescing */
-
-       /* MVS_INT_STAT, MVS_INT_MASK */
-       CINT_I2C                = (1U << 31),   /* I2C event */
-       CINT_SW0                = (1U << 30),   /* software event 0 */
-       CINT_SW1                = (1U << 29),   /* software event 1 */
-       CINT_PRD_BC             = (1U << 28),   /* PRD BC err for read cmd */
-       CINT_DMA_PCIE           = (1U << 27),   /* DMA to PCIE timeout */
-       CINT_MEM                = (1U << 26),   /* int mem parity err */
-       CINT_I2C_SLAVE          = (1U << 25),   /* slave I2C event */
-       CINT_SRS                = (1U << 3),    /* SRS event */
-       CINT_CI_STOP            = (1U << 1),    /* cmd issue stopped */
-       CINT_DONE               = (1U << 0),    /* cmd completion */
-
-                                               /* shl for ports 1-3 */
-       CINT_PORT_STOPPED       = (1U << 16),   /* port0 stopped */
-       CINT_PORT               = (1U << 8),    /* port0 event */
-       CINT_PORT_MASK_OFFSET   = 8,
-       CINT_PORT_MASK          = (0xFF << CINT_PORT_MASK_OFFSET),
-
-       /* TX (delivery) ring bits */
-       TXQ_CMD_SHIFT           = 29,
-       TXQ_CMD_SSP             = 1,            /* SSP protocol */
-       TXQ_CMD_SMP             = 2,            /* SMP protocol */
-       TXQ_CMD_STP             = 3,            /* STP/SATA protocol */
-       TXQ_CMD_SSP_FREE_LIST   = 4,            /* add to SSP targ free list */
-       TXQ_CMD_SLOT_RESET      = 7,            /* reset command slot */
-       TXQ_MODE_I              = (1U << 28),   /* mode: 0=target,1=initiator */
-       TXQ_PRIO_HI             = (1U << 27),   /* priority: 0=normal, 1=high */
-       TXQ_SRS_SHIFT           = 20,           /* SATA register set */
-       TXQ_SRS_MASK            = 0x7f,
-       TXQ_PHY_SHIFT           = 12,           /* PHY bitmap */
-       TXQ_PHY_MASK            = 0xff,
-       TXQ_SLOT_MASK           = 0xfff,        /* slot number */
-
-       /* RX (completion) ring bits */
-       RXQ_GOOD                = (1U << 23),   /* Response good */
-       RXQ_SLOT_RESET          = (1U << 21),   /* Slot reset complete */
-       RXQ_CMD_RX              = (1U << 20),   /* target cmd received */
-       RXQ_ATTN                = (1U << 19),   /* attention */
-       RXQ_RSP                 = (1U << 18),   /* response frame xfer'd */
-       RXQ_ERR                 = (1U << 17),   /* err info rec xfer'd */
-       RXQ_DONE                = (1U << 16),   /* cmd complete */
-       RXQ_SLOT_MASK           = 0xfff,        /* slot number */
-
-       /* mvs_cmd_hdr bits */
-       MCH_PRD_LEN_SHIFT       = 16,           /* 16-bit PRD table len */
-       MCH_SSP_FR_TYPE_SHIFT   = 13,           /* SSP frame type */
-
-                                               /* SSP initiator only */
-       MCH_SSP_FR_CMD          = 0x0,          /* COMMAND frame */
-
-                                               /* SSP initiator or target */
-       MCH_SSP_FR_TASK         = 0x1,          /* TASK frame */
-
-                                               /* SSP target only */
-       MCH_SSP_FR_XFER_RDY     = 0x4,          /* XFER_RDY frame */
-       MCH_SSP_FR_RESP         = 0x5,          /* RESPONSE frame */
-       MCH_SSP_FR_READ         = 0x6,          /* Read DATA frame(s) */
-       MCH_SSP_FR_READ_RESP    = 0x7,          /* ditto, plus RESPONSE */
-
-       MCH_PASSTHRU            = (1U << 12),   /* pass-through (SSP) */
-       MCH_FBURST              = (1U << 11),   /* first burst (SSP) */
-       MCH_CHK_LEN             = (1U << 10),   /* chk xfer len (SSP) */
-       MCH_RETRY               = (1U << 9),    /* tport layer retry (SSP) */
-       MCH_PROTECTION          = (1U << 8),    /* protection info rec (SSP) */
-       MCH_RESET               = (1U << 7),    /* Reset (STP/SATA) */
-       MCH_FPDMA               = (1U << 6),    /* First party DMA (STP/SATA) */
-       MCH_ATAPI               = (1U << 5),    /* ATAPI (STP/SATA) */
-       MCH_BIST                = (1U << 4),    /* BIST activate (STP/SATA) */
-       MCH_PMP_MASK            = 0xf,          /* PMP from cmd FIS (STP/SATA)*/
-
-       CCTL_RST                = (1U << 5),    /* port logic reset */
-
-                                               /* 0(LSB first), 1(MSB first) */
-       CCTL_ENDIAN_DATA        = (1U << 3),    /* PRD data */
-       CCTL_ENDIAN_RSP         = (1U << 2),    /* response frame */
-       CCTL_ENDIAN_OPEN        = (1U << 1),    /* open address frame */
-       CCTL_ENDIAN_CMD         = (1U << 0),    /* command table */
-
-       /* MVS_Px_SER_CTLSTAT (per-phy control) */
-       PHY_SSP_RST             = (1U << 3),    /* reset SSP link layer */
-       PHY_BCAST_CHG           = (1U << 2),    /* broadcast(change) notif */
-       PHY_RST_HARD            = (1U << 1),    /* hard reset + phy reset */
-       PHY_RST                 = (1U << 0),    /* phy reset */
-       PHY_MIN_SPP_PHYS_LINK_RATE_MASK = (0xF << 8),
-       PHY_MAX_SPP_PHYS_LINK_RATE_MASK = (0xF << 12),
-       PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET = (16),
-       PHY_NEG_SPP_PHYS_LINK_RATE_MASK =
-                       (0xF << PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET),
-       PHY_READY_MASK          = (1U << 20),
-
-       /* MVS_Px_INT_STAT, MVS_Px_INT_MASK (per-phy events) */
-       PHYEV_DEC_ERR           = (1U << 24),   /* Phy Decoding Error */
-       PHYEV_UNASSOC_FIS       = (1U << 19),   /* unassociated FIS rx'd */
-       PHYEV_AN                = (1U << 18),   /* SATA async notification */
-       PHYEV_BIST_ACT          = (1U << 17),   /* BIST activate FIS */
-       PHYEV_SIG_FIS           = (1U << 16),   /* signature FIS */
-       PHYEV_POOF              = (1U << 12),   /* phy ready from 1 -> 0 */
-       PHYEV_IU_BIG            = (1U << 11),   /* IU too long err */
-       PHYEV_IU_SMALL          = (1U << 10),   /* IU too short err */
-       PHYEV_UNK_TAG           = (1U << 9),    /* unknown tag */
-       PHYEV_BROAD_CH          = (1U << 8),    /* broadcast(CHANGE) */
-       PHYEV_COMWAKE           = (1U << 7),    /* COMWAKE rx'd */
-       PHYEV_PORT_SEL          = (1U << 6),    /* port selector present */
-       PHYEV_HARD_RST          = (1U << 5),    /* hard reset rx'd */
-       PHYEV_ID_TMOUT          = (1U << 4),    /* identify timeout */
-       PHYEV_ID_FAIL           = (1U << 3),    /* identify failed */
-       PHYEV_ID_DONE           = (1U << 2),    /* identify done */
-       PHYEV_HARD_RST_DONE     = (1U << 1),    /* hard reset done */
-       PHYEV_RDY_CH            = (1U << 0),    /* phy ready changed state */
-
-       /* MVS_PCS */
-       PCS_EN_SATA_REG_SHIFT   = (16),         /* Enable SATA Register Set */
-       PCS_EN_PORT_XMT_SHIFT   = (12),         /* Enable Port Transmit */
-       PCS_EN_PORT_XMT_SHIFT2  = (8),          /* For 6480 */
-       PCS_SATA_RETRY          = (1U << 8),    /* retry ctl FIS on R_ERR */
-       PCS_RSP_RX_EN           = (1U << 7),    /* raw response rx */
-       PCS_SELF_CLEAR          = (1U << 5),    /* self-clearing int mode */
-       PCS_FIS_RX_EN           = (1U << 4),    /* FIS rx enable */
-       PCS_CMD_STOP_ERR        = (1U << 3),    /* cmd stop-on-err enable */
-       PCS_CMD_RST             = (1U << 1),    /* reset cmd issue */
-       PCS_CMD_EN              = (1U << 0),    /* enable cmd issue */
-
-       /* Port n Attached Device Info */
-       PORT_DEV_SSP_TRGT       = (1U << 19),
-       PORT_DEV_SMP_TRGT       = (1U << 18),
-       PORT_DEV_STP_TRGT       = (1U << 17),
-       PORT_DEV_SSP_INIT       = (1U << 11),
-       PORT_DEV_SMP_INIT       = (1U << 10),
-       PORT_DEV_STP_INIT       = (1U << 9),
-       PORT_PHY_ID_MASK        = (0xFFU << 24),
-       PORT_DEV_TRGT_MASK      = (0x7U << 17),
-       PORT_DEV_INIT_MASK      = (0x7U << 9),
-       PORT_DEV_TYPE_MASK      = (0x7U << 0),
-
-       /* Port n PHY Status */
-       PHY_RDY                 = (1U << 2),
-       PHY_DW_SYNC             = (1U << 1),
-       PHY_OOB_DTCTD           = (1U << 0),
-
-       /* VSR */
-       /* PHYMODE 6 (CDB) */
-       PHY_MODE6_LATECLK       = (1U << 29),   /* Lock Clock */
-       PHY_MODE6_DTL_SPEED     = (1U << 27),   /* Digital Loop Speed */
-       PHY_MODE6_FC_ORDER      = (1U << 26),   /* Fibre Channel Mode Order*/
-       PHY_MODE6_MUCNT_EN      = (1U << 24),   /* u Count Enable */
-       PHY_MODE6_SEL_MUCNT_LEN = (1U << 22),   /* Training Length Select */
-       PHY_MODE6_SELMUPI       = (1U << 20),   /* Phase Multi Select (init) */
-       PHY_MODE6_SELMUPF       = (1U << 18),   /* Phase Multi Select (final) */
-       PHY_MODE6_SELMUFF       = (1U << 16),   /* Freq Loop Multi Sel(final) */
-       PHY_MODE6_SELMUFI       = (1U << 14),   /* Freq Loop Multi Sel(init) */
-       PHY_MODE6_FREEZE_LOOP   = (1U << 12),   /* Freeze Rx CDR Loop */
-       PHY_MODE6_INT_RXFOFFS   = (1U << 3),    /* Rx CDR Freq Loop Enable */
-       PHY_MODE6_FRC_RXFOFFS   = (1U << 2),    /* Initial Rx CDR Offset */
-       PHY_MODE6_STAU_0D8      = (1U << 1),    /* Rx CDR Freq Loop Saturate */
-       PHY_MODE6_RXSAT_DIS     = (1U << 0),    /* Saturate Ctl */
-};
-
-enum mvs_info_flags {
-       MVF_MSI                 = (1U << 0),    /* MSI is enabled */
-       MVF_PHY_PWR_FIX         = (1U << 1),    /* bug workaround */
-};
-
-enum sas_cmd_port_registers {
-       CMD_CMRST_OOB_DET       = 0x100, /* COMRESET OOB detect register */
-       CMD_CMWK_OOB_DET        = 0x104, /* COMWAKE OOB detect register */
-       CMD_CMSAS_OOB_DET       = 0x108, /* COMSAS OOB detect register */
-       CMD_BRST_OOB_DET        = 0x10c, /* burst OOB detect register */
-       CMD_OOB_SPACE           = 0x110, /* OOB space control register */
-       CMD_OOB_BURST           = 0x114, /* OOB burst control register */
-       CMD_PHY_TIMER           = 0x118, /* PHY timer control register */
-       CMD_PHY_CONFIG0         = 0x11c, /* PHY config register 0 */
-       CMD_PHY_CONFIG1         = 0x120, /* PHY config register 1 */
-       CMD_SAS_CTL0            = 0x124, /* SAS control register 0 */
-       CMD_SAS_CTL1            = 0x128, /* SAS control register 1 */
-       CMD_SAS_CTL2            = 0x12c, /* SAS control register 2 */
-       CMD_SAS_CTL3            = 0x130, /* SAS control register 3 */
-       CMD_ID_TEST             = 0x134, /* ID test register */
-       CMD_PL_TIMER            = 0x138, /* PL timer register */
-       CMD_WD_TIMER            = 0x13c, /* WD timer register */
-       CMD_PORT_SEL_COUNT      = 0x140, /* port selector count register */
-       CMD_APP_MEM_CTL         = 0x144, /* Application Memory Control */
-       CMD_XOR_MEM_CTL         = 0x148, /* XOR Block Memory Control */
-       CMD_DMA_MEM_CTL         = 0x14c, /* DMA Block Memory Control */
-       CMD_PORT_MEM_CTL0       = 0x150, /* Port Memory Control 0 */
-       CMD_PORT_MEM_CTL1       = 0x154, /* Port Memory Control 1 */
-       CMD_SATA_PORT_MEM_CTL0  = 0x158, /* SATA Port Memory Control 0 */
-       CMD_SATA_PORT_MEM_CTL1  = 0x15c, /* SATA Port Memory Control 1 */
-       CMD_XOR_MEM_BIST_CTL    = 0x160, /* XOR Memory BIST Control */
-       CMD_XOR_MEM_BIST_STAT   = 0x164, /* XOR Memroy BIST Status */
-       CMD_DMA_MEM_BIST_CTL    = 0x168, /* DMA Memory BIST Control */
-       CMD_DMA_MEM_BIST_STAT   = 0x16c, /* DMA Memory BIST Status */
-       CMD_PORT_MEM_BIST_CTL   = 0x170, /* Port Memory BIST Control */
-       CMD_PORT_MEM_BIST_STAT0 = 0x174, /* Port Memory BIST Status 0 */
-       CMD_PORT_MEM_BIST_STAT1 = 0x178, /* Port Memory BIST Status 1 */
-       CMD_STP_MEM_BIST_CTL    = 0x17c, /* STP Memory BIST Control */
-       CMD_STP_MEM_BIST_STAT0  = 0x180, /* STP Memory BIST Status 0 */
-       CMD_STP_MEM_BIST_STAT1  = 0x184, /* STP Memory BIST Status 1 */
-       CMD_RESET_COUNT         = 0x188, /* Reset Count */
-       CMD_MONTR_DATA_SEL      = 0x18C, /* Monitor Data/Select */
-       CMD_PLL_PHY_CONFIG      = 0x190, /* PLL/PHY Configuration */
-       CMD_PHY_CTL             = 0x194, /* PHY Control and Status */
-       CMD_PHY_TEST_COUNT0     = 0x198, /* Phy Test Count 0 */
-       CMD_PHY_TEST_COUNT1     = 0x19C, /* Phy Test Count 1 */
-       CMD_PHY_TEST_COUNT2     = 0x1A0, /* Phy Test Count 2 */
-       CMD_APP_ERR_CONFIG      = 0x1A4, /* Application Error Configuration */
-       CMD_PND_FIFO_CTL0       = 0x1A8, /* Pending FIFO Control 0 */
-       CMD_HOST_CTL            = 0x1AC, /* Host Control Status */
-       CMD_HOST_WR_DATA        = 0x1B0, /* Host Write Data */
-       CMD_HOST_RD_DATA        = 0x1B4, /* Host Read Data */
-       CMD_PHY_MODE_21         = 0x1B8, /* Phy Mode 21 */
-       CMD_SL_MODE0            = 0x1BC, /* SL Mode 0 */
-       CMD_SL_MODE1            = 0x1C0, /* SL Mode 1 */
-       CMD_PND_FIFO_CTL1       = 0x1C4, /* Pending FIFO Control 1 */
-};
-
-/* SAS/SATA configuration port registers, aka phy registers */
-enum sas_sata_config_port_regs {
-       PHYR_IDENTIFY           = 0x00, /* info for IDENTIFY frame */
-       PHYR_ADDR_LO            = 0x04, /* my SAS address (low) */
-       PHYR_ADDR_HI            = 0x08, /* my SAS address (high) */
-       PHYR_ATT_DEV_INFO       = 0x0C, /* attached device info */
-       PHYR_ATT_ADDR_LO        = 0x10, /* attached dev SAS addr (low) */
-       PHYR_ATT_ADDR_HI        = 0x14, /* attached dev SAS addr (high) */
-       PHYR_SATA_CTL           = 0x18, /* SATA control */
-       PHYR_PHY_STAT           = 0x1C, /* PHY status */
-       PHYR_SATA_SIG0          = 0x20, /*port SATA signature FIS(Byte 0-3) */
-       PHYR_SATA_SIG1          = 0x24, /*port SATA signature FIS(Byte 4-7) */
-       PHYR_SATA_SIG2          = 0x28, /*port SATA signature FIS(Byte 8-11) */
-       PHYR_SATA_SIG3          = 0x2c, /*port SATA signature FIS(Byte 12-15) */
-       PHYR_R_ERR_COUNT        = 0x30, /* port R_ERR count register */
-       PHYR_CRC_ERR_COUNT      = 0x34, /* port CRC error count register */
-       PHYR_WIDE_PORT          = 0x38, /* wide port participating */
-       PHYR_CURRENT0           = 0x80, /* current connection info 0 */
-       PHYR_CURRENT1           = 0x84, /* current connection info 1 */
-       PHYR_CURRENT2           = 0x88, /* current connection info 2 */
-};
-
-/*  SAS/SATA Vendor Specific Port Registers */
-enum sas_sata_vsp_regs {
-       VSR_PHY_STAT            = 0x00, /* Phy Status */
-       VSR_PHY_MODE1           = 0x01, /* phy tx */
-       VSR_PHY_MODE2           = 0x02, /* tx scc */
-       VSR_PHY_MODE3           = 0x03, /* pll */
-       VSR_PHY_MODE4           = 0x04, /* VCO */
-       VSR_PHY_MODE5           = 0x05, /* Rx */
-       VSR_PHY_MODE6           = 0x06, /* CDR */
-       VSR_PHY_MODE7           = 0x07, /* Impedance */
-       VSR_PHY_MODE8           = 0x08, /* Voltage */
-       VSR_PHY_MODE9           = 0x09, /* Test */
-       VSR_PHY_MODE10          = 0x0A, /* Power */
-       VSR_PHY_MODE11          = 0x0B, /* Phy Mode */
-       VSR_PHY_VS0             = 0x0C, /* Vednor Specific 0 */
-       VSR_PHY_VS1             = 0x0D, /* Vednor Specific 1 */
-};
-
-enum pci_cfg_registers {
-       PCR_PHY_CTL     = 0x40,
-       PCR_PHY_CTL2    = 0x90,
-       PCR_DEV_CTRL    = 0xE8,
-};
-
-enum pci_cfg_register_bits {
-       PCTL_PWR_ON     = (0xFU << 24),
-       PCTL_OFF        = (0xFU << 12),
-       PRD_REQ_SIZE    = (0x4000),
-       PRD_REQ_MASK    = (0x00007000),
-};
-
-enum nvram_layout_offsets {
-       NVR_SIG         = 0x00,         /* 0xAA, 0x55 */
-       NVR_SAS_ADDR    = 0x02,         /* 8-byte SAS address */
-};
-
-enum chip_flavors {
-       chip_6320,
-       chip_6440,
-       chip_6480,
-};
-
-enum port_type {
-       PORT_TYPE_SAS   =  (1L << 1),
-       PORT_TYPE_SATA  =  (1L << 0),
-};
-
-/* Command Table Format */
-enum ct_format {
-       /* SSP */
-       SSP_F_H         =  0x00,
-       SSP_F_IU        =  0x18,
-       SSP_F_MAX       =  0x4D,
-       /* STP */
-       STP_CMD_FIS     =  0x00,
-       STP_ATAPI_CMD   =  0x40,
-       STP_F_MAX       =  0x10,
-       /* SMP */
-       SMP_F_T         =  0x00,
-       SMP_F_DEP       =  0x01,
-       SMP_F_MAX       =  0x101,
-};
-
-enum status_buffer {
-       SB_EIR_OFF      =  0x00,        /* Error Information Record */
-       SB_RFB_OFF      =  0x08,        /* Response Frame Buffer */
-       SB_RFB_MAX      =  0x400,       /* RFB size*/
-};
-
-enum error_info_rec {
-       CMD_ISS_STPD    = (1U << 31),   /* Cmd Issue Stopped */
-       CMD_PI_ERR      = (1U << 30),   /* Protection info error.  see flags2 */
-       RSP_OVER        = (1U << 29),   /* rsp buffer overflow */
-       RETRY_LIM       = (1U << 28),   /* FIS/frame retry limit exceeded */
-       UNK_FIS         = (1U << 27),   /* unknown FIS */
-       DMA_TERM        = (1U << 26),   /* DMA terminate primitive rx'd */
-       SYNC_ERR        = (1U << 25),   /* SYNC rx'd during frame xmit */
-       TFILE_ERR       = (1U << 24),   /* SATA taskfile Error bit set */
-       R_ERR           = (1U << 23),   /* SATA returned R_ERR prim */
-       RD_OFS          = (1U << 20),   /* Read DATA frame invalid offset */
-       XFER_RDY_OFS    = (1U << 19),   /* XFER_RDY offset error */
-       UNEXP_XFER_RDY  = (1U << 18),   /* unexpected XFER_RDY error */
-       DATA_OVER_UNDER = (1U << 16),   /* data overflow/underflow */
-       INTERLOCK       = (1U << 15),   /* interlock error */
-       NAK             = (1U << 14),   /* NAK rx'd */
-       ACK_NAK_TO      = (1U << 13),   /* ACK/NAK timeout */
-       CXN_CLOSED      = (1U << 12),   /* cxn closed w/out ack/nak */
-       OPEN_TO         = (1U << 11),   /* I_T nexus lost, open cxn timeout */
-       PATH_BLOCKED    = (1U << 10),   /* I_T nexus lost, pathway blocked */
-       NO_DEST         = (1U << 9),    /* I_T nexus lost, no destination */
-       STP_RES_BSY     = (1U << 8),    /* STP resources busy */
-       BREAK           = (1U << 7),    /* break received */
-       BAD_DEST        = (1U << 6),    /* bad destination */
-       BAD_PROTO       = (1U << 5),    /* protocol not supported */
-       BAD_RATE        = (1U << 4),    /* cxn rate not supported */
-       WRONG_DEST      = (1U << 3),    /* wrong destination error */
-       CREDIT_TO       = (1U << 2),    /* credit timeout */
-       WDOG_TO         = (1U << 1),    /* watchdog timeout */
-       BUF_PAR         = (1U << 0),    /* buffer parity error */
-};
-
-enum error_info_rec_2 {
-       SLOT_BSY_ERR    = (1U << 31),   /* Slot Busy Error */
-       GRD_CHK_ERR     = (1U << 14),   /* Guard Check Error */
-       APP_CHK_ERR     = (1U << 13),   /* Application Check error */
-       REF_CHK_ERR     = (1U << 12),   /* Reference Check Error */
-       USR_BLK_NM      = (1U << 0),    /* User Block Number */
-};
-
-struct mvs_chip_info {
-       u32             n_phy;
-       u32             srs_sz;
-       u32             slot_width;
-};
-
-struct mvs_err_info {
-       __le32                  flags;
-       __le32                  flags2;
-};
-
-struct mvs_prd {
-       __le64                  addr;           /* 64-bit buffer address */
-       __le32                  reserved;
-       __le32                  len;            /* 16-bit length */
-};
-
-struct mvs_cmd_hdr {
-       __le32                  flags;          /* PRD tbl len; SAS, SATA ctl */
-       __le32                  lens;           /* cmd, max resp frame len */
-       __le32                  tags;           /* targ port xfer tag; tag */
-       __le32                  data_len;       /* data xfer len */
-       __le64                  cmd_tbl;        /* command table address */
-       __le64                  open_frame;     /* open addr frame address */
-       __le64                  status_buf;     /* status buffer address */
-       __le64                  prd_tbl;        /* PRD tbl address */
-       __le32                  reserved[4];
-};
-
-struct mvs_port {
-       struct asd_sas_port     sas_port;
-       u8                      port_attached;
-       u8                      taskfileset;
-       u8                      wide_port_phymap;
-       struct list_head        list;
-};
-
-struct mvs_phy {
-       struct mvs_port         *port;
-       struct asd_sas_phy      sas_phy;
-       struct sas_identify     identify;
-       struct scsi_device      *sdev;
-       u64             dev_sas_addr;
-       u64             att_dev_sas_addr;
-       u32             att_dev_info;
-       u32             dev_info;
-       u32             phy_type;
-       u32             phy_status;
-       u32             irq_status;
-       u32             frame_rcvd_size;
-       u8              frame_rcvd[32];
-       u8              phy_attached;
-       enum sas_linkrate       minimum_linkrate;
-       enum sas_linkrate       maximum_linkrate;
-};
-
-struct mvs_slot_info {
-       struct list_head        list;
-       struct sas_task         *task;
-       u32                     n_elem;
-       u32                     tx;
-
-       /* DMA buffer for storing cmd tbl, open addr frame, status buffer,
-        * and PRD table
-        */
-       void                    *buf;
-       dma_addr_t              buf_dma;
-#if _MV_DUMP
-       u32                     cmd_size;
-#endif
-
-       void                    *response;
-       struct mvs_port         *port;
-};
-
-struct mvs_info {
-       unsigned long           flags;
-
-       spinlock_t              lock;           /* host-wide lock */
-       struct pci_dev          *pdev;          /* our device */
-       void __iomem            *regs;          /* enhanced mode registers */
-       void __iomem            *peri_regs;     /* peripheral registers */
-
-       u8                      sas_addr[SAS_ADDR_SIZE];
-       struct sas_ha_struct    sas;            /* SCSI/SAS glue */
-       struct Scsi_Host        *shost;
-
-       __le32                  *tx;            /* TX (delivery) DMA ring */
-       dma_addr_t              tx_dma;
-       u32                     tx_prod;        /* cached next-producer idx */
-
-       __le32                  *rx;            /* RX (completion) DMA ring */
-       dma_addr_t              rx_dma;
-       u32                     rx_cons;        /* RX consumer idx */
-
-       __le32                  *rx_fis;        /* RX'd FIS area */
-       dma_addr_t              rx_fis_dma;
-
-       struct mvs_cmd_hdr      *slot;  /* DMA command header slots */
-       dma_addr_t              slot_dma;
-
-       const struct mvs_chip_info *chip;
-
-       u8                      tags[MVS_SLOTS];
-       struct mvs_slot_info    slot_info[MVS_SLOTS];
-                               /* further per-slot information */
-       struct mvs_phy          phy[MVS_MAX_PHYS];
-       struct mvs_port         port[MVS_MAX_PHYS];
-#ifdef MVS_USE_TASKLET
-       struct tasklet_struct   tasklet;
-#endif
-};
-
-static int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
-                          void *funcdata);
-static u32 mvs_read_phy_ctl(struct mvs_info *mvi, u32 port);
-static void mvs_write_phy_ctl(struct mvs_info *mvi, u32 port, u32 val);
-static u32 mvs_read_port_irq_stat(struct mvs_info *mvi, u32 port);
-static void mvs_write_port_irq_stat(struct mvs_info *mvi, u32 port, u32 val);
-static void mvs_write_port_irq_mask(struct mvs_info *mvi, u32 port, u32 val);
-static u32 mvs_read_port_irq_mask(struct mvs_info *mvi, u32 port);
-
-static u32 mvs_is_phy_ready(struct mvs_info *mvi, int i);
-static void mvs_detect_porttype(struct mvs_info *mvi, int i);
-static void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st);
-static void mvs_release_task(struct mvs_info *mvi, int phy_no);
-
-static int mvs_scan_finished(struct Scsi_Host *, unsigned long);
-static void mvs_scan_start(struct Scsi_Host *);
-static int mvs_slave_configure(struct scsi_device *sdev);
-
-static struct scsi_transport_template *mvs_stt;
-
-static const struct mvs_chip_info mvs_chips[] = {
-       [chip_6320] =           { 2, 16, 9  },
-       [chip_6440] =           { 4, 16, 9  },
-       [chip_6480] =           { 8, 32, 10 },
-};
-
-static struct scsi_host_template mvs_sht = {
-       .module                 = THIS_MODULE,
-       .name                   = DRV_NAME,
-       .queuecommand           = sas_queuecommand,
-       .target_alloc           = sas_target_alloc,
-       .slave_configure        = mvs_slave_configure,
-       .slave_destroy          = sas_slave_destroy,
-       .scan_finished          = mvs_scan_finished,
-       .scan_start             = mvs_scan_start,
-       .change_queue_depth     = sas_change_queue_depth,
-       .change_queue_type      = sas_change_queue_type,
-       .bios_param             = sas_bios_param,
-       .can_queue              = 1,
-       .cmd_per_lun            = 1,
-       .this_id                = -1,
-       .sg_tablesize           = SG_ALL,
-       .max_sectors            = SCSI_DEFAULT_MAX_SECTORS,
-       .use_clustering         = ENABLE_CLUSTERING,
-       .eh_device_reset_handler        = sas_eh_device_reset_handler,
-       .eh_bus_reset_handler   = sas_eh_bus_reset_handler,
-       .slave_alloc            = sas_slave_alloc,
-       .target_destroy         = sas_target_destroy,
-       .ioctl                  = sas_ioctl,
-};
-
-static void mvs_hexdump(u32 size, u8 *data, u32 baseaddr)
-{
-       u32 i;
-       u32 run;
-       u32 offset;
-
-       offset = 0;
-       while (size) {
-               printk("%08X : ", baseaddr + offset);
-               if (size >= 16)
-                       run = 16;
-               else
-                       run = size;
-               size -= run;
-               for (i = 0; i < 16; i++) {
-                       if (i < run)
-                               printk("%02X ", (u32)data[i]);
-                       else
-                               printk("   ");
-               }
-               printk(": ");
-               for (i = 0; i < run; i++)
-                       printk("%c", isalnum(data[i]) ? data[i] : '.');
-               printk("\n");
-               data = &data[16];
-               offset += run;
-       }
-       printk("\n");
-}
-
-#if _MV_DUMP
-static void mvs_hba_sb_dump(struct mvs_info *mvi, u32 tag,
-                                  enum sas_protocol proto)
-{
-       u32 offset;
-       struct pci_dev *pdev = mvi->pdev;
-       struct mvs_slot_info *slot = &mvi->slot_info[tag];
-
-       offset = slot->cmd_size + MVS_OAF_SZ +
-           sizeof(struct mvs_prd) * slot->n_elem;
-       dev_printk(KERN_DEBUG, &pdev->dev, "+---->Status buffer[%d] :\n",
-                       tag);
-       mvs_hexdump(32, (u8 *) slot->response,
-                   (u32) slot->buf_dma + offset);
-}
-#endif
-
-static void mvs_hba_memory_dump(struct mvs_info *mvi, u32 tag,
-                               enum sas_protocol proto)
-{
-#if _MV_DUMP
-       u32 sz, w_ptr;
-       u64 addr;
-       void __iomem *regs = mvi->regs;
-       struct pci_dev *pdev = mvi->pdev;
-       struct mvs_slot_info *slot = &mvi->slot_info[tag];
-
-       /*Delivery Queue */
-       sz = mr32(TX_CFG) & TX_RING_SZ_MASK;
-       w_ptr = slot->tx;
-       addr = mr32(TX_HI) << 16 << 16 | mr32(TX_LO);
-       dev_printk(KERN_DEBUG, &pdev->dev,
-               "Delivery Queue Size=%04d , WRT_PTR=%04X\n", sz, w_ptr);
-       dev_printk(KERN_DEBUG, &pdev->dev,
-               "Delivery Queue Base Address=0x%llX (PA)"
-               "(tx_dma=0x%llX), Entry=%04d\n",
-               addr, mvi->tx_dma, w_ptr);
-       mvs_hexdump(sizeof(u32), (u8 *)(&mvi->tx[mvi->tx_prod]),
-                       (u32) mvi->tx_dma + sizeof(u32) * w_ptr);
-       /*Command List */
-       addr = mvi->slot_dma;
-       dev_printk(KERN_DEBUG, &pdev->dev,
-               "Command List Base Address=0x%llX (PA)"
-               "(slot_dma=0x%llX), Header=%03d\n",
-               addr, slot->buf_dma, tag);
-       dev_printk(KERN_DEBUG, &pdev->dev, "Command Header[%03d]:\n", tag);
-       /*mvs_cmd_hdr */
-       mvs_hexdump(sizeof(struct mvs_cmd_hdr), (u8 *)(&mvi->slot[tag]),
-               (u32) mvi->slot_dma + tag * sizeof(struct mvs_cmd_hdr));
-       /*1.command table area */
-       dev_printk(KERN_DEBUG, &pdev->dev, "+---->Command Table :\n");
-       mvs_hexdump(slot->cmd_size, (u8 *) slot->buf, (u32) slot->buf_dma);
-       /*2.open address frame area */
-       dev_printk(KERN_DEBUG, &pdev->dev, "+---->Open Address Frame :\n");
-       mvs_hexdump(MVS_OAF_SZ, (u8 *) slot->buf + slot->cmd_size,
-                               (u32) slot->buf_dma + slot->cmd_size);
-       /*3.status buffer */
-       mvs_hba_sb_dump(mvi, tag, proto);
-       /*4.PRD table */
-       dev_printk(KERN_DEBUG, &pdev->dev, "+---->PRD table :\n");
-       mvs_hexdump(sizeof(struct mvs_prd) * slot->n_elem,
-               (u8 *) slot->buf + slot->cmd_size + MVS_OAF_SZ,
-               (u32) slot->buf_dma + slot->cmd_size + MVS_OAF_SZ);
-#endif
-}
-
-static void mvs_hba_cq_dump(struct mvs_info *mvi)
-{
-#if (_MV_DUMP > 2)
-       u64 addr;
-       void __iomem *regs = mvi->regs;
-       struct pci_dev *pdev = mvi->pdev;
-       u32 entry = mvi->rx_cons + 1;
-       u32 rx_desc = le32_to_cpu(mvi->rx[entry]);
-
-       /*Completion Queue */
-       addr = mr32(RX_HI) << 16 << 16 | mr32(RX_LO);
-       dev_printk(KERN_DEBUG, &pdev->dev, "Completion Task = 0x%p\n",
-                  mvi->slot_info[rx_desc & RXQ_SLOT_MASK].task);
-       dev_printk(KERN_DEBUG, &pdev->dev,
-               "Completion List Base Address=0x%llX (PA), "
-               "CQ_Entry=%04d, CQ_WP=0x%08X\n",
-               addr, entry - 1, mvi->rx[0]);
-       mvs_hexdump(sizeof(u32), (u8 *)(&rx_desc),
-                   mvi->rx_dma + sizeof(u32) * entry);
-#endif
-}
-
-static void mvs_hba_interrupt_enable(struct mvs_info *mvi)
-{
-       void __iomem *regs = mvi->regs;
-       u32 tmp;
-
-       tmp = mr32(GBL_CTL);
-
-       mw32(GBL_CTL, tmp | INT_EN);
-}
-
-static void mvs_hba_interrupt_disable(struct mvs_info *mvi)
-{
-       void __iomem *regs = mvi->regs;
-       u32 tmp;
-
-       tmp = mr32(GBL_CTL);
-
-       mw32(GBL_CTL, tmp & ~INT_EN);
-}
-
-static int mvs_int_rx(struct mvs_info *mvi, bool self_clear);
-
-/* move to PCI layer or libata core? */
-static int pci_go_64(struct pci_dev *pdev)
-{
-       int rc;
-
-       if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
-               rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
-               if (rc) {
-                       rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
-                       if (rc) {
-                               dev_printk(KERN_ERR, &pdev->dev,
-                                          "64-bit DMA enable failed\n");
-                               return rc;
-                       }
-               }
-       } else {
-               rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
-               if (rc) {
-                       dev_printk(KERN_ERR, &pdev->dev,
-                                  "32-bit DMA enable failed\n");
-                       return rc;
-               }
-               rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
-               if (rc) {
-                       dev_printk(KERN_ERR, &pdev->dev,
-                                  "32-bit consistent DMA enable failed\n");
-                       return rc;
-               }
-       }
-
-       return rc;
-}
-
-static int mvs_find_tag(struct mvs_info *mvi, struct sas_task *task, u32 *tag)
-{
-       if (task->lldd_task) {
-               struct mvs_slot_info *slot;
-               slot = (struct mvs_slot_info *) task->lldd_task;
-               *tag = slot - mvi->slot_info;
-               return 1;
-       }
-       return 0;
-}
-
-static void mvs_tag_clear(struct mvs_info *mvi, u32 tag)
-{
-       void *bitmap = (void *) &mvi->tags;
-       clear_bit(tag, bitmap);
-}
-
-static void mvs_tag_free(struct mvs_info *mvi, u32 tag)
-{
-       mvs_tag_clear(mvi, tag);
-}
-
-static void mvs_tag_set(struct mvs_info *mvi, unsigned int tag)
-{
-       void *bitmap = (void *) &mvi->tags;
-       set_bit(tag, bitmap);
-}
-
-static int mvs_tag_alloc(struct mvs_info *mvi, u32 *tag_out)
-{
-       unsigned int index, tag;
-       void *bitmap = (void *) &mvi->tags;
-
-       index = find_first_zero_bit(bitmap, MVS_SLOTS);
-       tag = index;
-       if (tag >= MVS_SLOTS)
-               return -SAS_QUEUE_FULL;
-       mvs_tag_set(mvi, tag);
-       *tag_out = tag;
-       return 0;
-}
-
-static void mvs_tag_init(struct mvs_info *mvi)
-{
-       int i;
-       for (i = 0; i < MVS_SLOTS; ++i)
-               mvs_tag_clear(mvi, i);
-}
-
-#ifndef MVS_DISABLE_NVRAM
-static int mvs_eep_read(void __iomem *regs, u32 addr, u32 *data)
-{
-       int timeout = 1000;
-
-       if (addr & ~SPI_ADDR_MASK)
-               return -EINVAL;
-
-       writel(addr, regs + SPI_CMD);
-       writel(TWSI_RD, regs + SPI_CTL);
-
-       while (timeout-- > 0) {
-               if (readl(regs + SPI_CTL) & TWSI_RDY) {
-                       *data = readl(regs + SPI_DATA);
-                       return 0;
-               }
-
-               udelay(10);
-       }
-
-       return -EBUSY;
-}
-
-static int mvs_eep_read_buf(void __iomem *regs, u32 addr,
-                           void *buf, u32 buflen)
-{
-       u32 addr_end, tmp_addr, i, j;
-       u32 tmp = 0;
-       int rc;
-       u8 *tmp8, *buf8 = buf;
-
-       addr_end = addr + buflen;
-       tmp_addr = ALIGN(addr, 4);
-       if (addr > 0xff)
-               return -EINVAL;
-
-       j = addr & 0x3;
-       if (j) {
-               rc = mvs_eep_read(regs, tmp_addr, &tmp);
-               if (rc)
-                       return rc;
-
-               tmp8 = (u8 *)&tmp;
-               for (i = j; i < 4; i++)
-                       *buf8++ = tmp8[i];
-
-               tmp_addr += 4;
-       }
-
-       for (j = ALIGN(addr_end, 4); tmp_addr < j; tmp_addr += 4) {
-               rc = mvs_eep_read(regs, tmp_addr, &tmp);
-               if (rc)
-                       return rc;
-
-               memcpy(buf8, &tmp, 4);
-               buf8 += 4;
-       }
-
-       if (tmp_addr < addr_end) {
-               rc = mvs_eep_read(regs, tmp_addr, &tmp);
-               if (rc)
-                       return rc;
-
-               tmp8 = (u8 *)&tmp;
-               j = addr_end - tmp_addr;
-               for (i = 0; i < j; i++)
-                       *buf8++ = tmp8[i];
-
-               tmp_addr += 4;
-       }
-
-       return 0;
-}
-#endif
-
-static int mvs_nvram_read(struct mvs_info *mvi, u32 addr,
-                         void *buf, u32 buflen)
-{
-#ifndef MVS_DISABLE_NVRAM
-       void __iomem *regs = mvi->regs;
-       int rc, i;
-       u32 sum;
-       u8 hdr[2], *tmp;
-       const char *msg;
-
-       rc = mvs_eep_read_buf(regs, addr, &hdr, 2);
-       if (rc) {
-               msg = "nvram hdr read failed";
-               goto err_out;
-       }
-       rc = mvs_eep_read_buf(regs, addr + 2, buf, buflen);
-       if (rc) {
-               msg = "nvram read failed";
-               goto err_out;
-       }
-
-       if (hdr[0] != 0x5A) {
-               /* entry id */
-               msg = "invalid nvram entry id";
-               rc = -ENOENT;
-               goto err_out;
-       }
-
-       tmp = buf;
-       sum = ((u32)hdr[0]) + ((u32)hdr[1]);
-       for (i = 0; i < buflen; i++)
-               sum += ((u32)tmp[i]);
-
-       if (sum) {
-               msg = "nvram checksum failure";
-               rc = -EILSEQ;
-               goto err_out;
-       }
-
-       return 0;
-
-err_out:
-       dev_printk(KERN_ERR, &mvi->pdev->dev, "%s", msg);
-       return rc;
-#else
-       /* FIXME , For SAS target mode */
-       memcpy(buf, "\x50\x05\x04\x30\x11\xab\x00\x00", 8);
-       return 0;
-#endif
-}
-
-static void mvs_bytes_dmaed(struct mvs_info *mvi, int i)
-{
-       struct mvs_phy *phy = &mvi->phy[i];
-       struct asd_sas_phy *sas_phy = mvi->sas.sas_phy[i];
-
-       if (!phy->phy_attached)
-               return;
-
-       if (sas_phy->phy) {
-               struct sas_phy *sphy = sas_phy->phy;
-
-               sphy->negotiated_linkrate = sas_phy->linkrate;
-               sphy->minimum_linkrate = phy->minimum_linkrate;
-               sphy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
-               sphy->maximum_linkrate = phy->maximum_linkrate;
-               sphy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS;
-       }
-
-       if (phy->phy_type & PORT_TYPE_SAS) {
-               struct sas_identify_frame *id;
-
-               id = (struct sas_identify_frame *)phy->frame_rcvd;
-               id->dev_type = phy->identify.device_type;
-               id->initiator_bits = SAS_PROTOCOL_ALL;
-               id->target_bits = phy->identify.target_port_protocols;
-       } else if (phy->phy_type & PORT_TYPE_SATA) {
-               /* TODO */
-       }
-       mvi->sas.sas_phy[i]->frame_rcvd_size = phy->frame_rcvd_size;
-       mvi->sas.notify_port_event(mvi->sas.sas_phy[i],
-                                  PORTE_BYTES_DMAED);
-}
-
-static int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time)
-{
-       /* give the phy enabling interrupt event time to come in (1s
-        * is empirically about all it takes) */
-       if (time < HZ)
-               return 0;
-       /* Wait for discovery to finish */
-       scsi_flush_work(shost);
-       return 1;
-}
-
-static void mvs_scan_start(struct Scsi_Host *shost)
-{
-       int i;
-       struct mvs_info *mvi = SHOST_TO_SAS_HA(shost)->lldd_ha;
-
-       for (i = 0; i < mvi->chip->n_phy; ++i) {
-               mvs_bytes_dmaed(mvi, i);
-       }
-}
-
-static int mvs_slave_configure(struct scsi_device *sdev)
-{
-       struct domain_device *dev = sdev_to_domain_dev(sdev);
-       int ret = sas_slave_configure(sdev);
-
-       if (ret)
-               return ret;
-
-       if (dev_is_sata(dev)) {
-               /* struct ata_port *ap = dev->sata_dev.ap; */
-               /* struct ata_device *adev = ap->link.device; */
-
-               /* clamp at no NCQ for the time being */
-               /* adev->flags |= ATA_DFLAG_NCQ_OFF; */
-               scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, 1);
-       }
-       return 0;
-}
-
-static void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
-{
-       struct pci_dev *pdev = mvi->pdev;
-       struct sas_ha_struct *sas_ha = &mvi->sas;
-       struct mvs_phy *phy = &mvi->phy[phy_no];
-       struct asd_sas_phy *sas_phy = &phy->sas_phy;
-
-       phy->irq_status = mvs_read_port_irq_stat(mvi, phy_no);
-       /*
-       * events is port event now ,
-       * we need check the interrupt status which belongs to per port.
-       */
-       dev_printk(KERN_DEBUG, &pdev->dev,
-               "Port %d Event = %X\n",
-               phy_no, phy->irq_status);
-
-       if (phy->irq_status & (PHYEV_POOF | PHYEV_DEC_ERR)) {
-               mvs_release_task(mvi, phy_no);
-               if (!mvs_is_phy_ready(mvi, phy_no)) {
-                       sas_phy_disconnected(sas_phy);
-                       sas_ha->notify_phy_event(sas_phy, PHYE_LOSS_OF_SIGNAL);
-                       dev_printk(KERN_INFO, &pdev->dev,
-                               "Port %d Unplug Notice\n", phy_no);
-
-               } else
-                       mvs_phy_control(sas_phy, PHY_FUNC_LINK_RESET, NULL);
-       }
-       if (!(phy->irq_status & PHYEV_DEC_ERR)) {
-               if (phy->irq_status & PHYEV_COMWAKE) {
-                       u32 tmp = mvs_read_port_irq_mask(mvi, phy_no);
-                       mvs_write_port_irq_mask(mvi, phy_no,
-                                               tmp | PHYEV_SIG_FIS);
-               }
-               if (phy->irq_status & (PHYEV_SIG_FIS | PHYEV_ID_DONE)) {
-                       phy->phy_status = mvs_is_phy_ready(mvi, phy_no);
-                       if (phy->phy_status) {
-                               mvs_detect_porttype(mvi, phy_no);
-
-                               if (phy->phy_type & PORT_TYPE_SATA) {
-                                       u32 tmp = mvs_read_port_irq_mask(mvi,
-                                                               phy_no);
-                                       tmp &= ~PHYEV_SIG_FIS;
-                                       mvs_write_port_irq_mask(mvi,
-                                                               phy_no, tmp);
-                               }
-
-                               mvs_update_phyinfo(mvi, phy_no, 0);
-                               sas_ha->notify_phy_event(sas_phy,
-                                                       PHYE_OOB_DONE);
-                               mvs_bytes_dmaed(mvi, phy_no);
-                       } else {
-                               dev_printk(KERN_DEBUG, &pdev->dev,
-                                       "plugin interrupt but phy is gone\n");
-                               mvs_phy_control(sas_phy, PHY_FUNC_LINK_RESET,
-                                                       NULL);
-                       }
-               } else if (phy->irq_status & PHYEV_BROAD_CH) {
-                       mvs_release_task(mvi, phy_no);
-                       sas_ha->notify_port_event(sas_phy,
-                                               PORTE_BROADCAST_RCVD);
-               }
-       }
-       mvs_write_port_irq_stat(mvi, phy_no, phy->irq_status);
-}
-
-static void mvs_int_sata(struct mvs_info *mvi)
-{
-       u32 tmp;
-       void __iomem *regs = mvi->regs;
-       tmp = mr32(INT_STAT_SRS);
-       mw32(INT_STAT_SRS, tmp & 0xFFFF);
-}
-
-static void mvs_slot_reset(struct mvs_info *mvi, struct sas_task *task,
-                               u32 slot_idx)
-{
-       void __iomem *regs = mvi->regs;
-       struct domain_device *dev = task->dev;
-       struct asd_sas_port *sas_port = dev->port;
-       struct mvs_port *port = mvi->slot_info[slot_idx].port;
-       u32 reg_set, phy_mask;
-
-       if (!sas_protocol_ata(task->task_proto)) {
-               reg_set = 0;
-               phy_mask = (port->wide_port_phymap) ? port->wide_port_phymap :
-                               sas_port->phy_mask;
-       } else {
-               reg_set = port->taskfileset;
-               phy_mask = sas_port->phy_mask;
-       }
-       mvi->tx[mvi->tx_prod] = cpu_to_le32(TXQ_MODE_I | slot_idx |
-                                       (TXQ_CMD_SLOT_RESET << TXQ_CMD_SHIFT) |
-                                       (phy_mask << TXQ_PHY_SHIFT) |
-                                       (reg_set << TXQ_SRS_SHIFT));
-
-       mw32(TX_PROD_IDX, mvi->tx_prod);
-       mvi->tx_prod = (mvi->tx_prod + 1) & (MVS_CHIP_SLOT_SZ - 1);
-}
-
-static int mvs_sata_done(struct mvs_info *mvi, struct sas_task *task,
-                       u32 slot_idx, int err)
-{
-       struct mvs_port *port = mvi->slot_info[slot_idx].port;
-       struct task_status_struct *tstat = &task->task_status;
-       struct ata_task_resp *resp = (struct ata_task_resp *)tstat->buf;
-       int stat = SAM_GOOD;
-
-       resp->frame_len = sizeof(struct dev_to_host_fis);
-       memcpy(&resp->ending_fis[0],
-              SATA_RECEIVED_D2H_FIS(port->taskfileset),
-              sizeof(struct dev_to_host_fis));
-       tstat->buf_valid_size = sizeof(*resp);
-       if (unlikely(err))
-               stat = SAS_PROTO_RESPONSE;
-       return stat;
-}
-
-static void mvs_slot_free(struct mvs_info *mvi, u32 rx_desc)
-{
-       u32 slot_idx = rx_desc & RXQ_SLOT_MASK;
-       mvs_tag_clear(mvi, slot_idx);
-}
-
-static void mvs_slot_task_free(struct mvs_info *mvi, struct sas_task *task,
-                         struct mvs_slot_info *slot, u32 slot_idx)
-{
-       if (!sas_protocol_ata(task->task_proto))
-               if (slot->n_elem)
-                       pci_unmap_sg(mvi->pdev, task->scatter,
-                                    slot->n_elem, task->data_dir);
-
-       switch (task->task_proto) {
-       case SAS_PROTOCOL_SMP:
-               pci_unmap_sg(mvi->pdev, &task->smp_task.smp_resp, 1,
-                            PCI_DMA_FROMDEVICE);
-               pci_unmap_sg(mvi->pdev, &task->smp_task.smp_req, 1,
-                            PCI_DMA_TODEVICE);
-               break;
-
-       case SAS_PROTOCOL_SATA:
-       case SAS_PROTOCOL_STP:
-       case SAS_PROTOCOL_SSP:
-       default:
-               /* do nothing */
-               break;
-       }
-       list_del(&slot->list);
-       task->lldd_task = NULL;
-       slot->task = NULL;
-       slot->port = NULL;
-}
-
-static int mvs_slot_err(struct mvs_info *mvi, struct sas_task *task,
-                        u32 slot_idx)
-{
-       struct mvs_slot_info *slot = &mvi->slot_info[slot_idx];
-       u32 err_dw0 = le32_to_cpu(*(u32 *) (slot->response));
-       u32 err_dw1 = le32_to_cpu(*(u32 *) (slot->response + 4));
-       int stat = SAM_CHECK_COND;
-
-       if (err_dw1 & SLOT_BSY_ERR) {
-               stat = SAS_QUEUE_FULL;
-               mvs_slot_reset(mvi, task, slot_idx);
-       }
-       switch (task->task_proto) {
-       case SAS_PROTOCOL_SSP:
-               break;
-       case SAS_PROTOCOL_SMP:
-               break;
-       case SAS_PROTOCOL_SATA:
-       case SAS_PROTOCOL_STP:
-       case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
-               if (err_dw0 & TFILE_ERR)
-                       stat = mvs_sata_done(mvi, task, slot_idx, 1);
-               break;
-       default:
-               break;
-       }
-
-       mvs_hexdump(16, (u8 *) slot->response, 0);
-       return stat;
-}
-
-static int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags)
-{
-       u32 slot_idx = rx_desc & RXQ_SLOT_MASK;
-       struct mvs_slot_info *slot = &mvi->slot_info[slot_idx];
-       struct sas_task *task = slot->task;
-       struct task_status_struct *tstat;
-       struct mvs_port *port;
-       bool aborted;
-       void *to;
-
-       if (unlikely(!task || !task->lldd_task))
-               return -1;
-
-       mvs_hba_cq_dump(mvi);
-
-       spin_lock(&task->task_state_lock);
-       aborted = task->task_state_flags & SAS_TASK_STATE_ABORTED;
-       if (!aborted) {
-               task->task_state_flags &=
-                   ~(SAS_TASK_STATE_PENDING | SAS_TASK_AT_INITIATOR);
-               task->task_state_flags |= SAS_TASK_STATE_DONE;
-       }
-       spin_unlock(&task->task_state_lock);
-
-       if (aborted) {
-               mvs_slot_task_free(mvi, task, slot, slot_idx);
-               mvs_slot_free(mvi, rx_desc);
-               return -1;
-       }
-
-       port = slot->port;
-       tstat = &task->task_status;
-       memset(tstat, 0, sizeof(*tstat));
-       tstat->resp = SAS_TASK_COMPLETE;
-
-       if (unlikely(!port->port_attached || flags)) {
-               mvs_slot_err(mvi, task, slot_idx);
-               if (!sas_protocol_ata(task->task_proto))
-                       tstat->stat = SAS_PHY_DOWN;
-               goto out;
-       }
-
-       /* error info record present */
-       if (unlikely((rx_desc & RXQ_ERR) && (*(u64 *) slot->response))) {
-               tstat->stat = mvs_slot_err(mvi, task, slot_idx);
-               goto out;
-       }
-
-       switch (task->task_proto) {
-       case SAS_PROTOCOL_SSP:
-               /* hw says status == 0, datapres == 0 */
-               if (rx_desc & RXQ_GOOD) {
-                       tstat->stat = SAM_GOOD;
-                       tstat->resp = SAS_TASK_COMPLETE;
-               }
-               /* response frame present */
-               else if (rx_desc & RXQ_RSP) {
-                       struct ssp_response_iu *iu =
-                           slot->response + sizeof(struct mvs_err_info);
-                       sas_ssp_task_response(&mvi->pdev->dev, task, iu);
-               }
-
-               /* should never happen? */
-               else
-                       tstat->stat = SAM_CHECK_COND;
-               break;
-
-       case SAS_PROTOCOL_SMP: {
-                       struct scatterlist *sg_resp = &task->smp_task.smp_resp;
-                       tstat->stat = SAM_GOOD;
-                       to = kmap_atomic(sg_page(sg_resp), KM_IRQ0);
-                       memcpy(to + sg_resp->offset,
-                               slot->response + sizeof(struct mvs_err_info),
-                               sg_dma_len(sg_resp));
-                       kunmap_atomic(to, KM_IRQ0);
-                       break;
-               }
-
-       case SAS_PROTOCOL_SATA:
-       case SAS_PROTOCOL_STP:
-       case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP: {
-                       tstat->stat = mvs_sata_done(mvi, task, slot_idx, 0);
-                       break;
-               }
-
-       default:
-               tstat->stat = SAM_CHECK_COND;
-               break;
-       }
-
-out:
-       mvs_slot_task_free(mvi, task, slot, slot_idx);
-       if (unlikely(tstat->stat != SAS_QUEUE_FULL))
-               mvs_slot_free(mvi, rx_desc);
-
-       spin_unlock(&mvi->lock);
-       task->task_done(task);
-       spin_lock(&mvi->lock);
-       return tstat->stat;
-}
-
-static void mvs_release_task(struct mvs_info *mvi, int phy_no)
-{
-       struct list_head *pos, *n;
-       struct mvs_slot_info *slot;
-       struct mvs_phy *phy = &mvi->phy[phy_no];
-       struct mvs_port *port = phy->port;
-       u32 rx_desc;
-
-       if (!port)
-               return;
-
-       list_for_each_safe(pos, n, &port->list) {
-               slot = container_of(pos, struct mvs_slot_info, list);
-               rx_desc = (u32) (slot - mvi->slot_info);
-               mvs_slot_complete(mvi, rx_desc, 1);
-       }
-}
-
-static void mvs_int_full(struct mvs_info *mvi)
-{
-       void __iomem *regs = mvi->regs;
-       u32 tmp, stat;
-       int i;
-
-       stat = mr32(INT_STAT);
-
-       mvs_int_rx(mvi, false);
-
-       for (i = 0; i < MVS_MAX_PORTS; i++) {
-               tmp = (stat >> i) & (CINT_PORT | CINT_PORT_STOPPED);
-               if (tmp)
-                       mvs_int_port(mvi, i, tmp);
-       }
-
-       if (stat & CINT_SRS)
-               mvs_int_sata(mvi);
-
-       mw32(INT_STAT, stat);
-}
-
-static int mvs_int_rx(struct mvs_info *mvi, bool self_clear)
-{
-       void __iomem *regs = mvi->regs;
-       u32 rx_prod_idx, rx_desc;
-       bool attn = false;
-       struct pci_dev *pdev = mvi->pdev;
-
-       /* the first dword in the RX ring is special: it contains
-        * a mirror of the hardware's RX producer index, so that
-        * we don't have to stall the CPU reading that register.
-        * The actual RX ring is offset by one dword, due to this.
-        */
-       rx_prod_idx = mvi->rx_cons;
-       mvi->rx_cons = le32_to_cpu(mvi->rx[0]);
-       if (mvi->rx_cons == 0xfff)      /* h/w hasn't touched RX ring yet */
-               return 0;
-
-       /* The CMPL_Q may come late, read from register and try again
-       * note: if coalescing is enabled,
-       * it will need to read from register every time for sure
-       */
-       if (mvi->rx_cons == rx_prod_idx)
-               mvi->rx_cons = mr32(RX_CONS_IDX) & RX_RING_SZ_MASK;
-
-       if (mvi->rx_cons == rx_prod_idx)
-               return 0;
-
-       while (mvi->rx_cons != rx_prod_idx) {
-
-               /* increment our internal RX consumer pointer */
-               rx_prod_idx = (rx_prod_idx + 1) & (MVS_RX_RING_SZ - 1);
-
-               rx_desc = le32_to_cpu(mvi->rx[rx_prod_idx + 1]);
-
-               if (likely(rx_desc & RXQ_DONE))
-                       mvs_slot_complete(mvi, rx_desc, 0);
-               if (rx_desc & RXQ_ATTN) {
-                       attn = true;
-                       dev_printk(KERN_DEBUG, &pdev->dev, "ATTN %X\n",
-                               rx_desc);
-               } else if (rx_desc & RXQ_ERR) {
-                       if (!(rx_desc & RXQ_DONE))
-                               mvs_slot_complete(mvi, rx_desc, 0);
-                       dev_printk(KERN_DEBUG, &pdev->dev, "RXQ_ERR %X\n",
-                               rx_desc);
-               } else if (rx_desc & RXQ_SLOT_RESET) {
-                       dev_printk(KERN_DEBUG, &pdev->dev, "Slot reset[%X]\n",
-                               rx_desc);
-                       mvs_slot_free(mvi, rx_desc);
-               }
-       }
-
-       if (attn && self_clear)
-               mvs_int_full(mvi);
-
-       return 0;
-}
-
-#ifdef MVS_USE_TASKLET
-static void mvs_tasklet(unsigned long data)
-{
-       struct mvs_info *mvi = (struct mvs_info *) data;
-       unsigned long flags;
-
-       spin_lock_irqsave(&mvi->lock, flags);
-
-#ifdef MVS_DISABLE_MSI
-       mvs_int_full(mvi);
-#else
-       mvs_int_rx(mvi, true);
-#endif
-       spin_unlock_irqrestore(&mvi->lock, flags);
-}
-#endif
-
-static irqreturn_t mvs_interrupt(int irq, void *opaque)
-{
-       struct mvs_info *mvi = opaque;
-       void __iomem *regs = mvi->regs;
-       u32 stat;
-
-       stat = mr32(GBL_INT_STAT);
-
-       if (stat == 0 || stat == 0xffffffff)
-               return IRQ_NONE;
-
-       /* clear CMD_CMPLT ASAP */
-       mw32_f(INT_STAT, CINT_DONE);
-
-#ifndef MVS_USE_TASKLET
-       spin_lock(&mvi->lock);
-
-       mvs_int_full(mvi);
-
-       spin_unlock(&mvi->lock);
-#else
-       tasklet_schedule(&mvi->tasklet);
-#endif
-       return IRQ_HANDLED;
-}
-
-#ifndef MVS_DISABLE_MSI
-static irqreturn_t mvs_msi_interrupt(int irq, void *opaque)
-{
-       struct mvs_info *mvi = opaque;
-
-#ifndef MVS_USE_TASKLET
-       spin_lock(&mvi->lock);
-
-       mvs_int_rx(mvi, true);
-
-       spin_unlock(&mvi->lock);
-#else
-       tasklet_schedule(&mvi->tasklet);
-#endif
-       return IRQ_HANDLED;
-}
-#endif
-
-struct mvs_task_exec_info {
-       struct sas_task *task;
-       struct mvs_cmd_hdr *hdr;
-       struct mvs_port *port;
-       u32 tag;
-       int n_elem;
-};
-
-static int mvs_task_prep_smp(struct mvs_info *mvi,
-                            struct mvs_task_exec_info *tei)
-{
-       int elem, rc, i;
-       struct sas_task *task = tei->task;
-       struct mvs_cmd_hdr *hdr = tei->hdr;
-       struct scatterlist *sg_req, *sg_resp;
-       u32 req_len, resp_len, tag = tei->tag;
-       void *buf_tmp;
-       u8 *buf_oaf;
-       dma_addr_t buf_tmp_dma;
-       struct mvs_prd *buf_prd;
-       struct scatterlist *sg;
-       struct mvs_slot_info *slot = &mvi->slot_info[tag];
-       struct asd_sas_port *sas_port = task->dev->port;
-       u32 flags = (tei->n_elem << MCH_PRD_LEN_SHIFT);
-#if _MV_DUMP
-       u8 *buf_cmd;
-       void *from;
-#endif
-       /*
-        * DMA-map SMP request, response buffers
-        */
-       sg_req = &task->smp_task.smp_req;
-       elem = pci_map_sg(mvi->pdev, sg_req, 1, PCI_DMA_TODEVICE);
-       if (!elem)
-               return -ENOMEM;
-       req_len = sg_dma_len(sg_req);
-
-       sg_resp = &task->smp_task.smp_resp;
-       elem = pci_map_sg(mvi->pdev, sg_resp, 1, PCI_DMA_FROMDEVICE);
-       if (!elem) {
-               rc = -ENOMEM;
-               goto err_out;
-       }
-       resp_len = sg_dma_len(sg_resp);
-
-       /* must be in dwords */
-       if ((req_len & 0x3) || (resp_len & 0x3)) {
-               rc = -EINVAL;
-               goto err_out_2;
-       }
-
-       /*
-        * arrange MVS_SLOT_BUF_SZ-sized DMA buffer according to our needs
-        */
-
-       /* region 1: command table area (MVS_SSP_CMD_SZ bytes) ************** */
-       buf_tmp = slot->buf;
-       buf_tmp_dma = slot->buf_dma;
-
-#if _MV_DUMP
-       buf_cmd = buf_tmp;
-       hdr->cmd_tbl = cpu_to_le64(buf_tmp_dma);
-       buf_tmp += req_len;
-       buf_tmp_dma += req_len;
-       slot->cmd_size = req_len;
-#else
-       hdr->cmd_tbl = cpu_to_le64(sg_dma_address(sg_req));
-#endif
-
-       /* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
-       buf_oaf = buf_tmp;
-       hdr->open_frame = cpu_to_le64(buf_tmp_dma);
-
-       buf_tmp += MVS_OAF_SZ;
-       buf_tmp_dma += MVS_OAF_SZ;
-
-       /* region 3: PRD table ********************************************* */
-       buf_prd = buf_tmp;
-       if (tei->n_elem)
-               hdr->prd_tbl = cpu_to_le64(buf_tmp_dma);
-       else
-               hdr->prd_tbl = 0;
-
-       i = sizeof(struct mvs_prd) * tei->n_elem;
-       buf_tmp += i;
-       buf_tmp_dma += i;
-
-       /* region 4: status buffer (larger the PRD, smaller this buf) ****** */
-       slot->response = buf_tmp;
-       hdr->status_buf = cpu_to_le64(buf_tmp_dma);
-
-       /*
-        * Fill in TX ring and command slot header
-        */
-       slot->tx = mvi->tx_prod;
-       mvi->tx[mvi->tx_prod] = cpu_to_le32((TXQ_CMD_SMP << TXQ_CMD_SHIFT) |
-                                       TXQ_MODE_I | tag |
-                                       (sas_port->phy_mask << TXQ_PHY_SHIFT));
-
-       hdr->flags |= flags;
-       hdr->lens = cpu_to_le32(((resp_len / 4) << 16) | ((req_len - 4) / 4));
-       hdr->tags = cpu_to_le32(tag);
-       hdr->data_len = 0;
-
-       /* generate open address frame hdr (first 12 bytes) */
-       buf_oaf[0] = (1 << 7) | (0 << 4) | 0x01; /* initiator, SMP, ftype 1h */
-       buf_oaf[1] = task->dev->linkrate & 0xf;
-       *(u16 *)(buf_oaf + 2) = 0xFFFF;         /* SAS SPEC */
-       memcpy(buf_oaf + 4, task->dev->sas_addr, SAS_ADDR_SIZE);
-
-       /* fill in PRD (scatter/gather) table, if any */
-       for_each_sg(task->scatter, sg, tei->n_elem, i) {
-               buf_prd->addr = cpu_to_le64(sg_dma_address(sg));
-               buf_prd->len = cpu_to_le32(sg_dma_len(sg));
-               buf_prd++;
-       }
-
-#if _MV_DUMP
-       /* copy cmd table */
-       from = kmap_atomic(sg_page(sg_req), KM_IRQ0);
-       memcpy(buf_cmd, from + sg_req->offset, req_len);
-       kunmap_atomic(from, KM_IRQ0);
-#endif
-       return 0;
-
-err_out_2:
-       pci_unmap_sg(mvi->pdev, &tei->task->smp_task.smp_resp, 1,
-                    PCI_DMA_FROMDEVICE);
-err_out:
-       pci_unmap_sg(mvi->pdev, &tei->task->smp_task.smp_req, 1,
-                    PCI_DMA_TODEVICE);
-       return rc;
-}
-
-static void mvs_free_reg_set(struct mvs_info *mvi, struct mvs_port *port)
-{
-       void __iomem *regs = mvi->regs;
-       u32 tmp, offs;
-       u8 *tfs = &port->taskfileset;
-
-       if (*tfs == MVS_ID_NOT_MAPPED)
-               return;
-
-       offs = 1U << ((*tfs & 0x0f) + PCS_EN_SATA_REG_SHIFT);
-       if (*tfs < 16) {
-               tmp = mr32(PCS);
-               mw32(PCS, tmp & ~offs);
-       } else {
-               tmp = mr32(CTL);
-               mw32(CTL, tmp & ~offs);
-       }
-
-       tmp = mr32(INT_STAT_SRS) & (1U << *tfs);
-       if (tmp)
-               mw32(INT_STAT_SRS, tmp);
-
-       *tfs = MVS_ID_NOT_MAPPED;
-}
-
-static u8 mvs_assign_reg_set(struct mvs_info *mvi, struct mvs_port *port)
-{
-       int i;
-       u32 tmp, offs;
-       void __iomem *regs = mvi->regs;
-
-       if (port->taskfileset != MVS_ID_NOT_MAPPED)
-               return 0;
-
-       tmp = mr32(PCS);
-
-       for (i = 0; i < mvi->chip->srs_sz; i++) {
-               if (i == 16)
-                       tmp = mr32(CTL);
-               offs = 1U << ((i & 0x0f) + PCS_EN_SATA_REG_SHIFT);
-               if (!(tmp & offs)) {
-                       port->taskfileset = i;
-
-                       if (i < 16)
-                               mw32(PCS, tmp | offs);
-                       else
-                               mw32(CTL, tmp | offs);
-                       tmp = mr32(INT_STAT_SRS) & (1U << i);
-                       if (tmp)
-                               mw32(INT_STAT_SRS, tmp);
-                       return 0;
-               }
-       }
-       return MVS_ID_NOT_MAPPED;
-}
-
-static u32 mvs_get_ncq_tag(struct sas_task *task, u32 *tag)
-{
-       struct ata_queued_cmd *qc = task->uldd_task;
-
-       if (qc) {
-               if (qc->tf.command == ATA_CMD_FPDMA_WRITE ||
-                       qc->tf.command == ATA_CMD_FPDMA_READ) {
-                       *tag = qc->tag;
-                       return 1;
-               }
-       }
-
-       return 0;
-}
-
-static int mvs_task_prep_ata(struct mvs_info *mvi,
-                            struct mvs_task_exec_info *tei)
-{
-       struct sas_task *task = tei->task;
-       struct domain_device *dev = task->dev;
-       struct mvs_cmd_hdr *hdr = tei->hdr;
-       struct asd_sas_port *sas_port = dev->port;
-       struct mvs_slot_info *slot;
-       struct scatterlist *sg;
-       struct mvs_prd *buf_prd;
-       struct mvs_port *port = tei->port;
-       u32 tag = tei->tag;
-       u32 flags = (tei->n_elem << MCH_PRD_LEN_SHIFT);
-       void *buf_tmp;
-       u8 *buf_cmd, *buf_oaf;
-       dma_addr_t buf_tmp_dma;
-       u32 i, req_len, resp_len;
-       const u32 max_resp_len = SB_RFB_MAX;
-
-       if (mvs_assign_reg_set(mvi, port) == MVS_ID_NOT_MAPPED)
-               return -EBUSY;
-
-       slot = &mvi->slot_info[tag];
-       slot->tx = mvi->tx_prod;
-       mvi->tx[mvi->tx_prod] = cpu_to_le32(TXQ_MODE_I | tag |
-                                       (TXQ_CMD_STP << TXQ_CMD_SHIFT) |
-                                       (sas_port->phy_mask << TXQ_PHY_SHIFT) |
-                                       (port->taskfileset << TXQ_SRS_SHIFT));
-
-       if (task->ata_task.use_ncq)
-               flags |= MCH_FPDMA;
-       if (dev->sata_dev.command_set == ATAPI_COMMAND_SET) {
-               if (task->ata_task.fis.command != ATA_CMD_ID_ATAPI)
-                       flags |= MCH_ATAPI;
-       }
-
-       /* FIXME: fill in port multiplier number */
-
-       hdr->flags = cpu_to_le32(flags);
-
-       /* FIXME: the low order order 5 bits for the TAG if enable NCQ */
-       if (task->ata_task.use_ncq && mvs_get_ncq_tag(task, &hdr->tags))
-               task->ata_task.fis.sector_count |= hdr->tags << 3;
-       else
-               hdr->tags = cpu_to_le32(tag);
-       hdr->data_len = cpu_to_le32(task->total_xfer_len);
-
-       /*
-        * arrange MVS_SLOT_BUF_SZ-sized DMA buffer according to our needs
-        */
-
-       /* region 1: command table area (MVS_ATA_CMD_SZ bytes) ************** */
-       buf_cmd = buf_tmp = slot->buf;
-       buf_tmp_dma = slot->buf_dma;
-
-       hdr->cmd_tbl = cpu_to_le64(buf_tmp_dma);
-
-       buf_tmp += MVS_ATA_CMD_SZ;
-       buf_tmp_dma += MVS_ATA_CMD_SZ;
-#if _MV_DUMP
-       slot->cmd_size = MVS_ATA_CMD_SZ;
-#endif
-
-       /* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
-       /* used for STP.  unused for SATA? */
-       buf_oaf = buf_tmp;
-       hdr->open_frame = cpu_to_le64(buf_tmp_dma);
-
-       buf_tmp += MVS_OAF_SZ;
-       buf_tmp_dma += MVS_OAF_SZ;
-
-       /* region 3: PRD table ********************************************* */
-       buf_prd = buf_tmp;
-       if (tei->n_elem)
-               hdr->prd_tbl = cpu_to_le64(buf_tmp_dma);
-       else
-               hdr->prd_tbl = 0;
-
-       i = sizeof(struct mvs_prd) * tei->n_elem;
-       buf_tmp += i;
-       buf_tmp_dma += i;
-
-       /* region 4: status buffer (larger the PRD, smaller this buf) ****** */
-       /* FIXME: probably unused, for SATA.  kept here just in case
-        * we get a STP/SATA error information record
-        */
-       slot->response = buf_tmp;
-       hdr->status_buf = cpu_to_le64(buf_tmp_dma);
-
-       req_len = sizeof(struct host_to_dev_fis);
-       resp_len = MVS_SLOT_BUF_SZ - MVS_ATA_CMD_SZ -
-           sizeof(struct mvs_err_info) - i;
-
-       /* request, response lengths */
-       resp_len = min(resp_len, max_resp_len);
-       hdr->lens = cpu_to_le32(((resp_len / 4) << 16) | (req_len / 4));
-
-       task->ata_task.fis.flags |= 0x80; /* C=1: update ATA cmd reg */
-       /* fill in command FIS and ATAPI CDB */
-       memcpy(buf_cmd, &task->ata_task.fis, sizeof(struct host_to_dev_fis));
-       if (dev->sata_dev.command_set == ATAPI_COMMAND_SET)
-               memcpy(buf_cmd + STP_ATAPI_CMD,
-                       task->ata_task.atapi_packet, 16);
-
-       /* generate open address frame hdr (first 12 bytes) */
-       buf_oaf[0] = (1 << 7) | (2 << 4) | 0x1; /* initiator, STP, ftype 1h */
-       buf_oaf[1] = task->dev->linkrate & 0xf;
-       *(u16 *)(buf_oaf + 2) = cpu_to_be16(tag);
-       memcpy(buf_oaf + 4, task->dev->sas_addr, SAS_ADDR_SIZE);
-
-       /* fill in PRD (scatter/gather) table, if any */
-       for_each_sg(task->scatter, sg, tei->n_elem, i) {
-               buf_prd->addr = cpu_to_le64(sg_dma_address(sg));
-               buf_prd->len = cpu_to_le32(sg_dma_len(sg));
-               buf_prd++;
-       }
-
-       return 0;
-}
-
-static int mvs_task_prep_ssp(struct mvs_info *mvi,
-                            struct mvs_task_exec_info *tei)
-{
-       struct sas_task *task = tei->task;
-       struct mvs_cmd_hdr *hdr = tei->hdr;
-       struct mvs_port *port = tei->port;
-       struct mvs_slot_info *slot;
-       struct scatterlist *sg;
-       struct mvs_prd *buf_prd;
-       struct ssp_frame_hdr *ssp_hdr;
-       void *buf_tmp;
-       u8 *buf_cmd, *buf_oaf, fburst = 0;
-       dma_addr_t buf_tmp_dma;
-       u32 flags;
-       u32 resp_len, req_len, i, tag = tei->tag;
-       const u32 max_resp_len = SB_RFB_MAX;
-       u8 phy_mask;
-
-       slot = &mvi->slot_info[tag];
-
-       phy_mask = (port->wide_port_phymap) ? port->wide_port_phymap :
-               task->dev->port->phy_mask;
-       slot->tx = mvi->tx_prod;
-       mvi->tx[mvi->tx_prod] = cpu_to_le32(TXQ_MODE_I | tag |
-                               (TXQ_CMD_SSP << TXQ_CMD_SHIFT) |
-                               (phy_mask << TXQ_PHY_SHIFT));
-
-       flags = MCH_RETRY;
-       if (task->ssp_task.enable_first_burst) {
-               flags |= MCH_FBURST;
-               fburst = (1 << 7);
-       }
-       hdr->flags = cpu_to_le32(flags |
-                                (tei->n_elem << MCH_PRD_LEN_SHIFT) |
-                                (MCH_SSP_FR_CMD << MCH_SSP_FR_TYPE_SHIFT));
-
-       hdr->tags = cpu_to_le32(tag);
-       hdr->data_len = cpu_to_le32(task->total_xfer_len);
-
-       /*
-        * arrange MVS_SLOT_BUF_SZ-sized DMA buffer according to our needs
-        */
-
-       /* region 1: command table area (MVS_SSP_CMD_SZ bytes) ************** */
-       buf_cmd = buf_tmp = slot->buf;
-       buf_tmp_dma = slot->buf_dma;
-
-       hdr->cmd_tbl = cpu_to_le64(buf_tmp_dma);
-
-       buf_tmp += MVS_SSP_CMD_SZ;
-       buf_tmp_dma += MVS_SSP_CMD_SZ;
-#if _MV_DUMP
-       slot->cmd_size = MVS_SSP_CMD_SZ;
-#endif
-
-       /* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
-       buf_oaf = buf_tmp;
-       hdr->open_frame = cpu_to_le64(buf_tmp_dma);
-
-       buf_tmp += MVS_OAF_SZ;
-       buf_tmp_dma += MVS_OAF_SZ;
-
-       /* region 3: PRD table ********************************************* */
-       buf_prd = buf_tmp;
-       if (tei->n_elem)
-               hdr->prd_tbl = cpu_to_le64(buf_tmp_dma);
-       else
-               hdr->prd_tbl = 0;
-
-       i = sizeof(struct mvs_prd) * tei->n_elem;
-       buf_tmp += i;
-       buf_tmp_dma += i;
-
-       /* region 4: status buffer (larger the PRD, smaller this buf) ****** */
-       slot->response = buf_tmp;
-       hdr->status_buf = cpu_to_le64(buf_tmp_dma);
-
-       resp_len = MVS_SLOT_BUF_SZ - MVS_SSP_CMD_SZ - MVS_OAF_SZ -
-           sizeof(struct mvs_err_info) - i;
-       resp_len = min(resp_len, max_resp_len);
-
-       req_len = sizeof(struct ssp_frame_hdr) + 28;
-
-       /* request, response lengths */
-       hdr->lens = cpu_to_le32(((resp_len / 4) << 16) | (req_len / 4));
-
-       /* generate open address frame hdr (first 12 bytes) */
-       buf_oaf[0] = (1 << 7) | (1 << 4) | 0x1; /* initiator, SSP, ftype 1h */
-       buf_oaf[1] = task->dev->linkrate & 0xf;
-       *(u16 *)(buf_oaf + 2) = cpu_to_be16(tag);
-       memcpy(buf_oaf + 4, task->dev->sas_addr, SAS_ADDR_SIZE);
-
-       /* fill in SSP frame header (Command Table.SSP frame header) */
-       ssp_hdr = (struct ssp_frame_hdr *)buf_cmd;
-       ssp_hdr->frame_type = SSP_COMMAND;
-       memcpy(ssp_hdr->hashed_dest_addr, task->dev->hashed_sas_addr,
-              HASHED_SAS_ADDR_SIZE);
-       memcpy(ssp_hdr->hashed_src_addr,
-              task->dev->port->ha->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
-       ssp_hdr->tag = cpu_to_be16(tag);
-
-       /* fill in command frame IU */
-       buf_cmd += sizeof(*ssp_hdr);
-       memcpy(buf_cmd, &task->ssp_task.LUN, 8);
-       buf_cmd[9] = fburst | task->ssp_task.task_attr |
-                       (task->ssp_task.task_prio << 3);
-       memcpy(buf_cmd + 12, &task->ssp_task.cdb, 16);
-
-       /* fill in PRD (scatter/gather) table, if any */
-       for_each_sg(task->scatter, sg, tei->n_elem, i) {
-               buf_prd->addr = cpu_to_le64(sg_dma_address(sg));
-               buf_prd->len = cpu_to_le32(sg_dma_len(sg));
-               buf_prd++;
-       }
-
-       return 0;
-}
-
-static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags)
-{
-       struct domain_device *dev = task->dev;
-       struct mvs_info *mvi = dev->port->ha->lldd_ha;
-       struct pci_dev *pdev = mvi->pdev;
-       void __iomem *regs = mvi->regs;
-       struct mvs_task_exec_info tei;
-       struct sas_task *t = task;
-       struct mvs_slot_info *slot;
-       u32 tag = 0xdeadbeef, rc, n_elem = 0;
-       unsigned long flags;
-       u32 n = num, pass = 0;
-
-       spin_lock_irqsave(&mvi->lock, flags);
-       do {
-               dev = t->dev;
-               tei.port = &mvi->port[dev->port->id];
-
-               if (!tei.port->port_attached) {
-                       if (sas_protocol_ata(t->task_proto)) {
-                               rc = SAS_PHY_DOWN;
-                               goto out_done;
-                       } else {
-                               struct task_status_struct *ts = &t->task_status;
-                               ts->resp = SAS_TASK_UNDELIVERED;
-                               ts->stat = SAS_PHY_DOWN;
-                               t->task_done(t);
-                               if (n > 1)
-                                       t = list_entry(t->list.next,
-                                                       struct sas_task, list);
-                               continue;
-                       }
-               }
-
-               if (!sas_protocol_ata(t->task_proto)) {
-                       if (t->num_scatter) {
-                               n_elem = pci_map_sg(mvi->pdev, t->scatter,
-                                                   t->num_scatter,
-                                                   t->data_dir);
-                               if (!n_elem) {
-                                       rc = -ENOMEM;
-                                       goto err_out;
-                               }
-                       }
-               } else {
-                       n_elem = t->num_scatter;
-               }
-
-               rc = mvs_tag_alloc(mvi, &tag);
-               if (rc)
-                       goto err_out;
-
-               slot = &mvi->slot_info[tag];
-               t->lldd_task = NULL;
-               slot->n_elem = n_elem;
-               memset(slot->buf, 0, MVS_SLOT_BUF_SZ);
-               tei.task = t;
-               tei.hdr = &mvi->slot[tag];
-               tei.tag = tag;
-               tei.n_elem = n_elem;
-
-               switch (t->task_proto) {
-               case SAS_PROTOCOL_SMP:
-                       rc = mvs_task_prep_smp(mvi, &tei);
-                       break;
-               case SAS_PROTOCOL_SSP:
-                       rc = mvs_task_prep_ssp(mvi, &tei);
-                       break;
-               case SAS_PROTOCOL_SATA:
-               case SAS_PROTOCOL_STP:
-               case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
-                       rc = mvs_task_prep_ata(mvi, &tei);
-                       break;
-               default:
-                       dev_printk(KERN_ERR, &pdev->dev,
-                               "unknown sas_task proto: 0x%x\n",
-                               t->task_proto);
-                       rc = -EINVAL;
-                       break;
-               }
-
-               if (rc)
-                       goto err_out_tag;
-
-               slot->task = t;
-               slot->port = tei.port;
-               t->lldd_task = (void *) slot;
-               list_add_tail(&slot->list, &slot->port->list);
-               /* TODO: select normal or high priority */
-
-               spin_lock(&t->task_state_lock);
-               t->task_state_flags |= SAS_TASK_AT_INITIATOR;
-               spin_unlock(&t->task_state_lock);
-
-               mvs_hba_memory_dump(mvi, tag, t->task_proto);
-
-               ++pass;
-               mvi->tx_prod = (mvi->tx_prod + 1) & (MVS_CHIP_SLOT_SZ - 1);
-               if (n > 1)
-                       t = list_entry(t->list.next, struct sas_task, list);
-       } while (--n);
-
-       rc = 0;
-       goto out_done;
-
-err_out_tag:
-       mvs_tag_free(mvi, tag);
-err_out:
-       dev_printk(KERN_ERR, &pdev->dev, "mvsas exec failed[%d]!\n", rc);
-       if (!sas_protocol_ata(t->task_proto))
-               if (n_elem)
-                       pci_unmap_sg(mvi->pdev, t->scatter, n_elem,
-                                    t->data_dir);
-out_done:
-       if (pass)
-               mw32(TX_PROD_IDX, (mvi->tx_prod - 1) & (MVS_CHIP_SLOT_SZ - 1));
-       spin_unlock_irqrestore(&mvi->lock, flags);
-       return rc;
-}
-
-static int mvs_task_abort(struct sas_task *task)
-{
-       int rc;
-       unsigned long flags;
-       struct mvs_info *mvi = task->dev->port->ha->lldd_ha;
-       struct pci_dev *pdev = mvi->pdev;
-       int tag;
-
-       spin_lock_irqsave(&task->task_state_lock, flags);
-       if (task->task_state_flags & SAS_TASK_STATE_DONE) {
-               rc = TMF_RESP_FUNC_COMPLETE;
-               spin_unlock_irqrestore(&task->task_state_lock, flags);
-               goto out_done;
-       }
-       spin_unlock_irqrestore(&task->task_state_lock, flags);
-
-       switch (task->task_proto) {
-       case SAS_PROTOCOL_SMP:
-               dev_printk(KERN_DEBUG, &pdev->dev, "SMP Abort! \n");
-               break;
-       case SAS_PROTOCOL_SSP:
-               dev_printk(KERN_DEBUG, &pdev->dev, "SSP Abort! \n");
-               break;
-       case SAS_PROTOCOL_SATA:
-       case SAS_PROTOCOL_STP:
-       case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:{
-               dev_printk(KERN_DEBUG, &pdev->dev, "STP Abort! \n");
-#if _MV_DUMP
-               dev_printk(KERN_DEBUG, &pdev->dev, "Dump D2H FIS: \n");
-               mvs_hexdump(sizeof(struct host_to_dev_fis),
-                               (void *)&task->ata_task.fis, 0);
-               dev_printk(KERN_DEBUG, &pdev->dev, "Dump ATAPI Cmd : \n");
-               mvs_hexdump(16, task->ata_task.atapi_packet, 0);
-#endif
-               spin_lock_irqsave(&task->task_state_lock, flags);
-               if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET) {
-                       /* TODO */
-                       ;
-               }
-               spin_unlock_irqrestore(&task->task_state_lock, flags);
-               break;
-       }
-       default:
-               break;
-       }
-
-       if (mvs_find_tag(mvi, task, &tag)) {
-               spin_lock_irqsave(&mvi->lock, flags);
-               mvs_slot_task_free(mvi, task, &mvi->slot_info[tag], tag);
-               spin_unlock_irqrestore(&mvi->lock, flags);
-       }
-       if (!mvs_task_exec(task, 1, GFP_ATOMIC))
-               rc = TMF_RESP_FUNC_COMPLETE;
-       else
-               rc = TMF_RESP_FUNC_FAILED;
-out_done:
-       return rc;
-}
-
-static void mvs_free(struct mvs_info *mvi)
-{
-       int i;
-
-       if (!mvi)
-               return;
-
-       for (i = 0; i < MVS_SLOTS; i++) {
-               struct mvs_slot_info *slot = &mvi->slot_info[i];
-
-               if (slot->buf)
-                       dma_free_coherent(&mvi->pdev->dev, MVS_SLOT_BUF_SZ,
-                                         slot->buf, slot->buf_dma);
-       }
-
-       if (mvi->tx)
-               dma_free_coherent(&mvi->pdev->dev,
-                                 sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ,
-                                 mvi->tx, mvi->tx_dma);
-       if (mvi->rx_fis)
-               dma_free_coherent(&mvi->pdev->dev, MVS_RX_FISL_SZ,
-                                 mvi->rx_fis, mvi->rx_fis_dma);
-       if (mvi->rx)
-               dma_free_coherent(&mvi->pdev->dev,
-                                 sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1),
-                                 mvi->rx, mvi->rx_dma);
-       if (mvi->slot)
-               dma_free_coherent(&mvi->pdev->dev,
-                                 sizeof(*mvi->slot) * MVS_SLOTS,
-                                 mvi->slot, mvi->slot_dma);
-#ifdef MVS_ENABLE_PERI
-       if (mvi->peri_regs)
-               iounmap(mvi->peri_regs);
-#endif
-       if (mvi->regs)
-               iounmap(mvi->regs);
-       if (mvi->shost)
-               scsi_host_put(mvi->shost);
-       kfree(mvi->sas.sas_port);
-       kfree(mvi->sas.sas_phy);
-       kfree(mvi);
-}
-
-/* FIXME: locking? */
-static int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
-                          void *funcdata)
-{
-       struct mvs_info *mvi = sas_phy->ha->lldd_ha;
-       int rc = 0, phy_id = sas_phy->id;
-       u32 tmp;
-
-       tmp = mvs_read_phy_ctl(mvi, phy_id);
-
-       switch (func) {
-       case PHY_FUNC_SET_LINK_RATE:{
-                       struct sas_phy_linkrates *rates = funcdata;
-                       u32 lrmin = 0, lrmax = 0;
-
-                       lrmin = (rates->minimum_linkrate << 8);
-                       lrmax = (rates->maximum_linkrate << 12);
-
-                       if (lrmin) {
-                               tmp &= ~(0xf << 8);
-                               tmp |= lrmin;
-                       }
-                       if (lrmax) {
-                               tmp &= ~(0xf << 12);
-                               tmp |= lrmax;
-                       }
-                       mvs_write_phy_ctl(mvi, phy_id, tmp);
-                       break;
-               }
-
-       case PHY_FUNC_HARD_RESET:
-               if (tmp & PHY_RST_HARD)
-                       break;
-               mvs_write_phy_ctl(mvi, phy_id, tmp | PHY_RST_HARD);
-               break;
-
-       case PHY_FUNC_LINK_RESET:
-               mvs_write_phy_ctl(mvi, phy_id, tmp | PHY_RST);
-               break;
-
-       case PHY_FUNC_DISABLE:
-       case PHY_FUNC_RELEASE_SPINUP_HOLD:
-       default:
-               rc = -EOPNOTSUPP;
-       }
-
-       return rc;
-}
-
-static void __devinit mvs_phy_init(struct mvs_info *mvi, int phy_id)
-{
-       struct mvs_phy *phy = &mvi->phy[phy_id];
-       struct asd_sas_phy *sas_phy = &phy->sas_phy;
-
-       sas_phy->enabled = (phy_id < mvi->chip->n_phy) ? 1 : 0;
-       sas_phy->class = SAS;
-       sas_phy->iproto = SAS_PROTOCOL_ALL;
-       sas_phy->tproto = 0;
-       sas_phy->type = PHY_TYPE_PHYSICAL;
-       sas_phy->role = PHY_ROLE_INITIATOR;
-       sas_phy->oob_mode = OOB_NOT_CONNECTED;
-       sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN;
-
-       sas_phy->id = phy_id;
-       sas_phy->sas_addr = &mvi->sas_addr[0];
-       sas_phy->frame_rcvd = &phy->frame_rcvd[0];
-       sas_phy->ha = &mvi->sas;
-       sas_phy->lldd_phy = phy;
-}
-
-static struct mvs_info *__devinit mvs_alloc(struct pci_dev *pdev,
-                                           const struct pci_device_id *ent)
-{
-       struct mvs_info *mvi;
-       unsigned long res_start, res_len, res_flag;
-       struct asd_sas_phy **arr_phy;
-       struct asd_sas_port **arr_port;
-       const struct mvs_chip_info *chip = &mvs_chips[ent->driver_data];
-       int i;
-
-       /*
-        * alloc and init our per-HBA mvs_info struct
-        */
-
-       mvi = kzalloc(sizeof(*mvi), GFP_KERNEL);
-       if (!mvi)
-               return NULL;
-
-       spin_lock_init(&mvi->lock);
-#ifdef MVS_USE_TASKLET
-       tasklet_init(&mvi->tasklet, mvs_tasklet, (unsigned long)mvi);
-#endif
-       mvi->pdev = pdev;
-       mvi->chip = chip;
-
-       if (pdev->device == 0x6440 && pdev->revision == 0)
-               mvi->flags |= MVF_PHY_PWR_FIX;
-
-       /*
-        * alloc and init SCSI, SAS glue
-        */
-
-       mvi->shost = scsi_host_alloc(&mvs_sht, sizeof(void *));
-       if (!mvi->shost)
-               goto err_out;
-
-       arr_phy = kcalloc(MVS_MAX_PHYS, sizeof(void *), GFP_KERNEL);
-       arr_port = kcalloc(MVS_MAX_PHYS, sizeof(void *), GFP_KERNEL);
-       if (!arr_phy || !arr_port)
-               goto err_out;
-
-       for (i = 0; i < MVS_MAX_PHYS; i++) {
-               mvs_phy_init(mvi, i);
-               arr_phy[i] = &mvi->phy[i].sas_phy;
-               arr_port[i] = &mvi->port[i].sas_port;
-               mvi->port[i].taskfileset = MVS_ID_NOT_MAPPED;
-               mvi->port[i].wide_port_phymap = 0;
-               mvi->port[i].port_attached = 0;
-               INIT_LIST_HEAD(&mvi->port[i].list);
-       }
-
-       SHOST_TO_SAS_HA(mvi->shost) = &mvi->sas;
-       mvi->shost->transportt = mvs_stt;
-       mvi->shost->max_id = 21;
-       mvi->shost->max_lun = ~0;
-       mvi->shost->max_channel = 0;
-       mvi->shost->max_cmd_len = 16;
-
-       mvi->sas.sas_ha_name = DRV_NAME;
-       mvi->sas.dev = &pdev->dev;
-       mvi->sas.lldd_module = THIS_MODULE;
-       mvi->sas.sas_addr = &mvi->sas_addr[0];
-       mvi->sas.sas_phy = arr_phy;
-       mvi->sas.sas_port = arr_port;
-       mvi->sas.num_phys = chip->n_phy;
-       mvi->sas.lldd_max_execute_num = 1;
-       mvi->sas.lldd_queue_size = MVS_QUEUE_SIZE;
-       mvi->shost->can_queue = MVS_CAN_QUEUE;
-       mvi->shost->cmd_per_lun = MVS_SLOTS / mvi->sas.num_phys;
-       mvi->sas.lldd_ha = mvi;
-       mvi->sas.core.shost = mvi->shost;
-
-       mvs_tag_init(mvi);
-
-       /*
-        * ioremap main and peripheral registers
-        */
-
-#ifdef MVS_ENABLE_PERI
-       res_start = pci_resource_start(pdev, 2);
-       res_len = pci_resource_len(pdev, 2);
-       if (!res_start || !res_len)
-               goto err_out;
-
-       mvi->peri_regs = ioremap_nocache(res_start, res_len);
-       if (!mvi->peri_regs)
-               goto err_out;
-#endif
-
-       res_start = pci_resource_start(pdev, 4);
-       res_len = pci_resource_len(pdev, 4);
-       if (!res_start || !res_len)
-               goto err_out;
-
-       res_flag = pci_resource_flags(pdev, 4);
-       if (res_flag & IORESOURCE_CACHEABLE)
-               mvi->regs = ioremap(res_start, res_len);
-       else
-               mvi->regs = ioremap_nocache(res_start, res_len);
-
-       if (!mvi->regs)
-               goto err_out;
-
-       /*
-        * alloc and init our DMA areas
-        */
-
-       mvi->tx = dma_alloc_coherent(&pdev->dev,
-                                    sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ,
-                                    &mvi->tx_dma, GFP_KERNEL);
-       if (!mvi->tx)
-               goto err_out;
-       memset(mvi->tx, 0, sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ);
-
-       mvi->rx_fis = dma_alloc_coherent(&pdev->dev, MVS_RX_FISL_SZ,
-                                        &mvi->rx_fis_dma, GFP_KERNEL);
-       if (!mvi->rx_fis)
-               goto err_out;
-       memset(mvi->rx_fis, 0, MVS_RX_FISL_SZ);
-
-       mvi->rx = dma_alloc_coherent(&pdev->dev,
-                                    sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1),
-                                    &mvi->rx_dma, GFP_KERNEL);
-       if (!mvi->rx)
-               goto err_out;
-       memset(mvi->rx, 0, sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1));
-
-       mvi->rx[0] = cpu_to_le32(0xfff);
-       mvi->rx_cons = 0xfff;
-
-       mvi->slot = dma_alloc_coherent(&pdev->dev,
-                                      sizeof(*mvi->slot) * MVS_SLOTS,
-                                      &mvi->slot_dma, GFP_KERNEL);
-       if (!mvi->slot)
-               goto err_out;
-       memset(mvi->slot, 0, sizeof(*mvi->slot) * MVS_SLOTS);
-
-       for (i = 0; i < MVS_SLOTS; i++) {
-               struct mvs_slot_info *slot = &mvi->slot_info[i];
-
-               slot->buf = dma_alloc_coherent(&pdev->dev, MVS_SLOT_BUF_SZ,
-                                              &slot->buf_dma, GFP_KERNEL);
-               if (!slot->buf)
-                       goto err_out;
-               memset(slot->buf, 0, MVS_SLOT_BUF_SZ);
-       }
-
-       /* finally, read NVRAM to get our SAS address */
-       if (mvs_nvram_read(mvi, NVR_SAS_ADDR, &mvi->sas_addr, 8))
-               goto err_out;
-       return mvi;
-
-err_out:
-       mvs_free(mvi);
-       return NULL;
-}
-
-static u32 mvs_cr32(void __iomem *regs, u32 addr)
-{
-       mw32(CMD_ADDR, addr);
-       return mr32(CMD_DATA);
-}
-
-static void mvs_cw32(void __iomem *regs, u32 addr, u32 val)
-{
-       mw32(CMD_ADDR, addr);
-       mw32(CMD_DATA, val);
-}
-
-static u32 mvs_read_phy_ctl(struct mvs_info *mvi, u32 port)
-{
-       void __iomem *regs = mvi->regs;
-       return (port < 4)?mr32(P0_SER_CTLSTAT + port * 4):
-               mr32(P4_SER_CTLSTAT + (port - 4) * 4);
-}
-
-static void mvs_write_phy_ctl(struct mvs_info *mvi, u32 port, u32 val)
-{
-       void __iomem *regs = mvi->regs;
-       if (port < 4)
-               mw32(P0_SER_CTLSTAT + port * 4, val);
-       else
-               mw32(P4_SER_CTLSTAT + (port - 4) * 4, val);
-}
-
-static u32 mvs_read_port(struct mvs_info *mvi, u32 off, u32 off2, u32 port)
-{
-       void __iomem *regs = mvi->regs + off;
-       void __iomem *regs2 = mvi->regs + off2;
-       return (port < 4)?readl(regs + port * 8):
-               readl(regs2 + (port - 4) * 8);
-}
-
-static void mvs_write_port(struct mvs_info *mvi, u32 off, u32 off2,
-                               u32 port, u32 val)
-{
-       void __iomem *regs = mvi->regs + off;
-       void __iomem *regs2 = mvi->regs + off2;
-       if (port < 4)
-               writel(val, regs + port * 8);
-       else
-               writel(val, regs2 + (port - 4) * 8);
-}
-
-static u32 mvs_read_port_cfg_data(struct mvs_info *mvi, u32 port)
-{
-       return mvs_read_port(mvi, MVS_P0_CFG_DATA, MVS_P4_CFG_DATA, port);
-}
-
-static void mvs_write_port_cfg_data(struct mvs_info *mvi, u32 port, u32 val)
-{
-       mvs_write_port(mvi, MVS_P0_CFG_DATA, MVS_P4_CFG_DATA, port, val);
-}
-
-static void mvs_write_port_cfg_addr(struct mvs_info *mvi, u32 port, u32 addr)
-{
-       mvs_write_port(mvi, MVS_P0_CFG_ADDR, MVS_P4_CFG_ADDR, port, addr);
-}
-
-static u32 mvs_read_port_vsr_data(struct mvs_info *mvi, u32 port)
-{
-       return mvs_read_port(mvi, MVS_P0_VSR_DATA, MVS_P4_VSR_DATA, port);
-}
-
-static void mvs_write_port_vsr_data(struct mvs_info *mvi, u32 port, u32 val)
-{
-       mvs_write_port(mvi, MVS_P0_VSR_DATA, MVS_P4_VSR_DATA, port, val);
-}
-
-static void mvs_write_port_vsr_addr(struct mvs_info *mvi, u32 port, u32 addr)
-{
-       mvs_write_port(mvi, MVS_P0_VSR_ADDR, MVS_P4_VSR_ADDR, port, addr);
-}
-
-static u32 mvs_read_port_irq_stat(struct mvs_info *mvi, u32 port)
-{
-       return mvs_read_port(mvi, MVS_P0_INT_STAT, MVS_P4_INT_STAT, port);
-}
-
-static void mvs_write_port_irq_stat(struct mvs_info *mvi, u32 port, u32 val)
-{
-       mvs_write_port(mvi, MVS_P0_INT_STAT, MVS_P4_INT_STAT, port, val);
-}
-
-static u32 mvs_read_port_irq_mask(struct mvs_info *mvi, u32 port)
-{
-       return mvs_read_port(mvi, MVS_P0_INT_MASK, MVS_P4_INT_MASK, port);
-}
-
-static void mvs_write_port_irq_mask(struct mvs_info *mvi, u32 port, u32 val)
-{
-       mvs_write_port(mvi, MVS_P0_INT_MASK, MVS_P4_INT_MASK, port, val);
-}
-
-static void __devinit mvs_phy_hacks(struct mvs_info *mvi)
-{
-       void __iomem *regs = mvi->regs;
-       u32 tmp;
-
-       /* workaround for SATA R-ERR, to ignore phy glitch */
-       tmp = mvs_cr32(regs, CMD_PHY_TIMER);
-       tmp &= ~(1 << 9);
-       tmp |= (1 << 10);
-       mvs_cw32(regs, CMD_PHY_TIMER, tmp);
-
-       /* enable retry 127 times */
-       mvs_cw32(regs, CMD_SAS_CTL1, 0x7f7f);
-
-       /* extend open frame timeout to max */
-       tmp = mvs_cr32(regs, CMD_SAS_CTL0);
-       tmp &= ~0xffff;
-       tmp |= 0x3fff;
-       mvs_cw32(regs, CMD_SAS_CTL0, tmp);
-
-       /* workaround for WDTIMEOUT , set to 550 ms */
-       mvs_cw32(regs, CMD_WD_TIMER, 0x86470);
-
-       /* not to halt for different port op during wideport link change */
-       mvs_cw32(regs, CMD_APP_ERR_CONFIG, 0xffefbf7d);
-
-       /* workaround for Seagate disk not-found OOB sequence, recv
-        * COMINIT before sending out COMWAKE */
-       tmp = mvs_cr32(regs, CMD_PHY_MODE_21);
-       tmp &= 0x0000ffff;
-       tmp |= 0x00fa0000;
-       mvs_cw32(regs, CMD_PHY_MODE_21, tmp);
-
-       tmp = mvs_cr32(regs, CMD_PHY_TIMER);
-       tmp &= 0x1fffffff;
-       tmp |= (2U << 29);      /* 8 ms retry */
-       mvs_cw32(regs, CMD_PHY_TIMER, tmp);
-
-       /* TEST - for phy decoding error, adjust voltage levels */
-       mw32(P0_VSR_ADDR + 0, 0x8);
-       mw32(P0_VSR_DATA + 0, 0x2F0);
-
-       mw32(P0_VSR_ADDR + 8, 0x8);
-       mw32(P0_VSR_DATA + 8, 0x2F0);
-
-       mw32(P0_VSR_ADDR + 16, 0x8);
-       mw32(P0_VSR_DATA + 16, 0x2F0);
-
-       mw32(P0_VSR_ADDR + 24, 0x8);
-       mw32(P0_VSR_DATA + 24, 0x2F0);
-
-}
-
-static void mvs_enable_xmt(struct mvs_info *mvi, int PhyId)
-{
-       void __iomem *regs = mvi->regs;
-       u32 tmp;
-
-       tmp = mr32(PCS);
-       if (mvi->chip->n_phy <= 4)
-               tmp |= 1 << (PhyId + PCS_EN_PORT_XMT_SHIFT);
-       else
-               tmp |= 1 << (PhyId + PCS_EN_PORT_XMT_SHIFT2);
-       mw32(PCS, tmp);
-}
-
-static void mvs_detect_porttype(struct mvs_info *mvi, int i)
-{
-       void __iomem *regs = mvi->regs;
-       u32 reg;
-       struct mvs_phy *phy = &mvi->phy[i];
-
-       /* TODO check & save device type */
-       reg = mr32(GBL_PORT_TYPE);
-
-       if (reg & MODE_SAS_SATA & (1 << i))
-               phy->phy_type |= PORT_TYPE_SAS;
-       else
-               phy->phy_type |= PORT_TYPE_SATA;
-}
-
-static void *mvs_get_d2h_reg(struct mvs_info *mvi, int i, void *buf)
-{
-       u32 *s = (u32 *) buf;
-
-       if (!s)
-               return NULL;
-
-       mvs_write_port_cfg_addr(mvi, i, PHYR_SATA_SIG3);
-       s[3] = mvs_read_port_cfg_data(mvi, i);
-
-       mvs_write_port_cfg_addr(mvi, i, PHYR_SATA_SIG2);
-       s[2] = mvs_read_port_cfg_data(mvi, i);
-
-       mvs_write_port_cfg_addr(mvi, i, PHYR_SATA_SIG1);
-       s[1] = mvs_read_port_cfg_data(mvi, i);
-
-       mvs_write_port_cfg_addr(mvi, i, PHYR_SATA_SIG0);
-       s[0] = mvs_read_port_cfg_data(mvi, i);
-
-       return (void *)s;
-}
-
-static u32 mvs_is_sig_fis_received(u32 irq_status)
-{
-       return irq_status & PHYEV_SIG_FIS;
-}
-
-static void mvs_update_wideport(struct mvs_info *mvi, int i)
-{
-       struct mvs_phy *phy = &mvi->phy[i];
-       struct mvs_port *port = phy->port;
-       int j, no;
-
-       for_each_phy(port->wide_port_phymap, no, j, mvi->chip->n_phy)
-               if (no & 1) {
-                       mvs_write_port_cfg_addr(mvi, no, PHYR_WIDE_PORT);
-                       mvs_write_port_cfg_data(mvi, no,
-                                               port->wide_port_phymap);
-               } else {
-                       mvs_write_port_cfg_addr(mvi, no, PHYR_WIDE_PORT);
-                       mvs_write_port_cfg_data(mvi, no, 0);
-               }
-}
-
-static u32 mvs_is_phy_ready(struct mvs_info *mvi, int i)
-{
-       u32 tmp;
-       struct mvs_phy *phy = &mvi->phy[i];
-       struct mvs_port *port = phy->port;;
-
-       tmp = mvs_read_phy_ctl(mvi, i);
-
-       if ((tmp & PHY_READY_MASK) && !(phy->irq_status & PHYEV_POOF)) {
-               if (!port)
-                       phy->phy_attached = 1;
-               return tmp;
-       }
-
-       if (port) {
-               if (phy->phy_type & PORT_TYPE_SAS) {
-                       port->wide_port_phymap &= ~(1U << i);
-                       if (!port->wide_port_phymap)
-                               port->port_attached = 0;
-                       mvs_update_wideport(mvi, i);
-               } else if (phy->phy_type & PORT_TYPE_SATA)
-                       port->port_attached = 0;
-               mvs_free_reg_set(mvi, phy->port);
-               phy->port = NULL;
-               phy->phy_attached = 0;
-               phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
-       }
-       return 0;
-}
-
-static void mvs_update_phyinfo(struct mvs_info *mvi, int i,
-                                       int get_st)
-{
-       struct mvs_phy *phy = &mvi->phy[i];
-       struct pci_dev *pdev = mvi->pdev;
-       u32 tmp;
-       u64 tmp64;
-
-       mvs_write_port_cfg_addr(mvi, i, PHYR_IDENTIFY);
-       phy->dev_info = mvs_read_port_cfg_data(mvi, i);
-
-       mvs_write_port_cfg_addr(mvi, i, PHYR_ADDR_HI);
-       phy->dev_sas_addr = (u64) mvs_read_port_cfg_data(mvi, i) << 32;
-
-       mvs_write_port_cfg_addr(mvi, i, PHYR_ADDR_LO);
-       phy->dev_sas_addr |= mvs_read_port_cfg_data(mvi, i);
-
-       if (get_st) {
-               phy->irq_status = mvs_read_port_irq_stat(mvi, i);
-               phy->phy_status = mvs_is_phy_ready(mvi, i);
-       }
-
-       if (phy->phy_status) {
-               u32 phy_st;
-               struct asd_sas_phy *sas_phy = mvi->sas.sas_phy[i];
-
-               mvs_write_port_cfg_addr(mvi, i, PHYR_PHY_STAT);
-               phy_st = mvs_read_port_cfg_data(mvi, i);
-
-               sas_phy->linkrate =
-                       (phy->phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK) >>
-                               PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET;
-               phy->minimum_linkrate =
-                       (phy->phy_status &
-                               PHY_MIN_SPP_PHYS_LINK_RATE_MASK) >> 8;
-               phy->maximum_linkrate =
-                       (phy->phy_status &
-                               PHY_MAX_SPP_PHYS_LINK_RATE_MASK) >> 12;
-
-               if (phy->phy_type & PORT_TYPE_SAS) {
-                       /* Updated attached_sas_addr */
-                       mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_ADDR_HI);
-                       phy->att_dev_sas_addr =
-                               (u64) mvs_read_port_cfg_data(mvi, i) << 32;
-                       mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_ADDR_LO);
-                       phy->att_dev_sas_addr |= mvs_read_port_cfg_data(mvi, i);
-                       mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_DEV_INFO);
-                       phy->att_dev_info = mvs_read_port_cfg_data(mvi, i);
-                       phy->identify.device_type =
-                           phy->att_dev_info & PORT_DEV_TYPE_MASK;
-
-                       if (phy->identify.device_type == SAS_END_DEV)
-                               phy->identify.target_port_protocols =
-                                                       SAS_PROTOCOL_SSP;
-                       else if (phy->identify.device_type != NO_DEVICE)
-                               phy->identify.target_port_protocols =
-                                                       SAS_PROTOCOL_SMP;
-                       if (phy_st & PHY_OOB_DTCTD)
-                               sas_phy->oob_mode = SAS_OOB_MODE;
-                       phy->frame_rcvd_size =
-                           sizeof(struct sas_identify_frame);
-               } else if (phy->phy_type & PORT_TYPE_SATA) {
-                       phy->identify.target_port_protocols = SAS_PROTOCOL_STP;
-                       if (mvs_is_sig_fis_received(phy->irq_status)) {
-                               phy->att_dev_sas_addr = i;      /* temp */
-                               if (phy_st & PHY_OOB_DTCTD)
-                                       sas_phy->oob_mode = SATA_OOB_MODE;
-                               phy->frame_rcvd_size =
-                                   sizeof(struct dev_to_host_fis);
-                               mvs_get_d2h_reg(mvi, i,
-                                               (void *)sas_phy->frame_rcvd);
-                       } else {
-                               dev_printk(KERN_DEBUG, &pdev->dev,
-                                       "No sig fis\n");
-                               phy->phy_type &= ~(PORT_TYPE_SATA);
-                               goto out_done;
-                       }
-               }
-               tmp64 = cpu_to_be64(phy->att_dev_sas_addr);
-               memcpy(sas_phy->attached_sas_addr, &tmp64, SAS_ADDR_SIZE);
-
-               dev_printk(KERN_DEBUG, &pdev->dev,
-                       "phy[%d] Get Attached Address 0x%llX ,"
-                       " SAS Address 0x%llX\n",
-                       i,
-                       (unsigned long long)phy->att_dev_sas_addr,
-                       (unsigned long long)phy->dev_sas_addr);
-               dev_printk(KERN_DEBUG, &pdev->dev,
-                       "Rate = %x , type = %d\n",
-                       sas_phy->linkrate, phy->phy_type);
-
-               /* workaround for HW phy decoding error on 1.5g disk drive */
-               mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE6);
-               tmp = mvs_read_port_vsr_data(mvi, i);
-               if (((phy->phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK) >>
-                    PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET) ==
-                       SAS_LINK_RATE_1_5_GBPS)
-                       tmp &= ~PHY_MODE6_LATECLK;
-               else
-                       tmp |= PHY_MODE6_LATECLK;
-               mvs_write_port_vsr_data(mvi, i, tmp);
-
-       }
-out_done:
-       if (get_st)
-               mvs_write_port_irq_stat(mvi, i, phy->irq_status);
-}
-
-static void mvs_port_formed(struct asd_sas_phy *sas_phy)
-{
-       struct sas_ha_struct *sas_ha = sas_phy->ha;
-       struct mvs_info *mvi = sas_ha->lldd_ha;
-       struct asd_sas_port *sas_port = sas_phy->port;
-       struct mvs_phy *phy = sas_phy->lldd_phy;
-       struct mvs_port *port = &mvi->port[sas_port->id];
-       unsigned long flags;
-
-       spin_lock_irqsave(&mvi->lock, flags);
-       port->port_attached = 1;
-       phy->port = port;
-       port->taskfileset = MVS_ID_NOT_MAPPED;
-       if (phy->phy_type & PORT_TYPE_SAS) {
-               port->wide_port_phymap = sas_port->phy_mask;
-               mvs_update_wideport(mvi, sas_phy->id);
-       }
-       spin_unlock_irqrestore(&mvi->lock, flags);
-}
-
-static int mvs_I_T_nexus_reset(struct domain_device *dev)
-{
-       return TMF_RESP_FUNC_FAILED;
-}
-
-static int __devinit mvs_hw_init(struct mvs_info *mvi)
-{
-       void __iomem *regs = mvi->regs;
-       int i;
-       u32 tmp, cctl;
-
-       /* make sure interrupts are masked immediately (paranoia) */
-       mw32(GBL_CTL, 0);
-       tmp = mr32(GBL_CTL);
-
-       /* Reset Controller */
-       if (!(tmp & HBA_RST)) {
-               if (mvi->flags & MVF_PHY_PWR_FIX) {
-                       pci_read_config_dword(mvi->pdev, PCR_PHY_CTL, &tmp);
-                       tmp &= ~PCTL_PWR_ON;
-                       tmp |= PCTL_OFF;
-                       pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, tmp);
-
-                       pci_read_config_dword(mvi->pdev, PCR_PHY_CTL2, &tmp);
-                       tmp &= ~PCTL_PWR_ON;
-                       tmp |= PCTL_OFF;
-                       pci_write_config_dword(mvi->pdev, PCR_PHY_CTL2, tmp);
-               }
-
-               /* global reset, incl. COMRESET/H_RESET_N (self-clearing) */
-               mw32_f(GBL_CTL, HBA_RST);
-       }
-
-       /* wait for reset to finish; timeout is just a guess */
-       i = 1000;
-       while (i-- > 0) {
-               msleep(10);
-
-               if (!(mr32(GBL_CTL) & HBA_RST))
-                       break;
-       }
-       if (mr32(GBL_CTL) & HBA_RST) {
-               dev_printk(KERN_ERR, &mvi->pdev->dev, "HBA reset failed\n");
-               return -EBUSY;
-       }
-
-       /* Init Chip */
-       /* make sure RST is set; HBA_RST /should/ have done that for us */
-       cctl = mr32(CTL);
-       if (cctl & CCTL_RST)
-               cctl &= ~CCTL_RST;
-       else
-               mw32_f(CTL, cctl | CCTL_RST);
-
-       /* write to device control _AND_ device status register? - A.C. */
-       pci_read_config_dword(mvi->pdev, PCR_DEV_CTRL, &tmp);
-       tmp &= ~PRD_REQ_MASK;
-       tmp |= PRD_REQ_SIZE;
-       pci_write_config_dword(mvi->pdev, PCR_DEV_CTRL, tmp);
-
-       pci_read_config_dword(mvi->pdev, PCR_PHY_CTL, &tmp);
-       tmp |= PCTL_PWR_ON;
-       tmp &= ~PCTL_OFF;
-       pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, tmp);
-
-       pci_read_config_dword(mvi->pdev, PCR_PHY_CTL2, &tmp);
-       tmp |= PCTL_PWR_ON;
-       tmp &= ~PCTL_OFF;
-       pci_write_config_dword(mvi->pdev, PCR_PHY_CTL2, tmp);
-
-       mw32_f(CTL, cctl);
-
-       /* reset control */
-       mw32(PCS, 0);           /*MVS_PCS */
-
-       mvs_phy_hacks(mvi);
-
-       mw32(CMD_LIST_LO, mvi->slot_dma);
-       mw32(CMD_LIST_HI, (mvi->slot_dma >> 16) >> 16);
-
-       mw32(RX_FIS_LO, mvi->rx_fis_dma);
-       mw32(RX_FIS_HI, (mvi->rx_fis_dma >> 16) >> 16);
-
-       mw32(TX_CFG, MVS_CHIP_SLOT_SZ);
-       mw32(TX_LO, mvi->tx_dma);
-       mw32(TX_HI, (mvi->tx_dma >> 16) >> 16);
-
-       mw32(RX_CFG, MVS_RX_RING_SZ);
-       mw32(RX_LO, mvi->rx_dma);
-       mw32(RX_HI, (mvi->rx_dma >> 16) >> 16);
-
-       /* enable auto port detection */
-       mw32(GBL_PORT_TYPE, MODE_AUTO_DET_EN);
-       msleep(1100);
-       /* init and reset phys */
-       for (i = 0; i < mvi->chip->n_phy; i++) {
-               u32 lo = be32_to_cpu(*(u32 *)&mvi->sas_addr[4]);
-               u32 hi = be32_to_cpu(*(u32 *)&mvi->sas_addr[0]);
-
-               mvs_detect_porttype(mvi, i);
-
-               /* set phy local SAS address */
-               mvs_write_port_cfg_addr(mvi, i, PHYR_ADDR_LO);
-               mvs_write_port_cfg_data(mvi, i, lo);
-               mvs_write_port_cfg_addr(mvi, i, PHYR_ADDR_HI);
-               mvs_write_port_cfg_data(mvi, i, hi);
-
-               /* reset phy */
-               tmp = mvs_read_phy_ctl(mvi, i);
-               tmp |= PHY_RST;
-               mvs_write_phy_ctl(mvi, i, tmp);
-       }
-
-       msleep(100);
-
-       for (i = 0; i < mvi->chip->n_phy; i++) {
-               /* clear phy int status */
-               tmp = mvs_read_port_irq_stat(mvi, i);
-               tmp &= ~PHYEV_SIG_FIS;
-               mvs_write_port_irq_stat(mvi, i, tmp);
-
-               /* set phy int mask */
-               tmp = PHYEV_RDY_CH | PHYEV_BROAD_CH | PHYEV_UNASSOC_FIS |
-                       PHYEV_ID_DONE | PHYEV_DEC_ERR;
-               mvs_write_port_irq_mask(mvi, i, tmp);
-
-               msleep(100);
-               mvs_update_phyinfo(mvi, i, 1);
-               mvs_enable_xmt(mvi, i);
-       }
-
-       /* FIXME: update wide port bitmaps */
-
-       /* little endian for open address and command table, etc. */
-       /* A.C.
-        * it seems that ( from the spec ) turning on big-endian won't
-        * do us any good on big-endian machines, need further confirmation
-        */
-       cctl = mr32(CTL);
-       cctl |= CCTL_ENDIAN_CMD;
-       cctl |= CCTL_ENDIAN_DATA;
-       cctl &= ~CCTL_ENDIAN_OPEN;
-       cctl |= CCTL_ENDIAN_RSP;
-       mw32_f(CTL, cctl);
-
-       /* reset CMD queue */
-       tmp = mr32(PCS);
-       tmp |= PCS_CMD_RST;
-       mw32(PCS, tmp);
-       /* interrupt coalescing may cause missing HW interrput in some case,
-        * and the max count is 0x1ff, while our max slot is 0x200,
-        * it will make count 0.
-        */
-       tmp = 0;
-       mw32(INT_COAL, tmp);
-
-       tmp = 0x100;
-       mw32(INT_COAL_TMOUT, tmp);
-
-       /* ladies and gentlemen, start your engines */
-       mw32(TX_CFG, 0);
-       mw32(TX_CFG, MVS_CHIP_SLOT_SZ | TX_EN);
-       mw32(RX_CFG, MVS_RX_RING_SZ | RX_EN);
-       /* enable CMD/CMPL_Q/RESP mode */
-       mw32(PCS, PCS_SATA_RETRY | PCS_FIS_RX_EN | PCS_CMD_EN);
-
-       /* enable completion queue interrupt */
-       tmp = (CINT_PORT_MASK | CINT_DONE | CINT_MEM | CINT_SRS);
-       mw32(INT_MASK, tmp);
-
-       /* Enable SRS interrupt */
-       mw32(INT_MASK_SRS, 0xFF);
-       return 0;
-}
-
-static void __devinit mvs_print_info(struct mvs_info *mvi)
-{
-       struct pci_dev *pdev = mvi->pdev;
-       static int printed_version;
-
-       if (!printed_version++)
-               dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n");
-
-       dev_printk(KERN_INFO, &pdev->dev, "%u phys, addr %llx\n",
-                  mvi->chip->n_phy, SAS_ADDR(mvi->sas_addr));
-}
-
-static int __devinit mvs_pci_init(struct pci_dev *pdev,
-                                 const struct pci_device_id *ent)
-{
-       int rc;
-       struct mvs_info *mvi;
-       irq_handler_t irq_handler = mvs_interrupt;
-
-       rc = pci_enable_device(pdev);
-       if (rc)
-               return rc;
-
-       pci_set_master(pdev);
-
-       rc = pci_request_regions(pdev, DRV_NAME);
-       if (rc)
-               goto err_out_disable;
-
-       rc = pci_go_64(pdev);
-       if (rc)
-               goto err_out_regions;
-
-       mvi = mvs_alloc(pdev, ent);
-       if (!mvi) {
-               rc = -ENOMEM;
-               goto err_out_regions;
-       }
-
-       rc = mvs_hw_init(mvi);
-       if (rc)
-               goto err_out_mvi;
-
-#ifndef MVS_DISABLE_MSI
-       if (!pci_enable_msi(pdev)) {
-               u32 tmp;
-               void __iomem *regs = mvi->regs;
-               mvi->flags |= MVF_MSI;
-               irq_handler = mvs_msi_interrupt;
-               tmp = mr32(PCS);
-               mw32(PCS, tmp | PCS_SELF_CLEAR);
-       }
-#endif
-
-       rc = request_irq(pdev->irq, irq_handler, IRQF_SHARED, DRV_NAME, mvi);
-       if (rc)
-               goto err_out_msi;
-
-       rc = scsi_add_host(mvi->shost, &pdev->dev);
-       if (rc)
-               goto err_out_irq;
-
-       rc = sas_register_ha(&mvi->sas);
-       if (rc)
-               goto err_out_shost;
-
-       pci_set_drvdata(pdev, mvi);
-
-       mvs_print_info(mvi);
-
-       mvs_hba_interrupt_enable(mvi);
-
-       scsi_scan_host(mvi->shost);
-
-       return 0;
-
-err_out_shost:
-       scsi_remove_host(mvi->shost);
-err_out_irq:
-       free_irq(pdev->irq, mvi);
-err_out_msi:
-       if (mvi->flags |= MVF_MSI)
-               pci_disable_msi(pdev);
-err_out_mvi:
-       mvs_free(mvi);
-err_out_regions:
-       pci_release_regions(pdev);
-err_out_disable:
-       pci_disable_device(pdev);
-       return rc;
-}
-
-static void __devexit mvs_pci_remove(struct pci_dev *pdev)
-{
-       struct mvs_info *mvi = pci_get_drvdata(pdev);
-
-       pci_set_drvdata(pdev, NULL);
-
-       if (mvi) {
-               sas_unregister_ha(&mvi->sas);
-               mvs_hba_interrupt_disable(mvi);
-               sas_remove_host(mvi->shost);
-               scsi_remove_host(mvi->shost);
-
-               free_irq(pdev->irq, mvi);
-               if (mvi->flags & MVF_MSI)
-                       pci_disable_msi(pdev);
-               mvs_free(mvi);
-               pci_release_regions(pdev);
-       }
-       pci_disable_device(pdev);
-}
-
-static struct sas_domain_function_template mvs_transport_ops = {
-       .lldd_execute_task      = mvs_task_exec,
-       .lldd_control_phy       = mvs_phy_control,
-       .lldd_abort_task        = mvs_task_abort,
-       .lldd_port_formed       = mvs_port_formed,
-       .lldd_I_T_nexus_reset   = mvs_I_T_nexus_reset,
-};
-
-static struct pci_device_id __devinitdata mvs_pci_table[] = {
-       { PCI_VDEVICE(MARVELL, 0x6320), chip_6320 },
-       { PCI_VDEVICE(MARVELL, 0x6340), chip_6440 },
-       {
-               .vendor         = PCI_VENDOR_ID_MARVELL,
-               .device         = 0x6440,
-               .subvendor      = PCI_ANY_ID,
-               .subdevice      = 0x6480,
-               .class          = 0,
-               .class_mask     = 0,
-               .driver_data    = chip_6480,
-       },
-       { PCI_VDEVICE(MARVELL, 0x6440), chip_6440 },
-       { PCI_VDEVICE(MARVELL, 0x6480), chip_6480 },
-
-       { }     /* terminate list */
-};
-
-static struct pci_driver mvs_pci_driver = {
-       .name           = DRV_NAME,
-       .id_table       = mvs_pci_table,
-       .probe          = mvs_pci_init,
-       .remove         = __devexit_p(mvs_pci_remove),
-};
-
-static int __init mvs_init(void)
-{
-       int rc;
-
-       mvs_stt = sas_domain_attach_transport(&mvs_transport_ops);
-       if (!mvs_stt)
-               return -ENOMEM;
-
-       rc = pci_register_driver(&mvs_pci_driver);
-       if (rc)
-               goto err_out;
-
-       return 0;
-
-err_out:
-       sas_release_transport(mvs_stt);
-       return rc;
-}
-
-static void __exit mvs_exit(void)
-{
-       pci_unregister_driver(&mvs_pci_driver);
-       sas_release_transport(mvs_stt);
-}
-
-module_init(mvs_init);
-module_exit(mvs_exit);
-
-MODULE_AUTHOR("Jeff Garzik <jgarzik@pobox.com>");
-MODULE_DESCRIPTION("Marvell 88SE6440 SAS/SATA controller driver");
-MODULE_VERSION(DRV_VERSION);
-MODULE_LICENSE("GPL");
-MODULE_DEVICE_TABLE(pci, mvs_pci_table);
diff --git a/drivers/scsi/mvsas/Kconfig b/drivers/scsi/mvsas/Kconfig
new file mode 100644 (file)
index 0000000..6de7af2
--- /dev/null
@@ -0,0 +1,42 @@
+#
+# Kernel configuration file for 88SE64XX/88SE94XX SAS/SATA driver.
+#
+# Copyright 2007 Red Hat, Inc.
+# Copyright 2008 Marvell. <kewei@marvell.com>
+#
+# This file is licensed under GPLv2.
+#
+# This file is part of the 88SE64XX/88SE94XX driver.
+#
+# The 88SE64XX/88SE94XX 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; version 2 of the
+# License.
+#
+# The 88SE64XX/88SE94XX 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 88SE64XX/88SE94XX Driver; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+#
+#
+
+config SCSI_MVSAS
+       tristate "Marvell 88SE64XX/88SE94XX SAS/SATA support"
+       depends on PCI
+       select SCSI_SAS_LIBSAS
+       select FW_LOADER
+       help
+               This driver supports Marvell's SAS/SATA 3Gb/s PCI-E 88SE64XX and 6Gb/s
+               PCI-E 88SE94XX chip based host adapters.
+
+config SCSI_MVSAS_DEBUG
+       bool "Compile in debug mode"
+       default y
+       depends on SCSI_MVSAS
+       help
+               Compiles the 88SE64XX/88SE94XX driver in debug mode.  In debug mode,
+               the driver prints some messages to the console.
diff --git a/drivers/scsi/mvsas/Makefile b/drivers/scsi/mvsas/Makefile
new file mode 100644 (file)
index 0000000..52ac426
--- /dev/null
@@ -0,0 +1,32 @@
+#
+# Makefile for Marvell 88SE64xx/88SE84xx SAS/SATA driver.
+#
+# Copyright 2007 Red Hat, Inc.
+# Copyright 2008 Marvell. <kewei@marvell.com>
+#
+# This file is licensed under GPLv2.
+#
+# This program is free software; 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.
+#
+# 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
+
+ifeq ($(CONFIG_SCSI_MVSAS_DEBUG),y)
+       EXTRA_CFLAGS += -DMV_DEBUG
+endif
+
+obj-$(CONFIG_SCSI_MVSAS) += mvsas.o
+mvsas-y +=  mv_init.o  \
+           mv_sas.o   \
+           mv_64xx.o  \
+           mv_94xx.o
diff --git a/drivers/scsi/mvsas/mv_64xx.c b/drivers/scsi/mvsas/mv_64xx.c
new file mode 100644 (file)
index 0000000..10a5077
--- /dev/null
@@ -0,0 +1,793 @@
+/*
+ * Marvell 88SE64xx hardware specific
+ *
+ * Copyright 2007 Red Hat, Inc.
+ * Copyright 2008 Marvell. <kewei@marvell.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This program is free software; 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.
+ *
+ * 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 "mv_sas.h"
+#include "mv_64xx.h"
+#include "mv_chips.h"
+
+static void mvs_64xx_detect_porttype(struct mvs_info *mvi, int i)
+{
+       void __iomem *regs = mvi->regs;
+       u32 reg;
+       struct mvs_phy *phy = &mvi->phy[i];
+
+       /* TODO check & save device type */
+       reg = mr32(MVS_GBL_PORT_TYPE);
+       phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
+       if (reg & MODE_SAS_SATA & (1 << i))
+               phy->phy_type |= PORT_TYPE_SAS;
+       else
+               phy->phy_type |= PORT_TYPE_SATA;
+}
+
+static void __devinit mvs_64xx_enable_xmt(struct mvs_info *mvi, int phy_id)
+{
+       void __iomem *regs = mvi->regs;
+       u32 tmp;
+
+       tmp = mr32(MVS_PCS);
+       if (mvi->chip->n_phy <= 4)
+               tmp |= 1 << (phy_id + PCS_EN_PORT_XMT_SHIFT);
+       else
+               tmp |= 1 << (phy_id + PCS_EN_PORT_XMT_SHIFT2);
+       mw32(MVS_PCS, tmp);
+}
+
+static void __devinit mvs_64xx_phy_hacks(struct mvs_info *mvi)
+{
+       void __iomem *regs = mvi->regs;
+
+       mvs_phy_hacks(mvi);
+
+       if (!(mvi->flags & MVF_FLAG_SOC)) {
+               /* TEST - for phy decoding error, adjust voltage levels */
+               mw32(MVS_P0_VSR_ADDR + 0, 0x8);
+               mw32(MVS_P0_VSR_DATA + 0, 0x2F0);
+
+               mw32(MVS_P0_VSR_ADDR + 8, 0x8);
+               mw32(MVS_P0_VSR_DATA + 8, 0x2F0);
+
+               mw32(MVS_P0_VSR_ADDR + 16, 0x8);
+               mw32(MVS_P0_VSR_DATA + 16, 0x2F0);
+
+               mw32(MVS_P0_VSR_ADDR + 24, 0x8);
+               mw32(MVS_P0_VSR_DATA + 24, 0x2F0);
+       } else {
+               int i;
+               /* disable auto port detection */
+               mw32(MVS_GBL_PORT_TYPE, 0);
+               for (i = 0; i < mvi->chip->n_phy; i++) {
+                       mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE7);
+                       mvs_write_port_vsr_data(mvi, i, 0x90000000);
+                       mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE9);
+                       mvs_write_port_vsr_data(mvi, i, 0x50f2);
+                       mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE11);
+                       mvs_write_port_vsr_data(mvi, i, 0x0e);
+               }
+       }
+}
+
+static void mvs_64xx_stp_reset(struct mvs_info *mvi, u32 phy_id)
+{
+       void __iomem *regs = mvi->regs;
+       u32 reg, tmp;
+
+       if (!(mvi->flags & MVF_FLAG_SOC)) {
+               if (phy_id < 4)
+                       pci_read_config_dword(mvi->pdev, PCR_PHY_CTL, &reg);
+               else
+                       pci_read_config_dword(mvi->pdev, PCR_PHY_CTL2, &reg);
+
+       } else
+               reg = mr32(MVS_PHY_CTL);
+
+       tmp = reg;
+       if (phy_id < 4)
+               tmp |= (1U << phy_id) << PCTL_LINK_OFFS;
+       else
+               tmp |= (1U << (phy_id - 4)) << PCTL_LINK_OFFS;
+
+       if (!(mvi->flags & MVF_FLAG_SOC)) {
+               if (phy_id < 4) {
+                       pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, tmp);
+                       mdelay(10);
+                       pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, reg);
+               } else {
+                       pci_write_config_dword(mvi->pdev, PCR_PHY_CTL2, tmp);
+                       mdelay(10);
+                       pci_write_config_dword(mvi->pdev, PCR_PHY_CTL2, reg);
+               }
+       } else {
+               mw32(MVS_PHY_CTL, tmp);
+               mdelay(10);
+               mw32(MVS_PHY_CTL, reg);
+       }
+}
+
+static void mvs_64xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard)
+{
+       u32 tmp;
+       tmp = mvs_read_port_irq_stat(mvi, phy_id);
+       tmp &= ~PHYEV_RDY_CH;
+       mvs_write_port_irq_stat(mvi, phy_id, tmp);
+       tmp = mvs_read_phy_ctl(mvi, phy_id);
+       if (hard)
+               tmp |= PHY_RST_HARD;
+       else
+               tmp |= PHY_RST;
+       mvs_write_phy_ctl(mvi, phy_id, tmp);
+       if (hard) {
+               do {
+                       tmp = mvs_read_phy_ctl(mvi, phy_id);
+               } while (tmp & PHY_RST_HARD);
+       }
+}
+
+static int __devinit mvs_64xx_chip_reset(struct mvs_info *mvi)
+{
+       void __iomem *regs = mvi->regs;
+       u32 tmp;
+       int i;
+
+       /* make sure interrupts are masked immediately (paranoia) */
+       mw32(MVS_GBL_CTL, 0);
+       tmp = mr32(MVS_GBL_CTL);
+
+       /* Reset Controller */
+       if (!(tmp & HBA_RST)) {
+               if (mvi->flags & MVF_PHY_PWR_FIX) {
+                       pci_read_config_dword(mvi->pdev, PCR_PHY_CTL, &tmp);
+                       tmp &= ~PCTL_PWR_OFF;
+                       tmp |= PCTL_PHY_DSBL;
+                       pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, tmp);
+
+                       pci_read_config_dword(mvi->pdev, PCR_PHY_CTL2, &tmp);
+                       tmp &= ~PCTL_PWR_OFF;
+                       tmp |= PCTL_PHY_DSBL;
+                       pci_write_config_dword(mvi->pdev, PCR_PHY_CTL2, tmp);
+               }
+       }
+
+       /* make sure interrupts are masked immediately (paranoia) */
+       mw32(MVS_GBL_CTL, 0);
+       tmp = mr32(MVS_GBL_CTL);
+
+       /* Reset Controller */
+       if (!(tmp & HBA_RST)) {
+               /* global reset, incl. COMRESET/H_RESET_N (self-clearing) */
+               mw32_f(MVS_GBL_CTL, HBA_RST);
+       }
+
+       /* wait for reset to finish; timeout is just a guess */
+       i = 1000;
+       while (i-- > 0) {
+               msleep(10);
+
+               if (!(mr32(MVS_GBL_CTL) & HBA_RST))
+                       break;
+       }
+       if (mr32(MVS_GBL_CTL) & HBA_RST) {
+               dev_printk(KERN_ERR, mvi->dev, "HBA reset failed\n");
+               return -EBUSY;
+       }
+       return 0;
+}
+
+static void mvs_64xx_phy_disable(struct mvs_info *mvi, u32 phy_id)
+{
+       void __iomem *regs = mvi->regs;
+       u32 tmp;
+       if (!(mvi->flags & MVF_FLAG_SOC)) {
+               u32 offs;
+               if (phy_id < 4)
+                       offs = PCR_PHY_CTL;
+               else {
+                       offs = PCR_PHY_CTL2;
+                       phy_id -= 4;
+               }
+               pci_read_config_dword(mvi->pdev, offs, &tmp);
+               tmp |= 1U << (PCTL_PHY_DSBL_OFFS + phy_id);
+               pci_write_config_dword(mvi->pdev, offs, tmp);
+       } else {
+               tmp = mr32(MVS_PHY_CTL);
+               tmp |= 1U << (PCTL_PHY_DSBL_OFFS + phy_id);
+               mw32(MVS_PHY_CTL, tmp);
+       }
+}
+
+static void mvs_64xx_phy_enable(struct mvs_info *mvi, u32 phy_id)
+{
+       void __iomem *regs = mvi->regs;
+       u32 tmp;
+       if (!(mvi->flags & MVF_FLAG_SOC)) {
+               u32 offs;
+               if (phy_id < 4)
+                       offs = PCR_PHY_CTL;
+               else {
+                       offs = PCR_PHY_CTL2;
+                       phy_id -= 4;
+               }
+               pci_read_config_dword(mvi->pdev, offs, &tmp);
+               tmp &= ~(1U << (PCTL_PHY_DSBL_OFFS + phy_id));
+               pci_write_config_dword(mvi->pdev, offs, tmp);
+       } else {
+               tmp = mr32(MVS_PHY_CTL);
+               tmp &= ~(1U << (PCTL_PHY_DSBL_OFFS + phy_id));
+               mw32(MVS_PHY_CTL, tmp);
+       }
+}
+
+static int __devinit mvs_64xx_init(struct mvs_info *mvi)
+{
+       void __iomem *regs = mvi->regs;
+       int i;
+       u32 tmp, cctl;
+
+       if (mvi->pdev && mvi->pdev->revision == 0)
+               mvi->flags |= MVF_PHY_PWR_FIX;
+       if (!(mvi->flags & MVF_FLAG_SOC)) {
+               mvs_show_pcie_usage(mvi);
+               tmp = mvs_64xx_chip_reset(mvi);
+               if (tmp)
+                       return tmp;
+       } else {
+               tmp = mr32(MVS_PHY_CTL);
+               tmp &= ~PCTL_PWR_OFF;
+               tmp |= PCTL_PHY_DSBL;
+               mw32(MVS_PHY_CTL, tmp);
+       }
+
+       /* Init Chip */
+       /* make sure RST is set; HBA_RST /should/ have done that for us */
+       cctl = mr32(MVS_CTL) & 0xFFFF;
+       if (cctl & CCTL_RST)
+               cctl &= ~CCTL_RST;
+       else
+               mw32_f(MVS_CTL, cctl | CCTL_RST);
+
+       if (!(mvi->flags & MVF_FLAG_SOC)) {
+               /* write to device control _AND_ device status register */
+               pci_read_config_dword(mvi->pdev, PCR_DEV_CTRL, &tmp);
+               tmp &= ~PRD_REQ_MASK;
+               tmp |= PRD_REQ_SIZE;
+               pci_write_config_dword(mvi->pdev, PCR_DEV_CTRL, tmp);
+
+               pci_read_config_dword(mvi->pdev, PCR_PHY_CTL, &tmp);
+               tmp &= ~PCTL_PWR_OFF;
+               tmp &= ~PCTL_PHY_DSBL;
+               pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, tmp);
+
+               pci_read_config_dword(mvi->pdev, PCR_PHY_CTL2, &tmp);
+               tmp &= PCTL_PWR_OFF;
+               tmp &= ~PCTL_PHY_DSBL;
+               pci_write_config_dword(mvi->pdev, PCR_PHY_CTL2, tmp);
+       } else {
+               tmp = mr32(MVS_PHY_CTL);
+               tmp &= ~PCTL_PWR_OFF;
+               tmp |= PCTL_COM_ON;
+               tmp &= ~PCTL_PHY_DSBL;
+               tmp |= PCTL_LINK_RST;
+               mw32(MVS_PHY_CTL, tmp);
+               msleep(100);
+               tmp &= ~PCTL_LINK_RST;
+               mw32(MVS_PHY_CTL, tmp);
+               msleep(100);
+       }
+
+       /* reset control */
+       mw32(MVS_PCS, 0);               /* MVS_PCS */
+       /* init phys */
+       mvs_64xx_phy_hacks(mvi);
+
+       /* enable auto port detection */
+       mw32(MVS_GBL_PORT_TYPE, MODE_AUTO_DET_EN);
+
+       mw32(MVS_CMD_LIST_LO, mvi->slot_dma);
+       mw32(MVS_CMD_LIST_HI, (mvi->slot_dma >> 16) >> 16);
+
+       mw32(MVS_RX_FIS_LO, mvi->rx_fis_dma);
+       mw32(MVS_RX_FIS_HI, (mvi->rx_fis_dma >> 16) >> 16);
+
+       mw32(MVS_TX_CFG, MVS_CHIP_SLOT_SZ);
+       mw32(MVS_TX_LO, mvi->tx_dma);
+       mw32(MVS_TX_HI, (mvi->tx_dma >> 16) >> 16);
+
+       mw32(MVS_RX_CFG, MVS_RX_RING_SZ);
+       mw32(MVS_RX_LO, mvi->rx_dma);
+       mw32(MVS_RX_HI, (mvi->rx_dma >> 16) >> 16);
+
+       for (i = 0; i < mvi->chip->n_phy; i++) {
+               /* set phy local SAS address */
+               /* should set little endian SAS address to 64xx chip */
+               mvs_set_sas_addr(mvi, i, PHYR_ADDR_LO, PHYR_ADDR_HI,
+                               cpu_to_be64(mvi->phy[i].dev_sas_addr));
+
+               mvs_64xx_enable_xmt(mvi, i);
+
+               mvs_64xx_phy_reset(mvi, i, 1);
+               msleep(500);
+               mvs_64xx_detect_porttype(mvi, i);
+       }
+       if (mvi->flags & MVF_FLAG_SOC) {
+               /* set select registers */
+               writel(0x0E008000, regs + 0x000);
+               writel(0x59000008, regs + 0x004);
+               writel(0x20, regs + 0x008);
+               writel(0x20, regs + 0x00c);
+               writel(0x20, regs + 0x010);
+               writel(0x20, regs + 0x014);
+               writel(0x20, regs + 0x018);
+               writel(0x20, regs + 0x01c);
+       }
+       for (i = 0; i < mvi->chip->n_phy; i++) {
+               /* clear phy int status */
+               tmp = mvs_read_port_irq_stat(mvi, i);
+               tmp &= ~PHYEV_SIG_FIS;
+               mvs_write_port_irq_stat(mvi, i, tmp);
+
+               /* set phy int mask */
+               tmp = PHYEV_RDY_CH | PHYEV_BROAD_CH | PHYEV_UNASSOC_FIS |
+                       PHYEV_ID_DONE | PHYEV_DCDR_ERR | PHYEV_CRC_ERR |
+                       PHYEV_DEC_ERR;
+               mvs_write_port_irq_mask(mvi, i, tmp);
+
+               msleep(100);
+               mvs_update_phyinfo(mvi, i, 1);
+       }
+
+       /* FIXME: update wide port bitmaps */
+
+       /* little endian for open address and command table, etc. */
+       /*
+        * it seems that ( from the spec ) turning on big-endian won't
+        * do us any good on big-endian machines, need further confirmation
+        */
+       cctl = mr32(MVS_CTL);
+       cctl |= CCTL_ENDIAN_CMD;
+       cctl |= CCTL_ENDIAN_DATA;
+       cctl &= ~CCTL_ENDIAN_OPEN;
+       cctl |= CCTL_ENDIAN_RSP;
+       mw32_f(MVS_CTL, cctl);
+
+       /* reset CMD queue */
+       tmp = mr32(MVS_PCS);
+       tmp |= PCS_CMD_RST;
+       mw32(MVS_PCS, tmp);
+       /* interrupt coalescing may cause missing HW interrput in some case,
+        * and the max count is 0x1ff, while our max slot is 0x200,
+        * it will make count 0.
+        */
+       tmp = 0;
+       mw32(MVS_INT_COAL, tmp);
+
+       tmp = 0x100;
+       mw32(MVS_INT_COAL_TMOUT, tmp);
+
+       /* ladies and gentlemen, start your engines */
+       mw32(MVS_TX_CFG, 0);
+       mw32(MVS_TX_CFG, MVS_CHIP_SLOT_SZ | TX_EN);
+       mw32(MVS_RX_CFG, MVS_RX_RING_SZ | RX_EN);
+       /* enable CMD/CMPL_Q/RESP mode */
+       mw32(MVS_PCS, PCS_SATA_RETRY | PCS_FIS_RX_EN |
+               PCS_CMD_EN | PCS_CMD_STOP_ERR);
+
+       /* enable completion queue interrupt */
+       tmp = (CINT_PORT_MASK | CINT_DONE | CINT_MEM | CINT_SRS | CINT_CI_STOP |
+               CINT_DMA_PCIE);
+
+       mw32(MVS_INT_MASK, tmp);
+
+       /* Enable SRS interrupt */
+       mw32(MVS_INT_MASK_SRS_0, 0xFFFF);
+
+       return 0;
+}
+
+static int mvs_64xx_ioremap(struct mvs_info *mvi)
+{
+       if (!mvs_ioremap(mvi, 4, 2))
+               return 0;
+       return -1;
+}
+
+static void mvs_64xx_iounmap(struct mvs_info *mvi)
+{
+       mvs_iounmap(mvi->regs);
+       mvs_iounmap(mvi->regs_ex);
+}
+
+static void mvs_64xx_interrupt_enable(struct mvs_info *mvi)
+{
+       void __iomem *regs = mvi->regs;
+       u32 tmp;
+
+       tmp = mr32(MVS_GBL_CTL);
+       mw32(MVS_GBL_CTL, tmp | INT_EN);
+}
+
+static void mvs_64xx_interrupt_disable(struct mvs_info *mvi)
+{
+       void __iomem *regs = mvi->regs;
+       u32 tmp;
+
+       tmp = mr32(MVS_GBL_CTL);
+       mw32(MVS_GBL_CTL, tmp & ~INT_EN);
+}
+
+static u32 mvs_64xx_isr_status(struct mvs_info *mvi, int irq)
+{
+       void __iomem *regs = mvi->regs;
+       u32 stat;
+
+       if (!(mvi->flags & MVF_FLAG_SOC)) {
+               stat = mr32(MVS_GBL_INT_STAT);
+
+               if (stat == 0 || stat == 0xffffffff)
+                       return 0;
+       } else
+               stat = 1;
+       return stat;
+}
+
+static irqreturn_t mvs_64xx_isr(struct mvs_info *mvi, int irq, u32 stat)
+{
+       void __iomem *regs = mvi->regs;
+
+       /* clear CMD_CMPLT ASAP */
+       mw32_f(MVS_INT_STAT, CINT_DONE);
+#ifndef MVS_USE_TASKLET
+       spin_lock(&mvi->lock);
+#endif
+       mvs_int_full(mvi);
+#ifndef MVS_USE_TASKLET
+       spin_unlock(&mvi->lock);
+#endif
+       return IRQ_HANDLED;
+}
+
+static void mvs_64xx_command_active(struct mvs_info *mvi, u32 slot_idx)
+{
+       u32 tmp;
+       mvs_cw32(mvi, 0x40 + (slot_idx >> 3), 1 << (slot_idx % 32));
+       mvs_cw32(mvi, 0x00 + (slot_idx >> 3), 1 << (slot_idx % 32));
+       do {
+               tmp = mvs_cr32(mvi, 0x00 + (slot_idx >> 3));
+       } while (tmp & 1 << (slot_idx % 32));
+       do {
+               tmp = mvs_cr32(mvi, 0x40 + (slot_idx >> 3));
+       } while (tmp & 1 << (slot_idx % 32));
+}
+
+static void mvs_64xx_issue_stop(struct mvs_info *mvi, enum mvs_port_type type,
+                               u32 tfs)
+{
+       void __iomem *regs = mvi->regs;
+       u32 tmp;
+
+       if (type == PORT_TYPE_SATA) {
+               tmp = mr32(MVS_INT_STAT_SRS_0) | (1U << tfs);
+               mw32(MVS_INT_STAT_SRS_0, tmp);
+       }
+       mw32(MVS_INT_STAT, CINT_CI_STOP);
+       tmp = mr32(MVS_PCS) | 0xFF00;
+       mw32(MVS_PCS, tmp);
+}
+
+static void mvs_64xx_free_reg_set(struct mvs_info *mvi, u8 *tfs)
+{
+       void __iomem *regs = mvi->regs;
+       u32 tmp, offs;
+
+       if (*tfs == MVS_ID_NOT_MAPPED)
+               return;
+
+       offs = 1U << ((*tfs & 0x0f) + PCS_EN_SATA_REG_SHIFT);
+       if (*tfs < 16) {
+               tmp = mr32(MVS_PCS);
+               mw32(MVS_PCS, tmp & ~offs);
+       } else {
+               tmp = mr32(MVS_CTL);
+               mw32(MVS_CTL, tmp & ~offs);
+       }
+
+       tmp = mr32(MVS_INT_STAT_SRS_0) & (1U << *tfs);
+       if (tmp)
+               mw32(MVS_INT_STAT_SRS_0, tmp);
+
+       *tfs = MVS_ID_NOT_MAPPED;
+       return;
+}
+
+static u8 mvs_64xx_assign_reg_set(struct mvs_info *mvi, u8 *tfs)
+{
+       int i;
+       u32 tmp, offs;
+       void __iomem *regs = mvi->regs;
+
+       if (*tfs != MVS_ID_NOT_MAPPED)
+               return 0;
+
+       tmp = mr32(MVS_PCS);
+
+       for (i = 0; i < mvi->chip->srs_sz; i++) {
+               if (i == 16)
+                       tmp = mr32(MVS_CTL);
+               offs = 1U << ((i & 0x0f) + PCS_EN_SATA_REG_SHIFT);
+               if (!(tmp & offs)) {
+                       *tfs = i;
+
+                       if (i < 16)
+                               mw32(MVS_PCS, tmp | offs);
+                       else
+                               mw32(MVS_CTL, tmp | offs);
+                       tmp = mr32(MVS_INT_STAT_SRS_0) & (1U << i);
+                       if (tmp)
+                               mw32(MVS_INT_STAT_SRS_0, tmp);
+                       return 0;
+               }
+       }
+       return MVS_ID_NOT_MAPPED;
+}
+
+void mvs_64xx_make_prd(struct scatterlist *scatter, int nr, void *prd)
+{
+       int i;
+       struct scatterlist *sg;
+       struct mvs_prd *buf_prd = prd;
+       for_each_sg(scatter, sg, nr, i) {
+               buf_prd->addr = cpu_to_le64(sg_dma_address(sg));
+               buf_prd->len = cpu_to_le32(sg_dma_len(sg));
+               buf_prd++;
+       }
+}
+
+static int mvs_64xx_oob_done(struct mvs_info *mvi, int i)
+{
+       u32 phy_st;
+       mvs_write_port_cfg_addr(mvi, i,
+                       PHYR_PHY_STAT);
+       phy_st = mvs_read_port_cfg_data(mvi, i);
+       if (phy_st & PHY_OOB_DTCTD)
+               return 1;
+       return 0;
+}
+
+static void mvs_64xx_fix_phy_info(struct mvs_info *mvi, int i,
+                               struct sas_identify_frame *id)
+
+{
+       struct mvs_phy *phy = &mvi->phy[i];
+       struct asd_sas_phy *sas_phy = &phy->sas_phy;
+
+       sas_phy->linkrate =
+               (phy->phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK) >>
+                       PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET;
+
+       phy->minimum_linkrate =
+               (phy->phy_status &
+                       PHY_MIN_SPP_PHYS_LINK_RATE_MASK) >> 8;
+       phy->maximum_linkrate =
+               (phy->phy_status &
+                       PHY_MAX_SPP_PHYS_LINK_RATE_MASK) >> 12;
+
+       mvs_write_port_cfg_addr(mvi, i, PHYR_IDENTIFY);
+       phy->dev_info = mvs_read_port_cfg_data(mvi, i);
+
+       mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_DEV_INFO);
+       phy->att_dev_info = mvs_read_port_cfg_data(mvi, i);
+
+       mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_ADDR_HI);
+       phy->att_dev_sas_addr =
+            (u64) mvs_read_port_cfg_data(mvi, i) << 32;
+       mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_ADDR_LO);
+       phy->att_dev_sas_addr |= mvs_read_port_cfg_data(mvi, i);
+       phy->att_dev_sas_addr = SAS_ADDR(&phy->att_dev_sas_addr);
+}
+
+static void mvs_64xx_phy_work_around(struct mvs_info *mvi, int i)
+{
+       u32 tmp;
+       struct mvs_phy *phy = &mvi->phy[i];
+       /* workaround for HW phy decoding error on 1.5g disk drive */
+       mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE6);
+       tmp = mvs_read_port_vsr_data(mvi, i);
+       if (((phy->phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK) >>
+            PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET) ==
+               SAS_LINK_RATE_1_5_GBPS)
+               tmp &= ~PHY_MODE6_LATECLK;
+       else
+               tmp |= PHY_MODE6_LATECLK;
+       mvs_write_port_vsr_data(mvi, i, tmp);
+}
+
+void mvs_64xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id,
+                       struct sas_phy_linkrates *rates)
+{
+       u32 lrmin = 0, lrmax = 0;
+       u32 tmp;
+
+       tmp = mvs_read_phy_ctl(mvi, phy_id);
+       lrmin = (rates->minimum_linkrate << 8);
+       lrmax = (rates->maximum_linkrate << 12);
+
+       if (lrmin) {
+               tmp &= ~(0xf << 8);
+               tmp |= lrmin;
+       }
+       if (lrmax) {
+               tmp &= ~(0xf << 12);
+               tmp |= lrmax;
+       }
+       mvs_write_phy_ctl(mvi, phy_id, tmp);
+       mvs_64xx_phy_reset(mvi, phy_id, 1);
+}
+
+static void mvs_64xx_clear_active_cmds(struct mvs_info *mvi)
+{
+       u32 tmp;
+       void __iomem *regs = mvi->regs;
+       tmp = mr32(MVS_PCS);
+       mw32(MVS_PCS, tmp & 0xFFFF);
+       mw32(MVS_PCS, tmp);
+       tmp = mr32(MVS_CTL);
+       mw32(MVS_CTL, tmp & 0xFFFF);
+       mw32(MVS_CTL, tmp);
+}
+
+
+u32 mvs_64xx_spi_read_data(struct mvs_info *mvi)
+{
+       void __iomem *regs = mvi->regs_ex;
+       return ior32(SPI_DATA_REG_64XX);
+}
+
+void mvs_64xx_spi_write_data(struct mvs_info *mvi, u32 data)
+{
+       void __iomem *regs = mvi->regs_ex;
+        iow32(SPI_DATA_REG_64XX, data);
+}
+
+
+int mvs_64xx_spi_buildcmd(struct mvs_info *mvi,
+                       u32      *dwCmd,
+                       u8       cmd,
+                       u8       read,
+                       u8       length,
+                       u32      addr
+                       )
+{
+       u32  dwTmp;
+
+       dwTmp = ((u32)cmd << 24) | ((u32)length << 19);
+       if (read)
+               dwTmp |= 1U<<23;
+
+       if (addr != MV_MAX_U32) {
+               dwTmp |= 1U<<22;
+               dwTmp |= (addr & 0x0003FFFF);
+       }
+
+       *dwCmd = dwTmp;
+       return 0;
+}
+
+
+int mvs_64xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd)
+{
+       void __iomem *regs = mvi->regs_ex;
+       int     retry;
+
+       for (retry = 0; retry < 1; retry++) {
+               iow32(SPI_CTRL_REG_64XX, SPI_CTRL_VENDOR_ENABLE);
+               iow32(SPI_CMD_REG_64XX, cmd);
+               iow32(SPI_CTRL_REG_64XX,
+                       SPI_CTRL_VENDOR_ENABLE | SPI_CTRL_SPISTART);
+       }
+
+       return 0;
+}
+
+int mvs_64xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout)
+{
+       void __iomem *regs = mvi->regs_ex;
+       u32 i, dwTmp;
+
+       for (i = 0; i < timeout; i++) {
+               dwTmp = ior32(SPI_CTRL_REG_64XX);
+               if (!(dwTmp & SPI_CTRL_SPISTART))
+                       return 0;
+               msleep(10);
+       }
+
+       return -1;
+}
+
+#ifndef DISABLE_HOTPLUG_DMA_FIX
+void mvs_64xx_fix_dma(dma_addr_t buf_dma, int buf_len, int from, void *prd)
+{
+       int i;
+       struct mvs_prd *buf_prd = prd;
+       buf_prd += from;
+       for (i = 0; i < MAX_SG_ENTRY - from; i++) {
+               buf_prd->addr = cpu_to_le64(buf_dma);
+               buf_prd->len = cpu_to_le32(buf_len);
+               ++buf_prd;
+       }
+}
+#endif
+
+const struct mvs_dispatch mvs_64xx_dispatch = {
+       "mv64xx",
+       mvs_64xx_init,
+       NULL,
+       mvs_64xx_ioremap,
+       mvs_64xx_iounmap,
+       mvs_64xx_isr,
+       mvs_64xx_isr_status,
+       mvs_64xx_interrupt_enable,
+       mvs_64xx_interrupt_disable,
+       mvs_read_phy_ctl,
+       mvs_write_phy_ctl,
+       mvs_read_port_cfg_data,
+       mvs_write_port_cfg_data,
+       mvs_write_port_cfg_addr,
+       mvs_read_port_vsr_data,
+       mvs_write_port_vsr_data,
+       mvs_write_port_vsr_addr,
+       mvs_read_port_irq_stat,
+       mvs_write_port_irq_stat,
+       mvs_read_port_irq_mask,
+       mvs_write_port_irq_mask,
+       mvs_get_sas_addr,
+       mvs_64xx_command_active,
+       mvs_64xx_issue_stop,
+       mvs_start_delivery,
+       mvs_rx_update,
+       mvs_int_full,
+       mvs_64xx_assign_reg_set,
+       mvs_64xx_free_reg_set,
+       mvs_get_prd_size,
+       mvs_get_prd_count,
+       mvs_64xx_make_prd,
+       mvs_64xx_detect_porttype,
+       mvs_64xx_oob_done,
+       mvs_64xx_fix_phy_info,
+       mvs_64xx_phy_work_around,
+       mvs_64xx_phy_set_link_rate,
+       mvs_hw_max_link_rate,
+       mvs_64xx_phy_disable,
+       mvs_64xx_phy_enable,
+       mvs_64xx_phy_reset,
+       mvs_64xx_stp_reset,
+       mvs_64xx_clear_active_cmds,
+       mvs_64xx_spi_read_data,
+       mvs_64xx_spi_write_data,
+       mvs_64xx_spi_buildcmd,
+       mvs_64xx_spi_issuecmd,
+       mvs_64xx_spi_waitdataready,
+#ifndef DISABLE_HOTPLUG_DMA_FIX
+       mvs_64xx_fix_dma,
+#endif
+};
+
diff --git a/drivers/scsi/mvsas/mv_64xx.h b/drivers/scsi/mvsas/mv_64xx.h
new file mode 100644 (file)
index 0000000..42e947d
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Marvell 88SE64xx hardware specific head file
+ *
+ * Copyright 2007 Red Hat, Inc.
+ * Copyright 2008 Marvell. <kewei@marvell.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This program is free software; 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.
+ *
+ * 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 _MVS64XX_REG_H_
+#define _MVS64XX_REG_H_
+
+#include <linux/types.h>
+
+#define MAX_LINK_RATE          SAS_LINK_RATE_3_0_GBPS
+
+/* enhanced mode registers (BAR4) */
+enum hw_registers {
+       MVS_GBL_CTL             = 0x04,  /* global control */
+       MVS_GBL_INT_STAT        = 0x08,  /* global irq status */
+       MVS_GBL_PI              = 0x0C,  /* ports implemented bitmask */
+
+       MVS_PHY_CTL             = 0x40,  /* SOC PHY Control */
+       MVS_PORTS_IMP           = 0x9C,  /* SOC Port Implemented */
+
+       MVS_GBL_PORT_TYPE       = 0xa0,  /* port type */
+
+       MVS_CTL                 = 0x100, /* SAS/SATA port configuration */
+       MVS_PCS                 = 0x104, /* SAS/SATA port control/status */
+       MVS_CMD_LIST_LO         = 0x108, /* cmd list addr */
+       MVS_CMD_LIST_HI         = 0x10C,
+       MVS_RX_FIS_LO           = 0x110, /* RX FIS list addr */
+       MVS_RX_FIS_HI           = 0x114,
+
+       MVS_TX_CFG              = 0x120, /* TX configuration */
+       MVS_TX_LO               = 0x124, /* TX (delivery) ring addr */
+       MVS_TX_HI               = 0x128,
+
+       MVS_TX_PROD_IDX         = 0x12C, /* TX producer pointer */
+       MVS_TX_CONS_IDX         = 0x130, /* TX consumer pointer (RO) */
+       MVS_RX_CFG              = 0x134, /* RX configuration */
+       MVS_RX_LO               = 0x138, /* RX (completion) ring addr */
+       MVS_RX_HI               = 0x13C,
+       MVS_RX_CONS_IDX         = 0x140, /* RX consumer pointer (RO) */
+
+       MVS_INT_COAL            = 0x148, /* Int coalescing config */
+       MVS_INT_COAL_TMOUT      = 0x14C, /* Int coalescing timeout */
+       MVS_INT_STAT            = 0x150, /* Central int status */
+       MVS_INT_MASK            = 0x154, /* Central int enable */
+       MVS_INT_STAT_SRS_0      = 0x158, /* SATA register set status */
+       MVS_INT_MASK_SRS_0      = 0x15C,
+
+                                        /* ports 1-3 follow after this */
+       MVS_P0_INT_STAT         = 0x160, /* port0 interrupt status */
+       MVS_P0_INT_MASK         = 0x164, /* port0 interrupt mask */
+                                        /* ports 5-7 follow after this */
+       MVS_P4_INT_STAT         = 0x200, /* Port4 interrupt status */
+       MVS_P4_INT_MASK         = 0x204, /* Port4 interrupt enable mask */
+
+                                        /* ports 1-3 follow after this */
+       MVS_P0_SER_CTLSTAT      = 0x180, /* port0 serial control/status */
+                                        /* ports 5-7 follow after this */
+       MVS_P4_SER_CTLSTAT      = 0x220, /* port4 serial control/status */
+
+       MVS_CMD_ADDR            = 0x1B8, /* Command register port (addr) */
+       MVS_CMD_DATA            = 0x1BC, /* Command register port (data) */
+
+                                        /* ports 1-3 follow after this */
+       MVS_P0_CFG_ADDR         = 0x1C0, /* port0 phy register address */
+       MVS_P0_CFG_DATA         = 0x1C4, /* port0 phy register data */
+                                        /* ports 5-7 follow after this */
+       MVS_P4_CFG_ADDR         = 0x230, /* Port4 config address */
+       MVS_P4_CFG_DATA         = 0x234, /* Port4 config data */
+
+                                        /* ports 1-3 follow after this */
+       MVS_P0_VSR_ADDR         = 0x1E0, /* port0 VSR address */
+       MVS_P0_VSR_DATA         = 0x1E4, /* port0 VSR data */
+                                        /* ports 5-7 follow after this */
+       MVS_P4_VSR_ADDR         = 0x250, /* port4 VSR addr */
+       MVS_P4_VSR_DATA         = 0x254, /* port4 VSR data */
+};
+
+enum pci_cfg_registers {
+       PCR_PHY_CTL             = 0x40,
+       PCR_PHY_CTL2            = 0x90,
+       PCR_DEV_CTRL            = 0xE8,
+       PCR_LINK_STAT           = 0xF2,
+};
+
+/*  SAS/SATA Vendor Specific Port Registers */
+enum sas_sata_vsp_regs {
+       VSR_PHY_STAT            = 0x00, /* Phy Status */
+       VSR_PHY_MODE1           = 0x01, /* phy tx */
+       VSR_PHY_MODE2           = 0x02, /* tx scc */
+       VSR_PHY_MODE3           = 0x03, /* pll */
+       VSR_PHY_MODE4           = 0x04, /* VCO */
+       VSR_PHY_MODE5           = 0x05, /* Rx */
+       VSR_PHY_MODE6           = 0x06, /* CDR */
+       VSR_PHY_MODE7           = 0x07, /* Impedance */
+       VSR_PHY_MODE8           = 0x08, /* Voltage */
+       VSR_PHY_MODE9           = 0x09, /* Test */
+       VSR_PHY_MODE10          = 0x0A, /* Power */
+       VSR_PHY_MODE11          = 0x0B, /* Phy Mode */
+       VSR_PHY_VS0             = 0x0C, /* Vednor Specific 0 */
+       VSR_PHY_VS1             = 0x0D, /* Vednor Specific 1 */
+};
+
+enum chip_register_bits {
+       PHY_MIN_SPP_PHYS_LINK_RATE_MASK = (0xF << 8),
+       PHY_MAX_SPP_PHYS_LINK_RATE_MASK = (0xF << 12),
+       PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET = (16),
+       PHY_NEG_SPP_PHYS_LINK_RATE_MASK =
+                       (0xF << PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET),
+};
+
+#define MAX_SG_ENTRY           64
+
+struct mvs_prd {
+       __le64                  addr;           /* 64-bit buffer address */
+       __le32                  reserved;
+       __le32                  len;            /* 16-bit length */
+};
+
+#define SPI_CTRL_REG                           0xc0
+#define SPI_CTRL_VENDOR_ENABLE         (1U<<29)
+#define SPI_CTRL_SPIRDY                        (1U<<22)
+#define SPI_CTRL_SPISTART                      (1U<<20)
+
+#define SPI_CMD_REG            0xc4
+#define SPI_DATA_REG           0xc8
+
+#define SPI_CTRL_REG_64XX              0x10
+#define SPI_CMD_REG_64XX               0x14
+#define SPI_DATA_REG_64XX              0x18
+
+#endif
diff --git a/drivers/scsi/mvsas/mv_94xx.c b/drivers/scsi/mvsas/mv_94xx.c
new file mode 100644 (file)
index 0000000..0940fae
--- /dev/null
@@ -0,0 +1,672 @@
+/*
+ * Marvell 88SE94xx hardware specific
+ *
+ * Copyright 2007 Red Hat, Inc.
+ * Copyright 2008 Marvell. <kewei@marvell.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This program is free software; 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.
+ *
+ * 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 "mv_sas.h"
+#include "mv_94xx.h"
+#include "mv_chips.h"
+
+static void mvs_94xx_detect_porttype(struct mvs_info *mvi, int i)
+{
+       u32 reg;
+       struct mvs_phy *phy = &mvi->phy[i];
+       u32 phy_status;
+
+       mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE3);
+       reg = mvs_read_port_vsr_data(mvi, i);
+       phy_status = ((reg & 0x3f0000) >> 16) & 0xff;
+       phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
+       switch (phy_status) {
+       case 0x10:
+               phy->phy_type |= PORT_TYPE_SAS;
+               break;
+       case 0x1d:
+       default:
+               phy->phy_type |= PORT_TYPE_SATA;
+               break;
+       }
+}
+
+static void __devinit mvs_94xx_enable_xmt(struct mvs_info *mvi, int phy_id)
+{
+       void __iomem *regs = mvi->regs;
+       u32 tmp;
+
+       tmp = mr32(MVS_PCS);
+       tmp |= 1 << (phy_id + PCS_EN_PORT_XMT_SHIFT2);
+       mw32(MVS_PCS, tmp);
+}
+
+static void mvs_94xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard)
+{
+       u32 tmp;
+
+       tmp = mvs_read_port_irq_stat(mvi, phy_id);
+       tmp &= ~PHYEV_RDY_CH;
+       mvs_write_port_irq_stat(mvi, phy_id, tmp);
+       if (hard) {
+               tmp = mvs_read_phy_ctl(mvi, phy_id);
+               tmp |= PHY_RST_HARD;
+               mvs_write_phy_ctl(mvi, phy_id, tmp);
+               do {
+                       tmp = mvs_read_phy_ctl(mvi, phy_id);
+               } while (tmp & PHY_RST_HARD);
+       } else {
+               mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_STAT);
+               tmp = mvs_read_port_vsr_data(mvi, phy_id);
+               tmp |= PHY_RST;
+               mvs_write_port_vsr_data(mvi, phy_id, tmp);
+       }
+}
+
+static void mvs_94xx_phy_disable(struct mvs_info *mvi, u32 phy_id)
+{
+       u32 tmp;
+       mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_MODE2);
+       tmp = mvs_read_port_vsr_data(mvi, phy_id);
+       mvs_write_port_vsr_data(mvi, phy_id, tmp | 0x00800000);
+}
+
+static void mvs_94xx_phy_enable(struct mvs_info *mvi, u32 phy_id)
+{
+       mvs_write_port_vsr_addr(mvi, phy_id, 0x1B4);
+       mvs_write_port_vsr_data(mvi, phy_id, 0x8300ffc1);
+       mvs_write_port_vsr_addr(mvi, phy_id, 0x104);
+       mvs_write_port_vsr_data(mvi, phy_id, 0x00018080);
+       mvs_write_port_vsr_addr(mvi, phy_id, VSR_PHY_MODE2);
+       mvs_write_port_vsr_data(mvi, phy_id, 0x00207fff);
+}
+
+static int __devinit mvs_94xx_init(struct mvs_info *mvi)
+{
+       void __iomem *regs = mvi->regs;
+       int i;
+       u32 tmp, cctl;
+
+       mvs_show_pcie_usage(mvi);
+       if (mvi->flags & MVF_FLAG_SOC) {
+               tmp = mr32(MVS_PHY_CTL);
+               tmp &= ~PCTL_PWR_OFF;
+               tmp |= PCTL_PHY_DSBL;
+               mw32(MVS_PHY_CTL, tmp);
+       }
+
+       /* Init Chip */
+       /* make sure RST is set; HBA_RST /should/ have done that for us */
+       cctl = mr32(MVS_CTL) & 0xFFFF;
+       if (cctl & CCTL_RST)
+               cctl &= ~CCTL_RST;
+       else
+               mw32_f(MVS_CTL, cctl | CCTL_RST);
+
+       if (mvi->flags & MVF_FLAG_SOC) {
+               tmp = mr32(MVS_PHY_CTL);
+               tmp &= ~PCTL_PWR_OFF;
+               tmp |= PCTL_COM_ON;
+               tmp &= ~PCTL_PHY_DSBL;
+               tmp |= PCTL_LINK_RST;
+               mw32(MVS_PHY_CTL, tmp);
+               msleep(100);
+               tmp &= ~PCTL_LINK_RST;
+               mw32(MVS_PHY_CTL, tmp);
+               msleep(100);
+       }
+
+       /* reset control */
+       mw32(MVS_PCS, 0);               /* MVS_PCS */
+       mw32(MVS_STP_REG_SET_0, 0);
+       mw32(MVS_STP_REG_SET_1, 0);
+
+       /* init phys */
+       mvs_phy_hacks(mvi);
+
+       /* disable Multiplexing, enable phy implemented */
+       mw32(MVS_PORTS_IMP, 0xFF);
+
+
+       mw32(MVS_PA_VSR_ADDR, 0x00000104);
+       mw32(MVS_PA_VSR_PORT, 0x00018080);
+       mw32(MVS_PA_VSR_ADDR, VSR_PHY_MODE8);
+       mw32(MVS_PA_VSR_PORT, 0x0084ffff);
+
+       /* set LED blink when IO*/
+       mw32(MVS_PA_VSR_ADDR, 0x00000030);
+       tmp = mr32(MVS_PA_VSR_PORT);
+       tmp &= 0xFFFF00FF;
+       tmp |= 0x00003300;
+       mw32(MVS_PA_VSR_PORT, tmp);
+
+       mw32(MVS_CMD_LIST_LO, mvi->slot_dma);
+       mw32(MVS_CMD_LIST_HI, (mvi->slot_dma >> 16) >> 16);
+
+       mw32(MVS_RX_FIS_LO, mvi->rx_fis_dma);
+       mw32(MVS_RX_FIS_HI, (mvi->rx_fis_dma >> 16) >> 16);
+
+       mw32(MVS_TX_CFG, MVS_CHIP_SLOT_SZ);
+       mw32(MVS_TX_LO, mvi->tx_dma);
+       mw32(MVS_TX_HI, (mvi->tx_dma >> 16) >> 16);
+
+       mw32(MVS_RX_CFG, MVS_RX_RING_SZ);
+       mw32(MVS_RX_LO, mvi->rx_dma);
+       mw32(MVS_RX_HI, (mvi->rx_dma >> 16) >> 16);
+
+       for (i = 0; i < mvi->chip->n_phy; i++) {
+               mvs_94xx_phy_disable(mvi, i);
+               /* set phy local SAS address */
+               mvs_set_sas_addr(mvi, i, CONFIG_ID_FRAME3, CONFIG_ID_FRAME4,
+                                               (mvi->phy[i].dev_sas_addr));
+
+               mvs_94xx_enable_xmt(mvi, i);
+               mvs_94xx_phy_enable(mvi, i);
+
+               mvs_94xx_phy_reset(mvi, i, 1);
+               msleep(500);
+               mvs_94xx_detect_porttype(mvi, i);
+       }
+
+       if (mvi->flags & MVF_FLAG_SOC) {
+               /* set select registers */
+               writel(0x0E008000, regs + 0x000);
+               writel(0x59000008, regs + 0x004);
+               writel(0x20, regs + 0x008);
+               writel(0x20, regs + 0x00c);
+               writel(0x20, regs + 0x010);
+               writel(0x20, regs + 0x014);
+               writel(0x20, regs + 0x018);
+               writel(0x20, regs + 0x01c);
+       }
+       for (i = 0; i < mvi->chip->n_phy; i++) {
+               /* clear phy int status */
+               tmp = mvs_read_port_irq_stat(mvi, i);
+               tmp &= ~PHYEV_SIG_FIS;
+               mvs_write_port_irq_stat(mvi, i, tmp);
+
+               /* set phy int mask */
+               tmp = PHYEV_RDY_CH | PHYEV_BROAD_CH |
+                       PHYEV_ID_DONE  | PHYEV_DCDR_ERR | PHYEV_CRC_ERR ;
+               mvs_write_port_irq_mask(mvi, i, tmp);
+
+               msleep(100);
+               mvs_update_phyinfo(mvi, i, 1);
+       }
+
+       /* FIXME: update wide port bitmaps */
+
+       /* little endian for open address and command table, etc. */
+       /*
+        * it seems that ( from the spec ) turning on big-endian won't
+        * do us any good on big-endian machines, need further confirmation
+        */
+       cctl = mr32(MVS_CTL);
+       cctl |= CCTL_ENDIAN_CMD;
+       cctl |= CCTL_ENDIAN_DATA;
+       cctl &= ~CCTL_ENDIAN_OPEN;
+       cctl |= CCTL_ENDIAN_RSP;
+       mw32_f(MVS_CTL, cctl);
+
+       /* reset CMD queue */
+       tmp = mr32(MVS_PCS);
+       tmp |= PCS_CMD_RST;
+       mw32(MVS_PCS, tmp);
+       /* interrupt coalescing may cause missing HW interrput in some case,
+        * and the max count is 0x1ff, while our max slot is 0x200,
+        * it will make count 0.
+        */
+       tmp = 0;
+       mw32(MVS_INT_COAL, tmp);
+
+       tmp = 0x100;
+       mw32(MVS_INT_COAL_TMOUT, tmp);
+
+       /* ladies and gentlemen, start your engines */
+       mw32(MVS_TX_CFG, 0);
+       mw32(MVS_TX_CFG, MVS_CHIP_SLOT_SZ | TX_EN);
+       mw32(MVS_RX_CFG, MVS_RX_RING_SZ | RX_EN);
+       /* enable CMD/CMPL_Q/RESP mode */
+       mw32(MVS_PCS, PCS_SATA_RETRY_2 | PCS_FIS_RX_EN |
+               PCS_CMD_EN | PCS_CMD_STOP_ERR);
+
+       /* enable completion queue interrupt */
+       tmp = (CINT_PORT_MASK | CINT_DONE | CINT_MEM | CINT_SRS | CINT_CI_STOP |
+               CINT_DMA_PCIE);
+       tmp |= CINT_PHY_MASK;
+       mw32(MVS_INT_MASK, tmp);
+
+       /* Enable SRS interrupt */
+       mw32(MVS_INT_MASK_SRS_0, 0xFFFF);
+
+       return 0;
+}
+
+static int mvs_94xx_ioremap(struct mvs_info *mvi)
+{
+       if (!mvs_ioremap(mvi, 2, -1)) {
+               mvi->regs_ex = mvi->regs + 0x10200;
+               mvi->regs += 0x20000;
+               if (mvi->id == 1)
+                       mvi->regs += 0x4000;
+               return 0;
+       }
+       return -1;
+}
+
+static void mvs_94xx_iounmap(struct mvs_info *mvi)
+{
+       if (mvi->regs) {
+               mvi->regs -= 0x20000;
+               if (mvi->id == 1)
+                       mvi->regs -= 0x4000;
+               mvs_iounmap(mvi->regs);
+       }
+}
+
+static void mvs_94xx_interrupt_enable(struct mvs_info *mvi)
+{
+       void __iomem *regs = mvi->regs_ex;
+       u32 tmp;
+
+       tmp = mr32(MVS_GBL_CTL);
+       tmp |= (IRQ_SAS_A | IRQ_SAS_B);
+       mw32(MVS_GBL_INT_STAT, tmp);
+       writel(tmp, regs + 0x0C);
+       writel(tmp, regs + 0x10);
+       writel(tmp, regs + 0x14);
+       writel(tmp, regs + 0x18);
+       mw32(MVS_GBL_CTL, tmp);
+}
+
+static void mvs_94xx_interrupt_disable(struct mvs_info *mvi)
+{
+       void __iomem *regs = mvi->regs_ex;
+       u32 tmp;
+
+       tmp = mr32(MVS_GBL_CTL);
+
+       tmp &= ~(IRQ_SAS_A | IRQ_SAS_B);
+       mw32(MVS_GBL_INT_STAT, tmp);
+       writel(tmp, regs + 0x0C);
+       writel(tmp, regs + 0x10);
+       writel(tmp, regs + 0x14);
+       writel(tmp, regs + 0x18);
+       mw32(MVS_GBL_CTL, tmp);
+}
+
+static u32 mvs_94xx_isr_status(struct mvs_info *mvi, int irq)
+{
+       void __iomem *regs = mvi->regs_ex;
+       u32 stat = 0;
+       if (!(mvi->flags & MVF_FLAG_SOC)) {
+               stat = mr32(MVS_GBL_INT_STAT);
+
+               if (!(stat & (IRQ_SAS_A | IRQ_SAS_B)))
+                       return 0;
+       }
+       return stat;
+}
+
+static irqreturn_t mvs_94xx_isr(struct mvs_info *mvi, int irq, u32 stat)
+{
+       void __iomem *regs = mvi->regs;
+
+       if (((stat & IRQ_SAS_A) && mvi->id == 0) ||
+                       ((stat & IRQ_SAS_B) && mvi->id == 1)) {
+               mw32_f(MVS_INT_STAT, CINT_DONE);
+       #ifndef MVS_USE_TASKLET
+               spin_lock(&mvi->lock);
+       #endif
+               mvs_int_full(mvi);
+       #ifndef MVS_USE_TASKLET
+               spin_unlock(&mvi->lock);
+       #endif
+       }
+       return IRQ_HANDLED;
+}
+
+static void mvs_94xx_command_active(struct mvs_info *mvi, u32 slot_idx)
+{
+       u32 tmp;
+       mvs_cw32(mvi, 0x300 + (slot_idx >> 3), 1 << (slot_idx % 32));
+       do {
+               tmp = mvs_cr32(mvi, 0x300 + (slot_idx >> 3));
+       } while (tmp & 1 << (slot_idx % 32));
+}
+
+static void mvs_94xx_issue_stop(struct mvs_info *mvi, enum mvs_port_type type,
+                               u32 tfs)
+{
+       void __iomem *regs = mvi->regs;
+       u32 tmp;
+
+       if (type == PORT_TYPE_SATA) {
+               tmp = mr32(MVS_INT_STAT_SRS_0) | (1U << tfs);
+               mw32(MVS_INT_STAT_SRS_0, tmp);
+       }
+       mw32(MVS_INT_STAT, CINT_CI_STOP);
+       tmp = mr32(MVS_PCS) | 0xFF00;
+       mw32(MVS_PCS, tmp);
+}
+
+static void mvs_94xx_free_reg_set(struct mvs_info *mvi, u8 *tfs)
+{
+       void __iomem *regs = mvi->regs;
+       u32 tmp;
+       u8 reg_set = *tfs;
+
+       if (*tfs == MVS_ID_NOT_MAPPED)
+               return;
+
+       mvi->sata_reg_set &= ~bit(reg_set);
+       if (reg_set < 32) {
+               w_reg_set_enable(reg_set, (u32)mvi->sata_reg_set);
+               tmp = mr32(MVS_INT_STAT_SRS_0) & (u32)mvi->sata_reg_set;
+               if (tmp)
+                       mw32(MVS_INT_STAT_SRS_0, tmp);
+       } else {
+               w_reg_set_enable(reg_set, mvi->sata_reg_set);
+               tmp = mr32(MVS_INT_STAT_SRS_1) & mvi->sata_reg_set;
+               if (tmp)
+                       mw32(MVS_INT_STAT_SRS_1, tmp);
+       }
+
+       *tfs = MVS_ID_NOT_MAPPED;
+
+       return;
+}
+
+static u8 mvs_94xx_assign_reg_set(struct mvs_info *mvi, u8 *tfs)
+{
+       int i;
+       void __iomem *regs = mvi->regs;
+
+       if (*tfs != MVS_ID_NOT_MAPPED)
+               return 0;
+
+       i = mv_ffc64(mvi->sata_reg_set);
+       if (i > 32) {
+               mvi->sata_reg_set |= bit(i);
+               w_reg_set_enable(i, (u32)(mvi->sata_reg_set >> 32));
+               *tfs = i;
+               return 0;
+       } else if (i >= 0) {
+               mvi->sata_reg_set |= bit(i);
+               w_reg_set_enable(i, (u32)mvi->sata_reg_set);
+               *tfs = i;
+               return 0;
+       }
+       return MVS_ID_NOT_MAPPED;
+}
+
+static void mvs_94xx_make_prd(struct scatterlist *scatter, int nr, void *prd)
+{
+       int i;
+       struct scatterlist *sg;
+       struct mvs_prd *buf_prd = prd;
+       for_each_sg(scatter, sg, nr, i) {
+               buf_prd->addr = cpu_to_le64(sg_dma_address(sg));
+               buf_prd->im_len.len = cpu_to_le32(sg_dma_len(sg));
+               buf_prd++;
+       }
+}
+
+static int mvs_94xx_oob_done(struct mvs_info *mvi, int i)
+{
+       u32 phy_st;
+       phy_st = mvs_read_phy_ctl(mvi, i);
+       if (phy_st & PHY_READY_MASK)    /* phy ready */
+               return 1;
+       return 0;
+}
+
+static void mvs_94xx_get_dev_identify_frame(struct mvs_info *mvi, int port_id,
+                                       struct sas_identify_frame *id)
+{
+       int i;
+       u32 id_frame[7];
+
+       for (i = 0; i < 7; i++) {
+               mvs_write_port_cfg_addr(mvi, port_id,
+                                       CONFIG_ID_FRAME0 + i * 4);
+               id_frame[i] = mvs_read_port_cfg_data(mvi, port_id);
+       }
+       memcpy(id, id_frame, 28);
+}
+
+static void mvs_94xx_get_att_identify_frame(struct mvs_info *mvi, int port_id,
+                                       struct sas_identify_frame *id)
+{
+       int i;
+       u32 id_frame[7];
+
+       /* mvs_hexdump(28, (u8 *)id_frame, 0); */
+       for (i = 0; i < 7; i++) {
+               mvs_write_port_cfg_addr(mvi, port_id,
+                                       CONFIG_ATT_ID_FRAME0 + i * 4);
+               id_frame[i] = mvs_read_port_cfg_data(mvi, port_id);
+               mv_dprintk("94xx phy %d atta frame %d %x.\n",
+                       port_id + mvi->id * mvi->chip->n_phy, i, id_frame[i]);
+       }
+       /* mvs_hexdump(28, (u8 *)id_frame, 0); */
+       memcpy(id, id_frame, 28);
+}
+
+static u32 mvs_94xx_make_dev_info(struct sas_identify_frame *id)
+{
+       u32 att_dev_info = 0;
+
+       att_dev_info |= id->dev_type;
+       if (id->stp_iport)
+               att_dev_info |= PORT_DEV_STP_INIT;
+       if (id->smp_iport)
+               att_dev_info |= PORT_DEV_SMP_INIT;
+       if (id->ssp_iport)
+               att_dev_info |= PORT_DEV_SSP_INIT;
+       if (id->stp_tport)
+               att_dev_info |= PORT_DEV_STP_TRGT;
+       if (id->smp_tport)
+               att_dev_info |= PORT_DEV_SMP_TRGT;
+       if (id->ssp_tport)
+               att_dev_info |= PORT_DEV_SSP_TRGT;
+
+       att_dev_info |= (u32)id->phy_id<<24;
+       return att_dev_info;
+}
+
+static u32 mvs_94xx_make_att_info(struct sas_identify_frame *id)
+{
+       return mvs_94xx_make_dev_info(id);
+}
+
+static void mvs_94xx_fix_phy_info(struct mvs_info *mvi, int i,
+                               struct sas_identify_frame *id)
+{
+       struct mvs_phy *phy = &mvi->phy[i];
+       struct asd_sas_phy *sas_phy = &phy->sas_phy;
+       mv_dprintk("get all reg link rate is 0x%x\n", phy->phy_status);
+       sas_phy->linkrate =
+               (phy->phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK) >>
+                       PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET;
+       sas_phy->linkrate += 0x8;
+       mv_dprintk("get link rate is %d\n", sas_phy->linkrate);
+       phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS;
+       phy->maximum_linkrate = SAS_LINK_RATE_6_0_GBPS;
+       mvs_94xx_get_dev_identify_frame(mvi, i, id);
+       phy->dev_info = mvs_94xx_make_dev_info(id);
+
+       if (phy->phy_type & PORT_TYPE_SAS) {
+               mvs_94xx_get_att_identify_frame(mvi, i, id);
+               phy->att_dev_info = mvs_94xx_make_att_info(id);
+               phy->att_dev_sas_addr = *(u64 *)id->sas_addr;
+       } else {
+               phy->att_dev_info = PORT_DEV_STP_TRGT | 1;
+       }
+
+}
+
+void mvs_94xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id,
+                       struct sas_phy_linkrates *rates)
+{
+       /* TODO */
+}
+
+static void mvs_94xx_clear_active_cmds(struct mvs_info *mvi)
+{
+       u32 tmp;
+       void __iomem *regs = mvi->regs;
+       tmp = mr32(MVS_STP_REG_SET_0);
+       mw32(MVS_STP_REG_SET_0, 0);
+       mw32(MVS_STP_REG_SET_0, tmp);
+       tmp = mr32(MVS_STP_REG_SET_1);
+       mw32(MVS_STP_REG_SET_1, 0);
+       mw32(MVS_STP_REG_SET_1, tmp);
+}
+
+
+u32 mvs_94xx_spi_read_data(struct mvs_info *mvi)
+{
+       void __iomem *regs = mvi->regs_ex - 0x10200;
+       return mr32(SPI_RD_DATA_REG_94XX);
+}
+
+void mvs_94xx_spi_write_data(struct mvs_info *mvi, u32 data)
+{
+       void __iomem *regs = mvi->regs_ex - 0x10200;
+        mw32(SPI_RD_DATA_REG_94XX, data);
+}
+
+
+int mvs_94xx_spi_buildcmd(struct mvs_info *mvi,
+                               u32      *dwCmd,
+                               u8       cmd,
+                               u8       read,
+                               u8       length,
+                               u32      addr
+                               )
+{
+       void __iomem *regs = mvi->regs_ex - 0x10200;
+       u32  dwTmp;
+
+       dwTmp = ((u32)cmd << 8) | ((u32)length << 4);
+       if (read)
+               dwTmp |= SPI_CTRL_READ_94XX;
+
+       if (addr != MV_MAX_U32) {
+               mw32(SPI_ADDR_REG_94XX, (addr & 0x0003FFFFL));
+               dwTmp |= SPI_ADDR_VLD_94XX;
+       }
+
+       *dwCmd = dwTmp;
+       return 0;
+}
+
+
+int mvs_94xx_spi_issuecmd(struct mvs_info *mvi, u32 cmd)
+{
+       void __iomem *regs = mvi->regs_ex - 0x10200;
+       mw32(SPI_CTRL_REG_94XX, cmd | SPI_CTRL_SpiStart_94XX);
+
+       return 0;
+}
+
+int mvs_94xx_spi_waitdataready(struct mvs_info *mvi, u32 timeout)
+{
+       void __iomem *regs = mvi->regs_ex - 0x10200;
+       u32   i, dwTmp;
+
+       for (i = 0; i < timeout; i++) {
+               dwTmp = mr32(SPI_CTRL_REG_94XX);
+               if (!(dwTmp & SPI_CTRL_SpiStart_94XX))
+                       return 0;
+               msleep(10);
+       }
+
+       return -1;
+}
+
+#ifndef DISABLE_HOTPLUG_DMA_FIX
+void mvs_94xx_fix_dma(dma_addr_t buf_dma, int buf_len, int from, void *prd)
+{
+       int i;
+       struct mvs_prd *buf_prd = prd;
+       buf_prd += from;
+       for (i = 0; i < MAX_SG_ENTRY - from; i++) {
+               buf_prd->addr = cpu_to_le64(buf_dma);
+               buf_prd->im_len.len = cpu_to_le32(buf_len);
+               ++buf_prd;
+       }
+}
+#endif
+
+const struct mvs_dispatch mvs_94xx_dispatch = {
+       "mv94xx",
+       mvs_94xx_init,
+       NULL,
+       mvs_94xx_ioremap,
+       mvs_94xx_iounmap,
+       mvs_94xx_isr,
+       mvs_94xx_isr_status,
+       mvs_94xx_interrupt_enable,
+       mvs_94xx_interrupt_disable,
+       mvs_read_phy_ctl,
+       mvs_write_phy_ctl,
+       mvs_read_port_cfg_data,
+       mvs_write_port_cfg_data,
+       mvs_write_port_cfg_addr,
+       mvs_read_port_vsr_data,
+       mvs_write_port_vsr_data,
+       mvs_write_port_vsr_addr,
+       mvs_read_port_irq_stat,
+       mvs_write_port_irq_stat,
+       mvs_read_port_irq_mask,
+       mvs_write_port_irq_mask,
+       mvs_get_sas_addr,
+       mvs_94xx_command_active,
+       mvs_94xx_issue_stop,
+       mvs_start_delivery,
+       mvs_rx_update,
+       mvs_int_full,
+       mvs_94xx_assign_reg_set,
+       mvs_94xx_free_reg_set,
+       mvs_get_prd_size,
+       mvs_get_prd_count,
+       mvs_94xx_make_prd,
+       mvs_94xx_detect_porttype,
+       mvs_94xx_oob_done,
+       mvs_94xx_fix_phy_info,
+       NULL,
+       mvs_94xx_phy_set_link_rate,
+       mvs_hw_max_link_rate,
+       mvs_94xx_phy_disable,
+       mvs_94xx_phy_enable,
+       mvs_94xx_phy_reset,
+       NULL,
+       mvs_94xx_clear_active_cmds,
+       mvs_94xx_spi_read_data,
+       mvs_94xx_spi_write_data,
+       mvs_94xx_spi_buildcmd,
+       mvs_94xx_spi_issuecmd,
+       mvs_94xx_spi_waitdataready,
+#ifndef DISABLE_HOTPLUG_DMA_FIX
+       mvs_94xx_fix_dma,
+#endif
+};
+
diff --git a/drivers/scsi/mvsas/mv_94xx.h b/drivers/scsi/mvsas/mv_94xx.h
new file mode 100644 (file)
index 0000000..23ed9b1
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * Marvell 88SE94xx hardware specific head file
+ *
+ * Copyright 2007 Red Hat, Inc.
+ * Copyright 2008 Marvell. <kewei@marvell.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This program is free software; 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.
+ *
+ * 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 _MVS94XX_REG_H_
+#define _MVS94XX_REG_H_
+
+#include <linux/types.h>
+
+#define MAX_LINK_RATE          SAS_LINK_RATE_6_0_GBPS
+
+enum hw_registers {
+       MVS_GBL_CTL             = 0x04,  /* global control */
+       MVS_GBL_INT_STAT        = 0x00,  /* global irq status */
+       MVS_GBL_PI              = 0x0C,  /* ports implemented bitmask */
+
+       MVS_PHY_CTL             = 0x40,  /* SOC PHY Control */
+       MVS_PORTS_IMP           = 0x9C,  /* SOC Port Implemented */
+
+       MVS_GBL_PORT_TYPE       = 0xa0,  /* port type */
+
+       MVS_CTL                 = 0x100, /* SAS/SATA port configuration */
+       MVS_PCS                 = 0x104, /* SAS/SATA port control/status */
+       MVS_CMD_LIST_LO         = 0x108, /* cmd list addr */
+       MVS_CMD_LIST_HI         = 0x10C,
+       MVS_RX_FIS_LO           = 0x110, /* RX FIS list addr */
+       MVS_RX_FIS_HI           = 0x114,
+       MVS_STP_REG_SET_0       = 0x118, /* STP/SATA Register Set Enable */
+       MVS_STP_REG_SET_1       = 0x11C,
+       MVS_TX_CFG              = 0x120, /* TX configuration */
+       MVS_TX_LO               = 0x124, /* TX (delivery) ring addr */
+       MVS_TX_HI               = 0x128,
+
+       MVS_TX_PROD_IDX         = 0x12C, /* TX producer pointer */
+       MVS_TX_CONS_IDX         = 0x130, /* TX consumer pointer (RO) */
+       MVS_RX_CFG              = 0x134, /* RX configuration */
+       MVS_RX_LO               = 0x138, /* RX (completion) ring addr */
+       MVS_RX_HI               = 0x13C,
+       MVS_RX_CONS_IDX         = 0x140, /* RX consumer pointer (RO) */
+
+       MVS_INT_COAL            = 0x148, /* Int coalescing config */
+       MVS_INT_COAL_TMOUT      = 0x14C, /* Int coalescing timeout */
+       MVS_INT_STAT            = 0x150, /* Central int status */
+       MVS_INT_MASK            = 0x154, /* Central int enable */
+       MVS_INT_STAT_SRS_0      = 0x158, /* SATA register set status */
+       MVS_INT_MASK_SRS_0      = 0x15C,
+       MVS_INT_STAT_SRS_1      = 0x160,
+       MVS_INT_MASK_SRS_1      = 0x164,
+       MVS_NON_NCQ_ERR_0       = 0x168, /* SRS Non-specific NCQ Error */
+       MVS_NON_NCQ_ERR_1       = 0x16C,
+       MVS_CMD_ADDR            = 0x170, /* Command register port (addr) */
+       MVS_CMD_DATA            = 0x174, /* Command register port (data) */
+       MVS_MEM_PARITY_ERR      = 0x178, /* Memory parity error */
+
+                                        /* ports 1-3 follow after this */
+       MVS_P0_INT_STAT         = 0x180, /* port0 interrupt status */
+       MVS_P0_INT_MASK         = 0x184, /* port0 interrupt mask */
+                                        /* ports 5-7 follow after this */
+       MVS_P4_INT_STAT         = 0x1A0, /* Port4 interrupt status */
+       MVS_P4_INT_MASK         = 0x1A4, /* Port4 interrupt enable mask */
+
+                                        /* ports 1-3 follow after this */
+       MVS_P0_SER_CTLSTAT      = 0x1D0, /* port0 serial control/status */
+                                        /* ports 5-7 follow after this */
+       MVS_P4_SER_CTLSTAT      = 0x1E0, /* port4 serial control/status */
+
+                                        /* ports 1-3 follow after this */
+       MVS_P0_CFG_ADDR         = 0x200, /* port0 phy register address */
+       MVS_P0_CFG_DATA         = 0x204, /* port0 phy register data */
+                                        /* ports 5-7 follow after this */
+       MVS_P4_CFG_ADDR         = 0x220, /* Port4 config address */
+       MVS_P4_CFG_DATA         = 0x224, /* Port4 config data */
+
+                                        /* phys 1-3 follow after this */
+       MVS_P0_VSR_ADDR         = 0x250, /* phy0 VSR address */
+       MVS_P0_VSR_DATA         = 0x254, /* phy0 VSR data */
+                                        /* phys 1-3 follow after this */
+                                        /* multiplexing */
+       MVS_P4_VSR_ADDR         = 0x250, /* phy4 VSR address */
+       MVS_P4_VSR_DATA         = 0x254, /* phy4 VSR data */
+       MVS_PA_VSR_ADDR         = 0x290, /* All port VSR addr */
+       MVS_PA_VSR_PORT         = 0x294, /* All port VSR data */
+};
+
+enum pci_cfg_registers {
+       PCR_PHY_CTL             = 0x40,
+       PCR_PHY_CTL2            = 0x90,
+       PCR_DEV_CTRL            = 0x78,
+       PCR_LINK_STAT           = 0x82,
+};
+
+/*  SAS/SATA Vendor Specific Port Registers */
+enum sas_sata_vsp_regs {
+       VSR_PHY_STAT            = 0x00 * 4, /* Phy Status */
+       VSR_PHY_MODE1           = 0x01 * 4, /* phy tx */
+       VSR_PHY_MODE2           = 0x02 * 4, /* tx scc */
+       VSR_PHY_MODE3           = 0x03 * 4, /* pll */
+       VSR_PHY_MODE4           = 0x04 * 4, /* VCO */
+       VSR_PHY_MODE5           = 0x05 * 4, /* Rx */
+       VSR_PHY_MODE6           = 0x06 * 4, /* CDR */
+       VSR_PHY_MODE7           = 0x07 * 4, /* Impedance */
+       VSR_PHY_MODE8           = 0x08 * 4, /* Voltage */
+       VSR_PHY_MODE9           = 0x09 * 4, /* Test */
+       VSR_PHY_MODE10          = 0x0A * 4, /* Power */
+       VSR_PHY_MODE11          = 0x0B * 4, /* Phy Mode */
+       VSR_PHY_VS0             = 0x0C * 4, /* Vednor Specific 0 */
+       VSR_PHY_VS1             = 0x0D * 4, /* Vednor Specific 1 */
+};
+
+enum chip_register_bits {
+       PHY_MIN_SPP_PHYS_LINK_RATE_MASK = (0x7 << 8),
+       PHY_MAX_SPP_PHYS_LINK_RATE_MASK = (0x7 << 8),
+       PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET = (12),
+       PHY_NEG_SPP_PHYS_LINK_RATE_MASK =
+                       (0x3 << PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET),
+};
+
+enum pci_interrupt_cause {
+       /*  MAIN_IRQ_CAUSE (R10200) Bits*/
+       IRQ_COM_IN_I2O_IOP0            = (1 << 0),
+       IRQ_COM_IN_I2O_IOP1            = (1 << 1),
+       IRQ_COM_IN_I2O_IOP2            = (1 << 2),
+       IRQ_COM_IN_I2O_IOP3            = (1 << 3),
+       IRQ_COM_OUT_I2O_HOS0           = (1 << 4),
+       IRQ_COM_OUT_I2O_HOS1           = (1 << 5),
+       IRQ_COM_OUT_I2O_HOS2           = (1 << 6),
+       IRQ_COM_OUT_I2O_HOS3           = (1 << 7),
+       IRQ_PCIF_TO_CPU_DRBL0          = (1 << 8),
+       IRQ_PCIF_TO_CPU_DRBL1          = (1 << 9),
+       IRQ_PCIF_TO_CPU_DRBL2          = (1 << 10),
+       IRQ_PCIF_TO_CPU_DRBL3          = (1 << 11),
+       IRQ_PCIF_DRBL0                 = (1 << 12),
+       IRQ_PCIF_DRBL1                 = (1 << 13),
+       IRQ_PCIF_DRBL2                 = (1 << 14),
+       IRQ_PCIF_DRBL3                 = (1 << 15),
+       IRQ_XOR_A                      = (1 << 16),
+       IRQ_XOR_B                      = (1 << 17),
+       IRQ_SAS_A                      = (1 << 18),
+       IRQ_SAS_B                      = (1 << 19),
+       IRQ_CPU_CNTRL                  = (1 << 20),
+       IRQ_GPIO                       = (1 << 21),
+       IRQ_UART                       = (1 << 22),
+       IRQ_SPI                        = (1 << 23),
+       IRQ_I2C                        = (1 << 24),
+       IRQ_SGPIO                      = (1 << 25),
+       IRQ_COM_ERR                    = (1 << 29),
+       IRQ_I2O_ERR                    = (1 << 30),
+       IRQ_PCIE_ERR                   = (1 << 31),
+};
+
+#define MAX_SG_ENTRY           255
+
+struct mvs_prd_imt {
+       __le32                  len:22;
+       u8                      _r_a:2;
+       u8                      misc_ctl:4;
+       u8                      inter_sel:4;
+};
+
+struct mvs_prd {
+       /* 64-bit buffer address */
+       __le64                  addr;
+       /* 22-bit length */
+       struct mvs_prd_imt      im_len;
+} __attribute__ ((packed));
+
+#define SPI_CTRL_REG_94XX              0xc800
+#define SPI_ADDR_REG_94XX              0xc804
+#define SPI_WR_DATA_REG_94XX         0xc808
+#define SPI_RD_DATA_REG_94XX           0xc80c
+#define SPI_CTRL_READ_94XX             (1U << 2)
+#define SPI_ADDR_VLD_94XX              (1U << 1)
+#define SPI_CTRL_SpiStart_94XX         (1U << 0)
+
+#define mv_ffc(x)   ffz(x)
+
+static inline int
+mv_ffc64(u64 v)
+{
+       int i;
+       i = mv_ffc((u32)v);
+       if (i >= 0)
+               return i;
+       i = mv_ffc((u32)(v>>32));
+
+       if (i != 0)
+               return 32 + i;
+
+       return -1;
+}
+
+#define r_reg_set_enable(i) \
+       (((i) > 31) ? mr32(MVS_STP_REG_SET_1) : \
+       mr32(MVS_STP_REG_SET_0))
+
+#define w_reg_set_enable(i, tmp) \
+       (((i) > 31) ? mw32(MVS_STP_REG_SET_1, tmp) : \
+       mw32(MVS_STP_REG_SET_0, tmp))
+
+extern const struct mvs_dispatch mvs_94xx_dispatch;
+#endif
+
diff --git a/drivers/scsi/mvsas/mv_chips.h b/drivers/scsi/mvsas/mv_chips.h
new file mode 100644 (file)
index 0000000..a67e1c4
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Marvell 88SE64xx/88SE94xx register IO interface
+ *
+ * Copyright 2007 Red Hat, Inc.
+ * Copyright 2008 Marvell. <kewei@marvell.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This program is free software; 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.
+ *
+ * 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 _MV_CHIPS_H_
+#define _MV_CHIPS_H_
+
+#define mr32(reg)      readl(regs + reg)
+#define mw32(reg, val) writel((val), regs + reg)
+#define mw32_f(reg, val)       do {                    \
+                               mw32(reg, val); \
+                               mr32(reg);      \
+                       } while (0)
+
+#define iow32(reg, val)        outl(val, (unsigned long)(regs + reg))
+#define ior32(reg)             inl((unsigned long)(regs + reg))
+#define iow16(reg, val)        outw((unsigned long)(val, regs + reg))
+#define ior16(reg)             inw((unsigned long)(regs + reg))
+#define iow8(reg, val)                 outb((unsigned long)(val, regs + reg))
+#define ior8(reg)              inb((unsigned long)(regs + reg))
+
+static inline u32 mvs_cr32(struct mvs_info *mvi, u32 addr)
+{
+       void __iomem *regs = mvi->regs;
+       mw32(MVS_CMD_ADDR, addr);
+       return mr32(MVS_CMD_DATA);
+}
+
+static inline void mvs_cw32(struct mvs_info *mvi, u32 addr, u32 val)
+{
+       void __iomem *regs = mvi->regs;
+       mw32(MVS_CMD_ADDR, addr);
+       mw32(MVS_CMD_DATA, val);
+}
+
+static inline u32 mvs_read_phy_ctl(struct mvs_info *mvi, u32 port)
+{
+       void __iomem *regs = mvi->regs;
+       return (port < 4) ? mr32(MVS_P0_SER_CTLSTAT + port * 4) :
+               mr32(MVS_P4_SER_CTLSTAT + (port - 4) * 4);
+}
+
+static inline void mvs_write_phy_ctl(struct mvs_info *mvi, u32 port, u32 val)
+{
+       void __iomem *regs = mvi->regs;
+       if (port < 4)
+               mw32(MVS_P0_SER_CTLSTAT + port * 4, val);
+       else
+               mw32(MVS_P4_SER_CTLSTAT + (port - 4) * 4, val);
+}
+
+static inline u32 mvs_read_port(struct mvs_info *mvi, u32 off,
+                               u32 off2, u32 port)
+{
+       void __iomem *regs = mvi->regs + off;
+       void __iomem *regs2 = mvi->regs + off2;
+       return (port < 4) ? readl(regs + port * 8) :
+               readl(regs2 + (port - 4) * 8);
+}
+
+static inline void mvs_write_port(struct mvs_info *mvi, u32 off, u32 off2,
+                               u32 port, u32 val)
+{
+       void __iomem *regs = mvi->regs + off;
+       void __iomem *regs2 = mvi->regs + off2;
+       if (port < 4)
+               writel(val, regs + port * 8);
+       else
+               writel(val, regs2 + (port - 4) * 8);
+}
+
+static inline u32 mvs_read_port_cfg_data(struct mvs_info *mvi, u32 port)
+{
+       return mvs_read_port(mvi, MVS_P0_CFG_DATA,
+                       MVS_P4_CFG_DATA, port);
+}
+
+static inline void mvs_write_port_cfg_data(struct mvs_info *mvi,
+                                               u32 port, u32 val)
+{
+       mvs_write_port(mvi, MVS_P0_CFG_DATA,
+                       MVS_P4_CFG_DATA, port, val);
+}
+
+static inline void mvs_write_port_cfg_addr(struct mvs_info *mvi,
+                                               u32 port, u32 addr)
+{
+       mvs_write_port(mvi, MVS_P0_CFG_ADDR,
+                       MVS_P4_CFG_ADDR, port, addr);
+       mdelay(10);
+}
+
+static inline u32 mvs_read_port_vsr_data(struct mvs_info *mvi, u32 port)
+{
+       return mvs_read_port(mvi, MVS_P0_VSR_DATA,
+                       MVS_P4_VSR_DATA, port);
+}
+
+static inline void mvs_write_port_vsr_data(struct mvs_info *mvi,
+                                               u32 port, u32 val)
+{
+       mvs_write_port(mvi, MVS_P0_VSR_DATA,
+                       MVS_P4_VSR_DATA, port, val);
+}
+
+static inline void mvs_write_port_vsr_addr(struct mvs_info *mvi,
+                                               u32 port, u32 addr)
+{
+       mvs_write_port(mvi, MVS_P0_VSR_ADDR,
+                       MVS_P4_VSR_ADDR, port, addr);
+       mdelay(10);
+}
+
+static inline u32 mvs_read_port_irq_stat(struct mvs_info *mvi, u32 port)
+{
+       return mvs_read_port(mvi, MVS_P0_INT_STAT,
+                       MVS_P4_INT_STAT, port);
+}
+
+static inline void mvs_write_port_irq_stat(struct mvs_info *mvi,
+                                               u32 port, u32 val)
+{
+       mvs_write_port(mvi, MVS_P0_INT_STAT,
+                       MVS_P4_INT_STAT, port, val);
+}
+
+static inline u32 mvs_read_port_irq_mask(struct mvs_info *mvi, u32 port)
+{
+       return mvs_read_port(mvi, MVS_P0_INT_MASK,
+                       MVS_P4_INT_MASK, port);
+
+}
+
+static inline void mvs_write_port_irq_mask(struct mvs_info *mvi,
+                                               u32 port, u32 val)
+{
+       mvs_write_port(mvi, MVS_P0_INT_MASK,
+                       MVS_P4_INT_MASK, port, val);
+}
+
+static inline void __devinit mvs_phy_hacks(struct mvs_info *mvi)
+{
+       u32 tmp;
+
+       /* workaround for SATA R-ERR, to ignore phy glitch */
+       tmp = mvs_cr32(mvi, CMD_PHY_TIMER);
+       tmp &= ~(1 << 9);
+       tmp |= (1 << 10);
+       mvs_cw32(mvi, CMD_PHY_TIMER, tmp);
+
+       /* enable retry 127 times */
+       mvs_cw32(mvi, CMD_SAS_CTL1, 0x7f7f);
+
+       /* extend open frame timeout to max */
+       tmp = mvs_cr32(mvi, CMD_SAS_CTL0);
+       tmp &= ~0xffff;
+       tmp |= 0x3fff;
+       mvs_cw32(mvi, CMD_SAS_CTL0, tmp);
+
+       /* workaround for WDTIMEOUT , set to 550 ms */
+       mvs_cw32(mvi, CMD_WD_TIMER, 0x7a0000);
+
+       /* not to halt for different port op during wideport link change */
+       mvs_cw32(mvi, CMD_APP_ERR_CONFIG, 0xffefbf7d);
+
+       /* workaround for Seagate disk not-found OOB sequence, recv
+        * COMINIT before sending out COMWAKE */
+       tmp = mvs_cr32(mvi, CMD_PHY_MODE_21);
+       tmp &= 0x0000ffff;
+       tmp |= 0x00fa0000;
+       mvs_cw32(mvi, CMD_PHY_MODE_21, tmp);
+
+       tmp = mvs_cr32(mvi, CMD_PHY_TIMER);
+       tmp &= 0x1fffffff;
+       tmp |= (2U << 29);      /* 8 ms retry */
+       mvs_cw32(mvi, CMD_PHY_TIMER, tmp);
+}
+
+static inline void mvs_int_sata(struct mvs_info *mvi)
+{
+       u32 tmp;
+       void __iomem *regs = mvi->regs;
+       tmp = mr32(MVS_INT_STAT_SRS_0);
+       if (tmp)
+               mw32(MVS_INT_STAT_SRS_0, tmp);
+       MVS_CHIP_DISP->clear_active_cmds(mvi);
+}
+
+static inline void mvs_int_full(struct mvs_info *mvi)
+{
+       void __iomem *regs = mvi->regs;
+       u32 tmp, stat;
+       int i;
+
+       stat = mr32(MVS_INT_STAT);
+       mvs_int_rx(mvi, false);
+
+       for (i = 0; i < mvi->chip->n_phy; i++) {
+               tmp = (stat >> i) & (CINT_PORT | CINT_PORT_STOPPED);
+               if (tmp)
+                       mvs_int_port(mvi, i, tmp);
+       }
+
+       if (stat & CINT_SRS)
+               mvs_int_sata(mvi);
+
+       mw32(MVS_INT_STAT, stat);
+}
+
+static inline void mvs_start_delivery(struct mvs_info *mvi, u32 tx)
+{
+       void __iomem *regs = mvi->regs;
+       mw32(MVS_TX_PROD_IDX, tx);
+}
+
+static inline u32 mvs_rx_update(struct mvs_info *mvi)
+{
+       void __iomem *regs = mvi->regs;
+       return mr32(MVS_RX_CONS_IDX);
+}
+
+static inline u32 mvs_get_prd_size(void)
+{
+       return sizeof(struct mvs_prd);
+}
+
+static inline u32 mvs_get_prd_count(void)
+{
+       return MAX_SG_ENTRY;
+}
+
+static inline void mvs_show_pcie_usage(struct mvs_info *mvi)
+{
+       u16 link_stat, link_spd;
+       const char *spd[] = {
+               "UnKnown",
+               "2.5",
+               "5.0",
+       };
+       if (mvi->flags & MVF_FLAG_SOC || mvi->id > 0)
+               return;
+
+       pci_read_config_word(mvi->pdev, PCR_LINK_STAT, &link_stat);
+       link_spd = (link_stat & PLS_LINK_SPD) >> PLS_LINK_SPD_OFFS;
+       if (link_spd >= 3)
+               link_spd = 0;
+       dev_printk(KERN_INFO, mvi->dev,
+               "mvsas: PCI-E x%u, Bandwidth Usage: %s Gbps\n",
+              (link_stat & PLS_NEG_LINK_WD) >> PLS_NEG_LINK_WD_OFFS,
+              spd[link_spd]);
+}
+
+static inline u32 mvs_hw_max_link_rate(void)
+{
+       return MAX_LINK_RATE;
+}
+
+#endif  /* _MV_CHIPS_H_ */
+
diff --git a/drivers/scsi/mvsas/mv_defs.h b/drivers/scsi/mvsas/mv_defs.h
new file mode 100644 (file)
index 0000000..f8cb9de
--- /dev/null
@@ -0,0 +1,502 @@
+/*
+ * Marvell 88SE64xx/88SE94xx const head file
+ *
+ * Copyright 2007 Red Hat, Inc.
+ * Copyright 2008 Marvell. <kewei@marvell.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This program is free software; 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.
+ *
+ * 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 _MV_DEFS_H_
+#define _MV_DEFS_H_
+
+
+enum chip_flavors {
+       chip_6320,
+       chip_6440,
+       chip_6485,
+       chip_9480,
+       chip_9180,
+};
+
+/* driver compile-time configuration */
+enum driver_configuration {
+       MVS_SLOTS               = 512,  /* command slots */
+       MVS_TX_RING_SZ          = 1024, /* TX ring size (12-bit) */
+       MVS_RX_RING_SZ          = 1024, /* RX ring size (12-bit) */
+                                       /* software requires power-of-2
+                                          ring size */
+       MVS_SOC_SLOTS           = 64,
+       MVS_SOC_TX_RING_SZ      = MVS_SOC_SLOTS * 2,
+       MVS_SOC_RX_RING_SZ      = MVS_SOC_SLOTS * 2,
+
+       MVS_SLOT_BUF_SZ         = 8192, /* cmd tbl + IU + status + PRD */
+       MVS_SSP_CMD_SZ          = 64,   /* SSP command table buffer size */
+       MVS_ATA_CMD_SZ          = 96,   /* SATA command table buffer size */
+       MVS_OAF_SZ              = 64,   /* Open address frame buffer size */
+       MVS_QUEUE_SIZE  = 32,   /* Support Queue depth */
+       MVS_CAN_QUEUE           = MVS_SLOTS - 2,        /* SCSI Queue depth */
+       MVS_SOC_CAN_QUEUE       = MVS_SOC_SLOTS - 2,
+};
+
+/* unchangeable hardware details */
+enum hardware_details {
+       MVS_MAX_PHYS            = 8,    /* max. possible phys */
+       MVS_MAX_PORTS           = 8,    /* max. possible ports */
+       MVS_SOC_PHYS            = 4,    /* soc phys */
+       MVS_SOC_PORTS           = 4,    /* soc phys */
+       MVS_MAX_DEVICES = 1024, /* max supported device */
+};
+
+/* peripheral registers (BAR2) */
+enum peripheral_registers {
+       SPI_CTL                 = 0x10, /* EEPROM control */
+       SPI_CMD                 = 0x14, /* EEPROM command */
+       SPI_DATA                = 0x18, /* EEPROM data */
+};
+
+enum peripheral_register_bits {
+       TWSI_RDY                = (1U << 7),    /* EEPROM interface ready */
+       TWSI_RD                 = (1U << 4),    /* EEPROM read access */
+
+       SPI_ADDR_MASK           = 0x3ffff,      /* bits 17:0 */
+};
+
+enum hw_register_bits {
+       /* MVS_GBL_CTL */
+       INT_EN                  = (1U << 1),    /* Global int enable */
+       HBA_RST                 = (1U << 0),    /* HBA reset */
+
+       /* MVS_GBL_INT_STAT */
+       INT_XOR                 = (1U << 4),    /* XOR engine event */
+       INT_SAS_SATA            = (1U << 0),    /* SAS/SATA event */
+
+       /* MVS_GBL_PORT_TYPE */                 /* shl for ports 1-3 */
+       SATA_TARGET             = (1U << 16),   /* port0 SATA target enable */
+       MODE_AUTO_DET_PORT7 = (1U << 15),       /* port0 SAS/SATA autodetect */
+       MODE_AUTO_DET_PORT6 = (1U << 14),
+       MODE_AUTO_DET_PORT5 = (1U << 13),
+       MODE_AUTO_DET_PORT4 = (1U << 12),
+       MODE_AUTO_DET_PORT3 = (1U << 11),
+       MODE_AUTO_DET_PORT2 = (1U << 10),
+       MODE_AUTO_DET_PORT1 = (1U << 9),
+       MODE_AUTO_DET_PORT0 = (1U << 8),
+       MODE_AUTO_DET_EN    =   MODE_AUTO_DET_PORT0 | MODE_AUTO_DET_PORT1 |
+                               MODE_AUTO_DET_PORT2 | MODE_AUTO_DET_PORT3 |
+                               MODE_AUTO_DET_PORT4 | MODE_AUTO_DET_PORT5 |
+                               MODE_AUTO_DET_PORT6 | MODE_AUTO_DET_PORT7,
+       MODE_SAS_PORT7_MASK = (1U << 7),  /* port0 SAS(1), SATA(0) mode */
+       MODE_SAS_PORT6_MASK = (1U << 6),
+       MODE_SAS_PORT5_MASK = (1U << 5),
+       MODE_SAS_PORT4_MASK = (1U << 4),
+       MODE_SAS_PORT3_MASK = (1U << 3),
+       MODE_SAS_PORT2_MASK = (1U << 2),
+       MODE_SAS_PORT1_MASK = (1U << 1),
+       MODE_SAS_PORT0_MASK = (1U << 0),
+       MODE_SAS_SATA   =       MODE_SAS_PORT0_MASK | MODE_SAS_PORT1_MASK |
+                               MODE_SAS_PORT2_MASK | MODE_SAS_PORT3_MASK |
+                               MODE_SAS_PORT4_MASK | MODE_SAS_PORT5_MASK |
+                               MODE_SAS_PORT6_MASK | MODE_SAS_PORT7_MASK,
+
+                               /* SAS_MODE value may be
+                                * dictated (in hw) by values
+                                * of SATA_TARGET & AUTO_DET
+                                */
+
+       /* MVS_TX_CFG */
+       TX_EN                   = (1U << 16),   /* Enable TX */
+       TX_RING_SZ_MASK         = 0xfff,        /* TX ring size, bits 11:0 */
+
+       /* MVS_RX_CFG */
+       RX_EN                   = (1U << 16),   /* Enable RX */
+       RX_RING_SZ_MASK         = 0xfff,        /* RX ring size, bits 11:0 */
+
+       /* MVS_INT_COAL */
+       COAL_EN                 = (1U << 16),   /* Enable int coalescing */
+
+       /* MVS_INT_STAT, MVS_INT_MASK */
+       CINT_I2C                = (1U << 31),   /* I2C event */
+       CINT_SW0                = (1U << 30),   /* software event 0 */
+       CINT_SW1                = (1U << 29),   /* software event 1 */
+       CINT_PRD_BC             = (1U << 28),   /* PRD BC err for read cmd */
+       CINT_DMA_PCIE           = (1U << 27),   /* DMA to PCIE timeout */
+       CINT_MEM                = (1U << 26),   /* int mem parity err */
+       CINT_I2C_SLAVE          = (1U << 25),   /* slave I2C event */
+       CINT_SRS                = (1U << 3),    /* SRS event */
+       CINT_CI_STOP            = (1U << 1),    /* cmd issue stopped */
+       CINT_DONE               = (1U << 0),    /* cmd completion */
+
+                                               /* shl for ports 1-3 */
+       CINT_PORT_STOPPED       = (1U << 16),   /* port0 stopped */
+       CINT_PORT               = (1U << 8),    /* port0 event */
+       CINT_PORT_MASK_OFFSET   = 8,
+       CINT_PORT_MASK          = (0xFF << CINT_PORT_MASK_OFFSET),
+       CINT_PHY_MASK_OFFSET    = 4,
+       CINT_PHY_MASK           = (0x0F << CINT_PHY_MASK_OFFSET),
+
+       /* TX (delivery) ring bits */
+       TXQ_CMD_SHIFT           = 29,
+       TXQ_CMD_SSP             = 1,            /* SSP protocol */
+       TXQ_CMD_SMP             = 2,            /* SMP protocol */
+       TXQ_CMD_STP             = 3,            /* STP/SATA protocol */
+       TXQ_CMD_SSP_FREE_LIST   = 4,            /* add to SSP targ free list */
+       TXQ_CMD_SLOT_RESET      = 7,            /* reset command slot */
+       TXQ_MODE_I              = (1U << 28),   /* mode: 0=target,1=initiator */
+       TXQ_MODE_TARGET         = 0,
+       TXQ_MODE_INITIATOR      = 1,
+       TXQ_PRIO_HI             = (1U << 27),   /* priority: 0=normal, 1=high */
+       TXQ_PRI_NORMAL          = 0,
+       TXQ_PRI_HIGH            = 1,
+       TXQ_SRS_SHIFT           = 20,           /* SATA register set */
+       TXQ_SRS_MASK            = 0x7f,
+       TXQ_PHY_SHIFT           = 12,           /* PHY bitmap */
+       TXQ_PHY_MASK            = 0xff,
+       TXQ_SLOT_MASK           = 0xfff,        /* slot number */
+
+       /* RX (completion) ring bits */
+       RXQ_GOOD                = (1U << 23),   /* Response good */
+       RXQ_SLOT_RESET          = (1U << 21),   /* Slot reset complete */
+       RXQ_CMD_RX              = (1U << 20),   /* target cmd received */
+       RXQ_ATTN                = (1U << 19),   /* attention */
+       RXQ_RSP                 = (1U << 18),   /* response frame xfer'd */
+       RXQ_ERR                 = (1U << 17),   /* err info rec xfer'd */
+       RXQ_DONE                = (1U << 16),   /* cmd complete */
+       RXQ_SLOT_MASK           = 0xfff,        /* slot number */
+
+       /* mvs_cmd_hdr bits */
+       MCH_PRD_LEN_SHIFT       = 16,           /* 16-bit PRD table len */
+       MCH_SSP_FR_TYPE_SHIFT   = 13,           /* SSP frame type */
+
+                                               /* SSP initiator only */
+       MCH_SSP_FR_CMD          = 0x0,          /* COMMAND frame */
+
+                                               /* SSP initiator or target */
+       MCH_SSP_FR_TASK         = 0x1,          /* TASK frame */
+
+                                               /* SSP target only */
+       MCH_SSP_FR_XFER_RDY     = 0x4,          /* XFER_RDY frame */
+       MCH_SSP_FR_RESP         = 0x5,          /* RESPONSE frame */
+       MCH_SSP_FR_READ         = 0x6,          /* Read DATA frame(s) */
+       MCH_SSP_FR_READ_RESP    = 0x7,          /* ditto, plus RESPONSE */
+
+       MCH_SSP_MODE_PASSTHRU   = 1,
+       MCH_SSP_MODE_NORMAL     = 0,
+       MCH_PASSTHRU            = (1U << 12),   /* pass-through (SSP) */
+       MCH_FBURST              = (1U << 11),   /* first burst (SSP) */
+       MCH_CHK_LEN             = (1U << 10),   /* chk xfer len (SSP) */
+       MCH_RETRY               = (1U << 9),    /* tport layer retry (SSP) */
+       MCH_PROTECTION          = (1U << 8),    /* protection info rec (SSP) */
+       MCH_RESET               = (1U << 7),    /* Reset (STP/SATA) */
+       MCH_FPDMA               = (1U << 6),    /* First party DMA (STP/SATA) */
+       MCH_ATAPI               = (1U << 5),    /* ATAPI (STP/SATA) */
+       MCH_BIST                = (1U << 4),    /* BIST activate (STP/SATA) */
+       MCH_PMP_MASK            = 0xf,          /* PMP from cmd FIS (STP/SATA)*/
+
+       CCTL_RST                = (1U << 5),    /* port logic reset */
+
+                                               /* 0(LSB first), 1(MSB first) */
+       CCTL_ENDIAN_DATA        = (1U << 3),    /* PRD data */
+       CCTL_ENDIAN_RSP         = (1U << 2),    /* response frame */
+       CCTL_ENDIAN_OPEN        = (1U << 1),    /* open address frame */
+       CCTL_ENDIAN_CMD         = (1U << 0),    /* command table */
+
+       /* MVS_Px_SER_CTLSTAT (per-phy control) */
+       PHY_SSP_RST             = (1U << 3),    /* reset SSP link layer */
+       PHY_BCAST_CHG           = (1U << 2),    /* broadcast(change) notif */
+       PHY_RST_HARD            = (1U << 1),    /* hard reset + phy reset */
+       PHY_RST                 = (1U << 0),    /* phy reset */
+       PHY_READY_MASK          = (1U << 20),
+
+       /* MVS_Px_INT_STAT, MVS_Px_INT_MASK (per-phy events) */
+       PHYEV_DEC_ERR           = (1U << 24),   /* Phy Decoding Error */
+       PHYEV_DCDR_ERR          = (1U << 23),   /* STP Deocder Error */
+       PHYEV_CRC_ERR           = (1U << 22),   /* STP CRC Error */
+       PHYEV_UNASSOC_FIS       = (1U << 19),   /* unassociated FIS rx'd */
+       PHYEV_AN                = (1U << 18),   /* SATA async notification */
+       PHYEV_BIST_ACT          = (1U << 17),   /* BIST activate FIS */
+       PHYEV_SIG_FIS           = (1U << 16),   /* signature FIS */
+       PHYEV_POOF              = (1U << 12),   /* phy ready from 1 -> 0 */
+       PHYEV_IU_BIG            = (1U << 11),   /* IU too long err */
+       PHYEV_IU_SMALL          = (1U << 10),   /* IU too short err */
+       PHYEV_UNK_TAG           = (1U << 9),    /* unknown tag */
+       PHYEV_BROAD_CH          = (1U << 8),    /* broadcast(CHANGE) */
+       PHYEV_COMWAKE           = (1U << 7),    /* COMWAKE rx'd */
+       PHYEV_PORT_SEL          = (1U << 6),    /* port selector present */
+       PHYEV_HARD_RST          = (1U << 5),    /* hard reset rx'd */
+       PHYEV_ID_TMOUT          = (1U << 4),    /* identify timeout */
+       PHYEV_ID_FAIL           = (1U << 3),    /* identify failed */
+       PHYEV_ID_DONE           = (1U << 2),    /* identify done */
+       PHYEV_HARD_RST_DONE     = (1U << 1),    /* hard reset done */
+       PHYEV_RDY_CH            = (1U << 0),    /* phy ready changed state */
+
+       /* MVS_PCS */
+       PCS_EN_SATA_REG_SHIFT   = (16),         /* Enable SATA Register Set */
+       PCS_EN_PORT_XMT_SHIFT   = (12),         /* Enable Port Transmit */
+       PCS_EN_PORT_XMT_SHIFT2  = (8),          /* For 6485 */
+       PCS_SATA_RETRY          = (1U << 8),    /* retry ctl FIS on R_ERR */
+       PCS_RSP_RX_EN           = (1U << 7),    /* raw response rx */
+       PCS_SATA_RETRY_2        = (1U << 6),    /* For 9180 */
+       PCS_SELF_CLEAR          = (1U << 5),    /* self-clearing int mode */
+       PCS_FIS_RX_EN           = (1U << 4),    /* FIS rx enable */
+       PCS_CMD_STOP_ERR        = (1U << 3),    /* cmd stop-on-err enable */
+       PCS_CMD_RST             = (1U << 1),    /* reset cmd issue */
+       PCS_CMD_EN              = (1U << 0),    /* enable cmd issue */
+
+       /* Port n Attached Device Info */
+       PORT_DEV_SSP_TRGT       = (1U << 19),
+       PORT_DEV_SMP_TRGT       = (1U << 18),
+       PORT_DEV_STP_TRGT       = (1U << 17),
+       PORT_DEV_SSP_INIT       = (1U << 11),
+       PORT_DEV_SMP_INIT       = (1U << 10),
+       PORT_DEV_STP_INIT       = (1U << 9),
+       PORT_PHY_ID_MASK        = (0xFFU << 24),
+       PORT_SSP_TRGT_MASK      = (0x1U << 19),
+       PORT_SSP_INIT_MASK      = (0x1U << 11),
+       PORT_DEV_TRGT_MASK      = (0x7U << 17),
+       PORT_DEV_INIT_MASK      = (0x7U << 9),
+       PORT_DEV_TYPE_MASK      = (0x7U << 0),
+
+       /* Port n PHY Status */
+       PHY_RDY                 = (1U << 2),
+       PHY_DW_SYNC             = (1U << 1),
+       PHY_OOB_DTCTD           = (1U << 0),
+
+       /* VSR */
+       /* PHYMODE 6 (CDB) */
+       PHY_MODE6_LATECLK       = (1U << 29),   /* Lock Clock */
+       PHY_MODE6_DTL_SPEED     = (1U << 27),   /* Digital Loop Speed */
+       PHY_MODE6_FC_ORDER      = (1U << 26),   /* Fibre Channel Mode Order*/
+       PHY_MODE6_MUCNT_EN      = (1U << 24),   /* u Count Enable */
+       PHY_MODE6_SEL_MUCNT_LEN = (1U << 22),   /* Training Length Select */
+       PHY_MODE6_SELMUPI       = (1U << 20),   /* Phase Multi Select (init) */
+       PHY_MODE6_SELMUPF       = (1U << 18),   /* Phase Multi Select (final) */
+       PHY_MODE6_SELMUFF       = (1U << 16),   /* Freq Loop Multi Sel(final) */
+       PHY_MODE6_SELMUFI       = (1U << 14),   /* Freq Loop Multi Sel(init) */
+       PHY_MODE6_FREEZE_LOOP   = (1U << 12),   /* Freeze Rx CDR Loop */
+       PHY_MODE6_INT_RXFOFFS   = (1U << 3),    /* Rx CDR Freq Loop Enable */
+       PHY_MODE6_FRC_RXFOFFS   = (1U << 2),    /* Initial Rx CDR Offset */
+       PHY_MODE6_STAU_0D8      = (1U << 1),    /* Rx CDR Freq Loop Saturate */
+       PHY_MODE6_RXSAT_DIS     = (1U << 0),    /* Saturate Ctl */
+};
+
+/* SAS/SATA configuration port registers, aka phy registers */
+enum sas_sata_config_port_regs {
+       PHYR_IDENTIFY           = 0x00, /* info for IDENTIFY frame */
+       PHYR_ADDR_LO            = 0x04, /* my SAS address (low) */
+       PHYR_ADDR_HI            = 0x08, /* my SAS address (high) */
+       PHYR_ATT_DEV_INFO       = 0x0C, /* attached device info */
+       PHYR_ATT_ADDR_LO        = 0x10, /* attached dev SAS addr (low) */
+       PHYR_ATT_ADDR_HI        = 0x14, /* attached dev SAS addr (high) */
+       PHYR_SATA_CTL           = 0x18, /* SATA control */
+       PHYR_PHY_STAT           = 0x1C, /* PHY status */
+       PHYR_SATA_SIG0  = 0x20, /*port SATA signature FIS(Byte 0-3) */
+       PHYR_SATA_SIG1  = 0x24, /*port SATA signature FIS(Byte 4-7) */
+       PHYR_SATA_SIG2  = 0x28, /*port SATA signature FIS(Byte 8-11) */
+       PHYR_SATA_SIG3  = 0x2c, /*port SATA signature FIS(Byte 12-15) */
+       PHYR_R_ERR_COUNT        = 0x30, /* port R_ERR count register */
+       PHYR_CRC_ERR_COUNT      = 0x34, /* port CRC error count register */
+       PHYR_WIDE_PORT  = 0x38, /* wide port participating */
+       PHYR_CURRENT0           = 0x80, /* current connection info 0 */
+       PHYR_CURRENT1           = 0x84, /* current connection info 1 */
+       PHYR_CURRENT2           = 0x88, /* current connection info 2 */
+       CONFIG_ID_FRAME0       = 0x100, /* Port device ID frame register 0 */
+       CONFIG_ID_FRAME1       = 0x104, /* Port device ID frame register 1 */
+       CONFIG_ID_FRAME2       = 0x108, /* Port device ID frame register 2 */
+       CONFIG_ID_FRAME3       = 0x10c, /* Port device ID frame register 3 */
+       CONFIG_ID_FRAME4       = 0x110, /* Port device ID frame register 4 */
+       CONFIG_ID_FRAME5       = 0x114, /* Port device ID frame register 5 */
+       CONFIG_ID_FRAME6       = 0x118, /* Port device ID frame register 6 */
+       CONFIG_ATT_ID_FRAME0   = 0x11c, /* attached ID frame register 0 */
+       CONFIG_ATT_ID_FRAME1   = 0x120, /* attached ID frame register 1 */
+       CONFIG_ATT_ID_FRAME2   = 0x124, /* attached ID frame register 2 */
+       CONFIG_ATT_ID_FRAME3   = 0x128, /* attached ID frame register 3 */
+       CONFIG_ATT_ID_FRAME4   = 0x12c, /* attached ID frame register 4 */
+       CONFIG_ATT_ID_FRAME5   = 0x130, /* attached ID frame register 5 */
+       CONFIG_ATT_ID_FRAME6   = 0x134, /* attached ID frame register 6 */
+};
+
+enum sas_cmd_port_registers {
+       CMD_CMRST_OOB_DET       = 0x100, /* COMRESET OOB detect register */
+       CMD_CMWK_OOB_DET        = 0x104, /* COMWAKE OOB detect register */
+       CMD_CMSAS_OOB_DET       = 0x108, /* COMSAS OOB detect register */
+       CMD_BRST_OOB_DET        = 0x10c, /* burst OOB detect register */
+       CMD_OOB_SPACE   = 0x110, /* OOB space control register */
+       CMD_OOB_BURST   = 0x114, /* OOB burst control register */
+       CMD_PHY_TIMER           = 0x118, /* PHY timer control register */
+       CMD_PHY_CONFIG0 = 0x11c, /* PHY config register 0 */
+       CMD_PHY_CONFIG1 = 0x120, /* PHY config register 1 */
+       CMD_SAS_CTL0            = 0x124, /* SAS control register 0 */
+       CMD_SAS_CTL1            = 0x128, /* SAS control register 1 */
+       CMD_SAS_CTL2            = 0x12c, /* SAS control register 2 */
+       CMD_SAS_CTL3            = 0x130, /* SAS control register 3 */
+       CMD_ID_TEST             = 0x134, /* ID test register */
+       CMD_PL_TIMER            = 0x138, /* PL timer register */
+       CMD_WD_TIMER            = 0x13c, /* WD timer register */
+       CMD_PORT_SEL_COUNT      = 0x140, /* port selector count register */
+       CMD_APP_MEM_CTL = 0x144, /* Application Memory Control */
+       CMD_XOR_MEM_CTL = 0x148, /* XOR Block Memory Control */
+       CMD_DMA_MEM_CTL = 0x14c, /* DMA Block Memory Control */
+       CMD_PORT_MEM_CTL0       = 0x150, /* Port Memory Control 0 */
+       CMD_PORT_MEM_CTL1       = 0x154, /* Port Memory Control 1 */
+       CMD_SATA_PORT_MEM_CTL0  = 0x158, /* SATA Port Memory Control 0 */
+       CMD_SATA_PORT_MEM_CTL1  = 0x15c, /* SATA Port Memory Control 1 */
+       CMD_XOR_MEM_BIST_CTL    = 0x160, /* XOR Memory BIST Control */
+       CMD_XOR_MEM_BIST_STAT   = 0x164, /* XOR Memroy BIST Status */
+       CMD_DMA_MEM_BIST_CTL    = 0x168, /* DMA Memory BIST Control */
+       CMD_DMA_MEM_BIST_STAT   = 0x16c, /* DMA Memory BIST Status */
+       CMD_PORT_MEM_BIST_CTL   = 0x170, /* Port Memory BIST Control */
+       CMD_PORT_MEM_BIST_STAT0 = 0x174, /* Port Memory BIST Status 0 */
+       CMD_PORT_MEM_BIST_STAT1 = 0x178, /* Port Memory BIST Status 1 */
+       CMD_STP_MEM_BIST_CTL    = 0x17c, /* STP Memory BIST Control */
+       CMD_STP_MEM_BIST_STAT0  = 0x180, /* STP Memory BIST Status 0 */
+       CMD_STP_MEM_BIST_STAT1  = 0x184, /* STP Memory BIST Status 1 */
+       CMD_RESET_COUNT         = 0x188, /* Reset Count */
+       CMD_MONTR_DATA_SEL      = 0x18C, /* Monitor Data/Select */
+       CMD_PLL_PHY_CONFIG      = 0x190, /* PLL/PHY Configuration */
+       CMD_PHY_CTL             = 0x194, /* PHY Control and Status */
+       CMD_PHY_TEST_COUNT0     = 0x198, /* Phy Test Count 0 */
+       CMD_PHY_TEST_COUNT1     = 0x19C, /* Phy Test Count 1 */
+       CMD_PHY_TEST_COUNT2     = 0x1A0, /* Phy Test Count 2 */
+       CMD_APP_ERR_CONFIG      = 0x1A4, /* Application Error Configuration */
+       CMD_PND_FIFO_CTL0       = 0x1A8, /* Pending FIFO Control 0 */
+       CMD_HOST_CTL            = 0x1AC, /* Host Control Status */
+       CMD_HOST_WR_DATA        = 0x1B0, /* Host Write Data */
+       CMD_HOST_RD_DATA        = 0x1B4, /* Host Read Data */
+       CMD_PHY_MODE_21         = 0x1B8, /* Phy Mode 21 */
+       CMD_SL_MODE0            = 0x1BC, /* SL Mode 0 */
+       CMD_SL_MODE1            = 0x1C0, /* SL Mode 1 */
+       CMD_PND_FIFO_CTL1       = 0x1C4, /* Pending FIFO Control 1 */
+};
+
+enum mvs_info_flags {
+       MVF_MSI         = (1U << 0),    /* MSI is enabled */
+       MVF_PHY_PWR_FIX = (1U << 1),    /* bug workaround */
+       MVF_FLAG_SOC            = (1U << 2),    /* SoC integrated controllers */
+};
+
+enum mvs_event_flags {
+       PHY_PLUG_EVENT  = (3U),
+       PHY_PLUG_IN             = (1U << 0),    /* phy plug in */
+       PHY_PLUG_OUT            = (1U << 1),    /* phy plug out */
+};
+
+enum mvs_port_type {
+       PORT_TGT_MASK   =  (1U << 5),
+       PORT_INIT_PORT  =  (1U << 4),
+       PORT_TGT_PORT   =  (1U << 3),
+       PORT_INIT_TGT_PORT = (PORT_INIT_PORT | PORT_TGT_PORT),
+       PORT_TYPE_SAS   =  (1U << 1),
+       PORT_TYPE_SATA  =  (1U << 0),
+};
+
+/* Command Table Format */
+enum ct_format {
+       /* SSP */
+       SSP_F_H         =  0x00,
+       SSP_F_IU        =  0x18,
+       SSP_F_MAX       =  0x4D,
+       /* STP */
+       STP_CMD_FIS     =  0x00,
+       STP_ATAPI_CMD   =  0x40,
+       STP_F_MAX       =  0x10,
+       /* SMP */
+       SMP_F_T         =  0x00,
+       SMP_F_DEP       =  0x01,
+       SMP_F_MAX       =  0x101,
+};
+
+enum status_buffer {
+       SB_EIR_OFF      =  0x00,        /* Error Information Record */
+       SB_RFB_OFF      =  0x08,        /* Response Frame Buffer */
+       SB_RFB_MAX      =  0x400,       /* RFB size*/
+};
+
+enum error_info_rec {
+       CMD_ISS_STPD    = (1U << 31),   /* Cmd Issue Stopped */
+       CMD_PI_ERR      = (1U << 30),   /* Protection info error.  see flags2 */
+       RSP_OVER        = (1U << 29),   /* rsp buffer overflow */
+       RETRY_LIM       = (1U << 28),   /* FIS/frame retry limit exceeded */
+       UNK_FIS         = (1U << 27),   /* unknown FIS */
+       DMA_TERM        = (1U << 26),   /* DMA terminate primitive rx'd */
+       SYNC_ERR        = (1U << 25),   /* SYNC rx'd during frame xmit */
+       TFILE_ERR       = (1U << 24),   /* SATA taskfile Error bit set */
+       R_ERR           = (1U << 23),   /* SATA returned R_ERR prim */
+       RD_OFS          = (1U << 20),   /* Read DATA frame invalid offset */
+       XFER_RDY_OFS    = (1U << 19),   /* XFER_RDY offset error */
+       UNEXP_XFER_RDY  = (1U << 18),   /* unexpected XFER_RDY error */
+       DATA_OVER_UNDER = (1U << 16),   /* data overflow/underflow */
+       INTERLOCK       = (1U << 15),   /* interlock error */
+       NAK             = (1U << 14),   /* NAK rx'd */
+       ACK_NAK_TO      = (1U << 13),   /* ACK/NAK timeout */
+       CXN_CLOSED      = (1U << 12),   /* cxn closed w/out ack/nak */
+       OPEN_TO         = (1U << 11),   /* I_T nexus lost, open cxn timeout */
+       PATH_BLOCKED    = (1U << 10),   /* I_T nexus lost, pathway blocked */
+       NO_DEST         = (1U << 9),    /* I_T nexus lost, no destination */
+       STP_RES_BSY     = (1U << 8),    /* STP resources busy */
+       BREAK           = (1U << 7),    /* break received */
+       BAD_DEST        = (1U << 6),    /* bad destination */
+       BAD_PROTO       = (1U << 5),    /* protocol not supported */
+       BAD_RATE        = (1U << 4),    /* cxn rate not supported */
+       WRONG_DEST      = (1U << 3),    /* wrong destination error */
+       CREDIT_TO       = (1U << 2),    /* credit timeout */
+       WDOG_TO         = (1U << 1),    /* watchdog timeout */
+       BUF_PAR         = (1U << 0),    /* buffer parity error */
+};
+
+enum error_info_rec_2 {
+       SLOT_BSY_ERR    = (1U << 31),   /* Slot Busy Error */
+       GRD_CHK_ERR     = (1U << 14),   /* Guard Check Error */
+       APP_CHK_ERR     = (1U << 13),   /* Application Check error */
+       REF_CHK_ERR     = (1U << 12),   /* Reference Check Error */
+       USR_BLK_NM      = (1U << 0),    /* User Block Number */
+};
+
+enum pci_cfg_register_bits {
+       PCTL_PWR_OFF    = (0xFU << 24),
+       PCTL_COM_ON     = (0xFU << 20),
+       PCTL_LINK_RST   = (0xFU << 16),
+       PCTL_LINK_OFFS  = (16),
+       PCTL_PHY_DSBL   = (0xFU << 12),
+       PCTL_PHY_DSBL_OFFS      = (12),
+       PRD_REQ_SIZE    = (0x4000),
+       PRD_REQ_MASK    = (0x00007000),
+       PLS_NEG_LINK_WD         = (0x3FU << 4),
+       PLS_NEG_LINK_WD_OFFS    = 4,
+       PLS_LINK_SPD            = (0x0FU << 0),
+       PLS_LINK_SPD_OFFS       = 0,
+};
+
+enum open_frame_protocol {
+       PROTOCOL_SMP    = 0x0,
+       PROTOCOL_SSP    = 0x1,
+       PROTOCOL_STP    = 0x2,
+};
+
+/* define for response frame datapres field */
+enum datapres_field {
+       NO_DATA         = 0,
+       RESPONSE_DATA   = 1,
+       SENSE_DATA      = 2,
+};
+
+/* define task management IU */
+struct mvs_tmf_task{
+       u8 tmf;
+       u16 tag_of_task_to_be_managed;
+};
+#endif
diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c
new file mode 100644 (file)
index 0000000..8646a19
--- /dev/null
@@ -0,0 +1,703 @@
+/*
+ * Marvell 88SE64xx/88SE94xx pci init
+ *
+ * Copyright 2007 Red Hat, Inc.
+ * Copyright 2008 Marvell. <kewei@marvell.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This program is free software; 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.
+ *
+ * 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 "mv_sas.h"
+
+static struct scsi_transport_template *mvs_stt;
+static const struct mvs_chip_info mvs_chips[] = {
+       [chip_6320] =   { 1, 2, 0x400, 17, 16,  9, &mvs_64xx_dispatch, },
+       [chip_6440] =   { 1, 4, 0x400, 17, 16,  9, &mvs_64xx_dispatch, },
+       [chip_6485] =   { 1, 8, 0x800, 33, 32, 10, &mvs_64xx_dispatch, },
+       [chip_9180] =   { 2, 4, 0x800, 17, 64,  9, &mvs_94xx_dispatch, },
+       [chip_9480] =   { 2, 4, 0x800, 17, 64,  9, &mvs_94xx_dispatch, },
+};
+
+#define SOC_SAS_NUM 2
+
+static struct scsi_host_template mvs_sht = {
+       .module                 = THIS_MODULE,
+       .name                   = DRV_NAME,
+       .queuecommand           = sas_queuecommand,
+       .target_alloc           = sas_target_alloc,
+       .slave_configure        = mvs_slave_configure,
+       .slave_destroy          = sas_slave_destroy,
+       .scan_finished          = mvs_scan_finished,
+       .scan_start             = mvs_scan_start,
+       .change_queue_depth     = sas_change_queue_depth,
+       .change_queue_type      = sas_change_queue_type,
+       .bios_param             = sas_bios_param,
+       .can_queue              = 1,
+       .cmd_per_lun            = 1,
+       .this_id                = -1,
+       .sg_tablesize           = SG_ALL,
+       .max_sectors            = SCSI_DEFAULT_MAX_SECTORS,
+       .use_clustering         = ENABLE_CLUSTERING,
+       .eh_device_reset_handler        = sas_eh_device_reset_handler,
+       .eh_bus_reset_handler   = sas_eh_bus_reset_handler,
+       .slave_alloc            = mvs_slave_alloc,
+       .target_destroy         = sas_target_destroy,
+       .ioctl                  = sas_ioctl,
+};
+
+static struct sas_domain_function_template mvs_transport_ops = {
+       .lldd_dev_found         = mvs_dev_found,
+       .lldd_dev_gone  = mvs_dev_gone,
+
+       .lldd_execute_task      = mvs_queue_command,
+       .lldd_control_phy       = mvs_phy_control,
+
+       .lldd_abort_task        = mvs_abort_task,
+       .lldd_abort_task_set    = mvs_abort_task_set,
+       .lldd_clear_aca         = mvs_clear_aca,
+       .lldd_clear_task_set    = mvs_clear_task_set,
+       .lldd_I_T_nexus_reset   = mvs_I_T_nexus_reset,
+       .lldd_lu_reset          = mvs_lu_reset,
+       .lldd_query_task        = mvs_query_task,
+
+       .lldd_port_formed       = mvs_port_formed,
+       .lldd_port_deformed     = mvs_port_deformed,
+
+};
+
+static void __devinit mvs_phy_init(struct mvs_info *mvi, int phy_id)
+{
+       struct mvs_phy *phy = &mvi->phy[phy_id];
+       struct asd_sas_phy *sas_phy = &phy->sas_phy;
+
+       phy->mvi = mvi;
+       init_timer(&phy->timer);
+       sas_phy->enabled = (phy_id < mvi->chip->n_phy) ? 1 : 0;
+       sas_phy->class = SAS;
+       sas_phy->iproto = SAS_PROTOCOL_ALL;
+       sas_phy->tproto = 0;
+       sas_phy->type = PHY_TYPE_PHYSICAL;
+       sas_phy->role = PHY_ROLE_INITIATOR;
+       sas_phy->oob_mode = OOB_NOT_CONNECTED;
+       sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN;
+
+       sas_phy->id = phy_id;
+       sas_phy->sas_addr = &mvi->sas_addr[0];
+       sas_phy->frame_rcvd = &phy->frame_rcvd[0];
+       sas_phy->ha = (struct sas_ha_struct *)mvi->shost->hostdata;
+       sas_phy->lldd_phy = phy;
+}
+
+static void mvs_free(struct mvs_info *mvi)
+{
+       int i;
+       struct mvs_wq *mwq;
+       int slot_nr;
+
+       if (!mvi)
+               return;
+
+       if (mvi->flags & MVF_FLAG_SOC)
+               slot_nr = MVS_SOC_SLOTS;
+       else
+               slot_nr = MVS_SLOTS;
+
+       for (i = 0; i < mvi->tags_num; i++) {
+               struct mvs_slot_info *slot = &mvi->slot_info[i];
+               if (slot->buf)
+                       dma_free_coherent(mvi->dev, MVS_SLOT_BUF_SZ,
+                                         slot->buf, slot->buf_dma);
+       }
+
+       if (mvi->tx)
+               dma_free_coherent(mvi->dev,
+                                 sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ,
+                                 mvi->tx, mvi->tx_dma);
+       if (mvi->rx_fis)
+               dma_free_coherent(mvi->dev, MVS_RX_FISL_SZ,
+                                 mvi->rx_fis, mvi->rx_fis_dma);
+       if (mvi->rx)
+               dma_free_coherent(mvi->dev,
+                                 sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1),
+                                 mvi->rx, mvi->rx_dma);
+       if (mvi->slot)
+               dma_free_coherent(mvi->dev,
+                                 sizeof(*mvi->slot) * slot_nr,
+                                 mvi->slot, mvi->slot_dma);
+#ifndef DISABLE_HOTPLUG_DMA_FIX
+       if (mvi->bulk_buffer)
+               dma_free_coherent(mvi->dev, TRASH_BUCKET_SIZE,
+                                 mvi->bulk_buffer, mvi->bulk_buffer_dma);
+#endif
+
+       MVS_CHIP_DISP->chip_iounmap(mvi);
+       if (mvi->shost)
+               scsi_host_put(mvi->shost);
+       list_for_each_entry(mwq, &mvi->wq_list, entry)
+               cancel_delayed_work(&mwq->work_q);
+       kfree(mvi);
+}
+
+#ifdef MVS_USE_TASKLET
+struct tasklet_struct  mv_tasklet;
+static void mvs_tasklet(unsigned long opaque)
+{
+       unsigned long flags;
+       u32 stat;
+       u16 core_nr, i = 0;
+
+       struct mvs_info *mvi;
+       struct sas_ha_struct *sha = (struct sas_ha_struct *)opaque;
+
+       core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
+       mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0];
+
+       if (unlikely(!mvi))
+               BUG_ON(1);
+
+       for (i = 0; i < core_nr; i++) {
+               mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i];
+               stat = MVS_CHIP_DISP->isr_status(mvi, mvi->irq);
+               if (stat)
+                       MVS_CHIP_DISP->isr(mvi, mvi->irq, stat);
+       }
+
+}
+#endif
+
+static irqreturn_t mvs_interrupt(int irq, void *opaque)
+{
+       u32 core_nr, i = 0;
+       u32 stat;
+       struct mvs_info *mvi;
+       struct sas_ha_struct *sha = opaque;
+
+       core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
+       mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0];
+
+       if (unlikely(!mvi))
+               return IRQ_NONE;
+
+       stat = MVS_CHIP_DISP->isr_status(mvi, irq);
+       if (!stat)
+               return IRQ_NONE;
+
+#ifdef MVS_USE_TASKLET
+       tasklet_schedule(&mv_tasklet);
+#else
+       for (i = 0; i < core_nr; i++) {
+               mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i];
+               MVS_CHIP_DISP->isr(mvi, irq, stat);
+       }
+#endif
+       return IRQ_HANDLED;
+}
+
+static int __devinit mvs_alloc(struct mvs_info *mvi, struct Scsi_Host *shost)
+{
+       int i, slot_nr;
+
+       if (mvi->flags & MVF_FLAG_SOC)
+               slot_nr = MVS_SOC_SLOTS;
+       else
+               slot_nr = MVS_SLOTS;
+
+       spin_lock_init(&mvi->lock);
+       for (i = 0; i < mvi->chip->n_phy; i++) {
+               mvs_phy_init(mvi, i);
+               mvi->port[i].wide_port_phymap = 0;
+               mvi->port[i].port_attached = 0;
+               INIT_LIST_HEAD(&mvi->port[i].list);
+       }
+       for (i = 0; i < MVS_MAX_DEVICES; i++) {
+               mvi->devices[i].taskfileset = MVS_ID_NOT_MAPPED;
+               mvi->devices[i].dev_type = NO_DEVICE;
+               mvi->devices[i].device_id = i;
+               mvi->devices[i].dev_status = MVS_DEV_NORMAL;
+       }
+
+       /*
+        * alloc and init our DMA areas
+        */
+       mvi->tx = dma_alloc_coherent(mvi->dev,
+                                    sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ,
+                                    &mvi->tx_dma, GFP_KERNEL);
+       if (!mvi->tx)
+               goto err_out;
+       memset(mvi->tx, 0, sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ);
+       mvi->rx_fis = dma_alloc_coherent(mvi->dev, MVS_RX_FISL_SZ,
+                                        &mvi->rx_fis_dma, GFP_KERNEL);
+       if (!mvi->rx_fis)
+               goto err_out;
+       memset(mvi->rx_fis, 0, MVS_RX_FISL_SZ);
+
+       mvi->rx = dma_alloc_coherent(mvi->dev,
+                                    sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1),
+                                    &mvi->rx_dma, GFP_KERNEL);
+       if (!mvi->rx)
+               goto err_out;
+       memset(mvi->rx, 0, sizeof(*mvi->rx) * (MVS_RX_RING_SZ + 1));
+       mvi->rx[0] = cpu_to_le32(0xfff);
+       mvi->rx_cons = 0xfff;
+
+       mvi->slot = dma_alloc_coherent(mvi->dev,
+                                      sizeof(*mvi->slot) * slot_nr,
+                                      &mvi->slot_dma, GFP_KERNEL);
+       if (!mvi->slot)
+               goto err_out;
+       memset(mvi->slot, 0, sizeof(*mvi->slot) * slot_nr);
+
+#ifndef DISABLE_HOTPLUG_DMA_FIX
+       mvi->bulk_buffer = dma_alloc_coherent(mvi->dev,
+                                      TRASH_BUCKET_SIZE,
+                                      &mvi->bulk_buffer_dma, GFP_KERNEL);
+       if (!mvi->bulk_buffer)
+               goto err_out;
+#endif
+       for (i = 0; i < slot_nr; i++) {
+               struct mvs_slot_info *slot = &mvi->slot_info[i];
+
+               slot->buf = dma_alloc_coherent(mvi->dev, MVS_SLOT_BUF_SZ,
+                                              &slot->buf_dma, GFP_KERNEL);
+               if (!slot->buf) {
+                       printk(KERN_DEBUG"failed to allocate slot->buf.\n");
+                       goto err_out;
+               }
+               memset(slot->buf, 0, MVS_SLOT_BUF_SZ);
+               ++mvi->tags_num;
+       }
+       /* Initialize tags */
+       mvs_tag_init(mvi);
+       return 0;
+err_out:
+       return 1;
+}
+
+
+int mvs_ioremap(struct mvs_info *mvi, int bar, int bar_ex)
+{
+       unsigned long res_start, res_len, res_flag, res_flag_ex = 0;
+       struct pci_dev *pdev = mvi->pdev;
+       if (bar_ex != -1) {
+               /*
+                * ioremap main and peripheral registers
+                */
+               res_start = pci_resource_start(pdev, bar_ex);
+               res_len = pci_resource_len(pdev, bar_ex);
+               if (!res_start || !res_len)
+                       goto err_out;
+
+               res_flag_ex = pci_resource_flags(pdev, bar_ex);
+               if (res_flag_ex & IORESOURCE_MEM) {
+                       if (res_flag_ex & IORESOURCE_CACHEABLE)
+                               mvi->regs_ex = ioremap(res_start, res_len);
+                       else
+                               mvi->regs_ex = ioremap_nocache(res_start,
+                                               res_len);
+               } else
+                       mvi->regs_ex = (void *)res_start;
+               if (!mvi->regs_ex)
+                       goto err_out;
+       }
+
+       res_start = pci_resource_start(pdev, bar);
+       res_len = pci_resource_len(pdev, bar);
+       if (!res_start || !res_len)
+               goto err_out;
+
+       res_flag = pci_resource_flags(pdev, bar);
+       if (res_flag & IORESOURCE_CACHEABLE)
+               mvi->regs = ioremap(res_start, res_len);
+       else
+               mvi->regs = ioremap_nocache(res_start, res_len);
+
+       if (!mvi->regs) {
+               if (mvi->regs_ex && (res_flag_ex & IORESOURCE_MEM))
+                       iounmap(mvi->regs_ex);
+               mvi->regs_ex = NULL;
+               goto err_out;
+       }
+
+       return 0;
+err_out:
+       return -1;
+}
+
+void mvs_iounmap(void __iomem *regs)
+{
+       iounmap(regs);
+}
+
+static struct mvs_info *__devinit mvs_pci_alloc(struct pci_dev *pdev,
+                               const struct pci_device_id *ent,
+                               struct Scsi_Host *shost, unsigned int id)
+{
+       struct mvs_info *mvi;
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+
+       mvi = kzalloc(sizeof(*mvi) + MVS_SLOTS * sizeof(struct mvs_slot_info),
+                       GFP_KERNEL);
+       if (!mvi)
+               return NULL;
+
+       mvi->pdev = pdev;
+       mvi->dev = &pdev->dev;
+       mvi->chip_id = ent->driver_data;
+       mvi->chip = &mvs_chips[mvi->chip_id];
+       INIT_LIST_HEAD(&mvi->wq_list);
+       mvi->irq = pdev->irq;
+
+       ((struct mvs_prv_info *)sha->lldd_ha)->mvi[id] = mvi;
+       ((struct mvs_prv_info *)sha->lldd_ha)->n_phy = mvi->chip->n_phy;
+
+       mvi->id = id;
+       mvi->sas = sha;
+       mvi->shost = shost;
+#ifdef MVS_USE_TASKLET
+       tasklet_init(&mv_tasklet, mvs_tasklet, (unsigned long)sha);
+#endif
+
+       if (MVS_CHIP_DISP->chip_ioremap(mvi))
+               goto err_out;
+       if (!mvs_alloc(mvi, shost))
+               return mvi;
+err_out:
+       mvs_free(mvi);
+       return NULL;
+}
+
+/* move to PCI layer or libata core? */
+static int pci_go_64(struct pci_dev *pdev)
+{
+       int rc;
+
+       if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
+               rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
+               if (rc) {
+                       rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+                       if (rc) {
+                               dev_printk(KERN_ERR, &pdev->dev,
+                                          "64-bit DMA enable failed\n");
+                               return rc;
+                       }
+               }
+       } else {
+               rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+               if (rc) {
+                       dev_printk(KERN_ERR, &pdev->dev,
+                                  "32-bit DMA enable failed\n");
+                       return rc;
+               }
+               rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+               if (rc) {
+                       dev_printk(KERN_ERR, &pdev->dev,
+                                  "32-bit consistent DMA enable failed\n");
+                       return rc;
+               }
+       }
+
+       return rc;
+}
+
+static int __devinit mvs_prep_sas_ha_init(struct Scsi_Host *shost,
+                               const struct mvs_chip_info *chip_info)
+{
+       int phy_nr, port_nr; unsigned short core_nr;
+       struct asd_sas_phy **arr_phy;
+       struct asd_sas_port **arr_port;
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+
+       core_nr = chip_info->n_host;
+       phy_nr  = core_nr * chip_info->n_phy;
+       port_nr = phy_nr;
+
+       memset(sha, 0x00, sizeof(struct sas_ha_struct));
+       arr_phy  = kcalloc(phy_nr, sizeof(void *), GFP_KERNEL);
+       arr_port = kcalloc(port_nr, sizeof(void *), GFP_KERNEL);
+       if (!arr_phy || !arr_port)
+               goto exit_free;
+
+       sha->sas_phy = arr_phy;
+       sha->sas_port = arr_port;
+
+       sha->lldd_ha = kzalloc(sizeof(struct mvs_prv_info), GFP_KERNEL);
+       if (!sha->lldd_ha)
+               goto exit_free;
+
+       ((struct mvs_prv_info *)sha->lldd_ha)->n_host = core_nr;
+
+       shost->transportt = mvs_stt;
+       shost->max_id = 128;
+       shost->max_lun = ~0;
+       shost->max_channel = 1;
+       shost->max_cmd_len = 16;
+
+       return 0;
+exit_free:
+       kfree(arr_phy);
+       kfree(arr_port);
+       return -1;
+
+}
+
+static void  __devinit mvs_post_sas_ha_init(struct Scsi_Host *shost,
+                       const struct mvs_chip_info *chip_info)
+{
+       int can_queue, i = 0, j = 0;
+       struct mvs_info *mvi = NULL;
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+       unsigned short nr_core = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
+
+       for (j = 0; j < nr_core; j++) {
+               mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[j];
+               for (i = 0; i < chip_info->n_phy; i++) {
+                       sha->sas_phy[j * chip_info->n_phy  + i] =
+                               &mvi->phy[i].sas_phy;
+                       sha->sas_port[j * chip_info->n_phy + i] =
+                               &mvi->port[i].sas_port;
+               }
+       }
+
+       sha->sas_ha_name = DRV_NAME;
+       sha->dev = mvi->dev;
+       sha->lldd_module = THIS_MODULE;
+       sha->sas_addr = &mvi->sas_addr[0];
+
+       sha->num_phys = nr_core * chip_info->n_phy;
+
+       sha->lldd_max_execute_num = 1;
+
+       if (mvi->flags & MVF_FLAG_SOC)
+               can_queue = MVS_SOC_CAN_QUEUE;
+       else
+               can_queue = MVS_CAN_QUEUE;
+
+       sha->lldd_queue_size = can_queue;
+       shost->can_queue = can_queue;
+       mvi->shost->cmd_per_lun = MVS_SLOTS/sha->num_phys;
+       sha->core.shost = mvi->shost;
+}
+
+static void mvs_init_sas_add(struct mvs_info *mvi)
+{
+       u8 i;
+       for (i = 0; i < mvi->chip->n_phy; i++) {
+               mvi->phy[i].dev_sas_addr = 0x5005043011ab0000ULL;
+               mvi->phy[i].dev_sas_addr =
+                       cpu_to_be64((u64)(*(u64 *)&mvi->phy[i].dev_sas_addr));
+       }
+
+       memcpy(mvi->sas_addr, &mvi->phy[0].dev_sas_addr, SAS_ADDR_SIZE);
+}
+
+static int __devinit mvs_pci_init(struct pci_dev *pdev,
+                                 const struct pci_device_id *ent)
+{
+       unsigned int rc, nhost = 0;
+       struct mvs_info *mvi;
+       irq_handler_t irq_handler = mvs_interrupt;
+       struct Scsi_Host *shost = NULL;
+       const struct mvs_chip_info *chip;
+
+       dev_printk(KERN_INFO, &pdev->dev,
+               "mvsas: driver version %s\n", DRV_VERSION);
+       rc = pci_enable_device(pdev);
+       if (rc)
+               goto err_out_enable;
+
+       pci_set_master(pdev);
+
+       rc = pci_request_regions(pdev, DRV_NAME);
+       if (rc)
+               goto err_out_disable;
+
+       rc = pci_go_64(pdev);
+       if (rc)
+               goto err_out_regions;
+
+       shost = scsi_host_alloc(&mvs_sht, sizeof(void *));
+       if (!shost) {
+               rc = -ENOMEM;
+               goto err_out_regions;
+       }
+
+       chip = &mvs_chips[ent->driver_data];
+       SHOST_TO_SAS_HA(shost) =
+               kcalloc(1, sizeof(struct sas_ha_struct), GFP_KERNEL);
+       if (!SHOST_TO_SAS_HA(shost)) {
+               kfree(shost);
+               rc = -ENOMEM;
+               goto err_out_regions;
+       }
+
+       rc = mvs_prep_sas_ha_init(shost, chip);
+       if (rc) {
+               kfree(shost);
+               rc = -ENOMEM;
+               goto err_out_regions;
+       }
+
+       pci_set_drvdata(pdev, SHOST_TO_SAS_HA(shost));
+
+       do {
+               mvi = mvs_pci_alloc(pdev, ent, shost, nhost);
+               if (!mvi) {
+                       rc = -ENOMEM;
+                       goto err_out_regions;
+               }
+
+               mvs_init_sas_add(mvi);
+
+               mvi->instance = nhost;
+               rc = MVS_CHIP_DISP->chip_init(mvi);
+               if (rc) {
+                       mvs_free(mvi);
+                       goto err_out_regions;
+               }
+               nhost++;
+       } while (nhost < chip->n_host);
+
+       mvs_post_sas_ha_init(shost, chip);
+
+       rc = scsi_add_host(shost, &pdev->dev);
+       if (rc)
+               goto err_out_shost;
+
+       rc = sas_register_ha(SHOST_TO_SAS_HA(shost));
+       if (rc)
+               goto err_out_shost;
+       rc = request_irq(pdev->irq, irq_handler, IRQF_SHARED,
+               DRV_NAME, SHOST_TO_SAS_HA(shost));
+       if (rc)
+               goto err_not_sas;
+
+       MVS_CHIP_DISP->interrupt_enable(mvi);
+
+       scsi_scan_host(mvi->shost);
+
+       return 0;
+
+err_not_sas:
+       sas_unregister_ha(SHOST_TO_SAS_HA(shost));
+err_out_shost:
+       scsi_remove_host(mvi->shost);
+err_out_regions:
+       pci_release_regions(pdev);
+err_out_disable:
+       pci_disable_device(pdev);
+err_out_enable:
+       return rc;
+}
+
+static void __devexit mvs_pci_remove(struct pci_dev *pdev)
+{
+       unsigned short core_nr, i = 0;
+       struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+       struct mvs_info *mvi = NULL;
+
+       core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
+       mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0];
+
+#ifdef MVS_USE_TASKLET
+       tasklet_kill(&mv_tasklet);
+#endif
+
+       pci_set_drvdata(pdev, NULL);
+       sas_unregister_ha(sha);
+       sas_remove_host(mvi->shost);
+       scsi_remove_host(mvi->shost);
+
+       MVS_CHIP_DISP->interrupt_disable(mvi);
+       free_irq(mvi->irq, sha);
+       for (i = 0; i < core_nr; i++) {
+               mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i];
+               mvs_free(mvi);
+       }
+       kfree(sha->sas_phy);
+       kfree(sha->sas_port);
+       kfree(sha);
+       pci_release_regions(pdev);
+       pci_disable_device(pdev);
+       return;
+}
+
+static struct pci_device_id __devinitdata mvs_pci_table[] = {
+       { PCI_VDEVICE(MARVELL, 0x6320), chip_6320 },
+       { PCI_VDEVICE(MARVELL, 0x6340), chip_6440 },
+       {
+               .vendor         = PCI_VENDOR_ID_MARVELL,
+               .device         = 0x6440,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = 0x6480,
+               .class          = 0,
+               .class_mask     = 0,
+               .driver_data    = chip_6485,
+       },
+       { PCI_VDEVICE(MARVELL, 0x6440), chip_6440 },
+       { PCI_VDEVICE(MARVELL, 0x6485), chip_6485 },
+       { PCI_VDEVICE(MARVELL, 0x9480), chip_9480 },
+       { PCI_VDEVICE(MARVELL, 0x9180), chip_9180 },
+
+       { }     /* terminate list */
+};
+
+static struct pci_driver mvs_pci_driver = {
+       .name           = DRV_NAME,
+       .id_table       = mvs_pci_table,
+       .probe          = mvs_pci_init,
+       .remove         = __devexit_p(mvs_pci_remove),
+};
+
+/* task handler */
+struct task_struct *mvs_th;
+static int __init mvs_init(void)
+{
+       int rc;
+       mvs_stt = sas_domain_attach_transport(&mvs_transport_ops);
+       if (!mvs_stt)
+               return -ENOMEM;
+
+       rc = pci_register_driver(&mvs_pci_driver);
+
+       if (rc)
+               goto err_out;
+
+       return 0;
+
+err_out:
+       sas_release_transport(mvs_stt);
+       return rc;
+}
+
+static void __exit mvs_exit(void)
+{
+       pci_unregister_driver(&mvs_pci_driver);
+       sas_release_transport(mvs_stt);
+}
+
+module_init(mvs_init);
+module_exit(mvs_exit);
+
+MODULE_AUTHOR("Jeff Garzik <jgarzik@pobox.com>");
+MODULE_DESCRIPTION("Marvell 88SE6440 SAS/SATA controller driver");
+MODULE_VERSION(DRV_VERSION);
+MODULE_LICENSE("GPL");
+#ifdef CONFIG_PCI
+MODULE_DEVICE_TABLE(pci, mvs_pci_table);
+#endif
diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c
new file mode 100644 (file)
index 0000000..0d21386
--- /dev/null
@@ -0,0 +1,2154 @@
+/*
+ * Marvell 88SE64xx/88SE94xx main function
+ *
+ * Copyright 2007 Red Hat, Inc.
+ * Copyright 2008 Marvell. <kewei@marvell.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This program is free software; 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.
+ *
+ * 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 "mv_sas.h"
+
+static int mvs_find_tag(struct mvs_info *mvi, struct sas_task *task, u32 *tag)
+{
+       if (task->lldd_task) {
+               struct mvs_slot_info *slot;
+               slot = task->lldd_task;
+               *tag = slot->slot_tag;
+               return 1;
+       }
+       return 0;
+}
+
+void mvs_tag_clear(struct mvs_info *mvi, u32 tag)
+{
+       void *bitmap = &mvi->tags;
+       clear_bit(tag, bitmap);
+}
+
+void mvs_tag_free(struct mvs_info *mvi, u32 tag)
+{
+       mvs_tag_clear(mvi, tag);
+}
+
+void mvs_tag_set(struct mvs_info *mvi, unsigned int tag)
+{
+       void *bitmap = &mvi->tags;
+       set_bit(tag, bitmap);
+}
+
+inline int mvs_tag_alloc(struct mvs_info *mvi, u32 *tag_out)
+{
+       unsigned int index, tag;
+       void *bitmap = &mvi->tags;
+
+       index = find_first_zero_bit(bitmap, mvi->tags_num);
+       tag = index;
+       if (tag >= mvi->tags_num)
+               return -SAS_QUEUE_FULL;
+       mvs_tag_set(mvi, tag);
+       *tag_out = tag;
+       return 0;
+}
+
+void mvs_tag_init(struct mvs_info *mvi)
+{
+       int i;
+       for (i = 0; i < mvi->tags_num; ++i)
+               mvs_tag_clear(mvi, i);
+}
+
+void mvs_hexdump(u32 size, u8 *data, u32 baseaddr)
+{
+       u32 i;
+       u32 run;
+       u32 offset;
+
+       offset = 0;
+       while (size) {
+               printk(KERN_DEBUG"%08X : ", baseaddr + offset);
+               if (size >= 16)
+                       run = 16;
+               else
+                       run = size;
+               size -= run;
+               for (i = 0; i < 16; i++) {
+                       if (i < run)
+                               printk(KERN_DEBUG"%02X ", (u32)data[i]);
+                       else
+                               printk(KERN_DEBUG"   ");
+               }
+               printk(KERN_DEBUG": ");
+               for (i = 0; i < run; i++)
+                       printk(KERN_DEBUG"%c",
+                               isalnum(data[i]) ? data[i] : '.');
+               printk(KERN_DEBUG"\n");
+               data = &data[16];
+               offset += run;
+       }
+       printk(KERN_DEBUG"\n");
+}
+
+#if (_MV_DUMP > 1)
+static void mvs_hba_sb_dump(struct mvs_info *mvi, u32 tag,
+                                  enum sas_protocol proto)
+{
+       u32 offset;
+       struct mvs_slot_info *slot = &mvi->slot_info[tag];
+
+       offset = slot->cmd_size + MVS_OAF_SZ +
+           MVS_CHIP_DISP->prd_size() * slot->n_elem;
+       dev_printk(KERN_DEBUG, mvi->dev, "+---->Status buffer[%d] :\n",
+                       tag);
+       mvs_hexdump(32, (u8 *) slot->response,
+                   (u32) slot->buf_dma + offset);
+}
+#endif
+
+static void mvs_hba_memory_dump(struct mvs_info *mvi, u32 tag,
+                               enum sas_protocol proto)
+{
+#if (_MV_DUMP > 1)
+       u32 sz, w_ptr;
+       u64 addr;
+       struct mvs_slot_info *slot = &mvi->slot_info[tag];
+
+       /*Delivery Queue */
+       sz = MVS_CHIP_SLOT_SZ;
+       w_ptr = slot->tx;
+       addr = mvi->tx_dma;
+       dev_printk(KERN_DEBUG, mvi->dev,
+               "Delivery Queue Size=%04d , WRT_PTR=%04X\n", sz, w_ptr);
+       dev_printk(KERN_DEBUG, mvi->dev,
+               "Delivery Queue Base Address=0x%llX (PA)"
+               "(tx_dma=0x%llX), Entry=%04d\n",
+               addr, (unsigned long long)mvi->tx_dma, w_ptr);
+       mvs_hexdump(sizeof(u32), (u8 *)(&mvi->tx[mvi->tx_prod]),
+                       (u32) mvi->tx_dma + sizeof(u32) * w_ptr);
+       /*Command List */
+       addr = mvi->slot_dma;
+       dev_printk(KERN_DEBUG, mvi->dev,
+               "Command List Base Address=0x%llX (PA)"
+               "(slot_dma=0x%llX), Header=%03d\n",
+               addr, (unsigned long long)slot->buf_dma, tag);
+       dev_printk(KERN_DEBUG, mvi->dev, "Command Header[%03d]:\n", tag);
+       /*mvs_cmd_hdr */
+       mvs_hexdump(sizeof(struct mvs_cmd_hdr), (u8 *)(&mvi->slot[tag]),
+               (u32) mvi->slot_dma + tag * sizeof(struct mvs_cmd_hdr));
+       /*1.command table area */
+       dev_printk(KERN_DEBUG, mvi->dev, "+---->Command Table :\n");
+       mvs_hexdump(slot->cmd_size, (u8 *) slot->buf, (u32) slot->buf_dma);
+       /*2.open address frame area */
+       dev_printk(KERN_DEBUG, mvi->dev, "+---->Open Address Frame :\n");
+       mvs_hexdump(MVS_OAF_SZ, (u8 *) slot->buf + slot->cmd_size,
+                               (u32) slot->buf_dma + slot->cmd_size);
+       /*3.status buffer */
+       mvs_hba_sb_dump(mvi, tag, proto);
+       /*4.PRD table */
+       dev_printk(KERN_DEBUG, mvi->dev, "+---->PRD table :\n");
+       mvs_hexdump(MVS_CHIP_DISP->prd_size() * slot->n_elem,
+               (u8 *) slot->buf + slot->cmd_size + MVS_OAF_SZ,
+               (u32) slot->buf_dma + slot->cmd_size + MVS_OAF_SZ);
+#endif
+}
+
+static void mvs_hba_cq_dump(struct mvs_info *mvi)
+{
+#if (_MV_DUMP > 2)
+       u64 addr;
+       void __iomem *regs = mvi->regs;
+       u32 entry = mvi->rx_cons + 1;
+       u32 rx_desc = le32_to_cpu(mvi->rx[entry]);
+
+       /*Completion Queue */
+       addr = mr32(RX_HI) << 16 << 16 | mr32(RX_LO);
+       dev_printk(KERN_DEBUG, mvi->dev, "Completion Task = 0x%p\n",
+                  mvi->slot_info[rx_desc & RXQ_SLOT_MASK].task);
+       dev_printk(KERN_DEBUG, mvi->dev,
+               "Completion List Base Address=0x%llX (PA), "
+               "CQ_Entry=%04d, CQ_WP=0x%08X\n",
+               addr, entry - 1, mvi->rx[0]);
+       mvs_hexdump(sizeof(u32), (u8 *)(&rx_desc),
+                   mvi->rx_dma + sizeof(u32) * entry);
+#endif
+}
+
+void mvs_get_sas_addr(void *buf, u32 buflen)
+{
+       /*memcpy(buf, "\x50\x05\x04\x30\x11\xab\x64\x40", 8);*/
+}
+
+struct mvs_info *mvs_find_dev_mvi(struct domain_device *dev)
+{
+       unsigned long i = 0, j = 0, hi = 0;
+       struct sas_ha_struct *sha = dev->port->ha;
+       struct mvs_info *mvi = NULL;
+       struct asd_sas_phy *phy;
+
+       while (sha->sas_port[i]) {
+               if (sha->sas_port[i] == dev->port) {
+                       phy =  container_of(sha->sas_port[i]->phy_list.next,
+                               struct asd_sas_phy, port_phy_el);
+                       j = 0;
+                       while (sha->sas_phy[j]) {
+                               if (sha->sas_phy[j] == phy)
+                                       break;
+                               j++;
+                       }
+                       break;
+               }
+               i++;
+       }
+       hi = j/((struct mvs_prv_info *)sha->lldd_ha)->n_phy;
+       mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[hi];
+
+       return mvi;
+
+}
+
+/* FIXME */
+int mvs_find_dev_phyno(struct domain_device *dev, int *phyno)
+{
+       unsigned long i = 0, j = 0, n = 0, num = 0;
+       struct mvs_device *mvi_dev = (struct mvs_device *)dev->lldd_dev;
+       struct mvs_info *mvi = mvi_dev->mvi_info;
+       struct sas_ha_struct *sha = dev->port->ha;
+
+       while (sha->sas_port[i]) {
+               if (sha->sas_port[i] == dev->port) {
+                       struct asd_sas_phy *phy;
+                       list_for_each_entry(phy,
+                               &sha->sas_port[i]->phy_list, port_phy_el) {
+                               j = 0;
+                               while (sha->sas_phy[j]) {
+                                       if (sha->sas_phy[j] == phy)
+                                               break;
+                                       j++;
+                               }
+                               phyno[n] = (j >= mvi->chip->n_phy) ?
+                                       (j - mvi->chip->n_phy) : j;
+                               num++;
+                               n++;
+                       }
+                       break;
+               }
+               i++;
+       }
+       return num;
+}
+
+static inline void mvs_free_reg_set(struct mvs_info *mvi,
+                               struct mvs_device *dev)
+{
+       if (!dev) {
+               mv_printk("device has been free.\n");
+               return;
+       }
+       if (dev->runing_req != 0)
+               return;
+       if (dev->taskfileset == MVS_ID_NOT_MAPPED)
+               return;
+       MVS_CHIP_DISP->free_reg_set(mvi, &dev->taskfileset);
+}
+
+static inline u8 mvs_assign_reg_set(struct mvs_info *mvi,
+                               struct mvs_device *dev)
+{
+       if (dev->taskfileset != MVS_ID_NOT_MAPPED)
+               return 0;
+       return MVS_CHIP_DISP->assign_reg_set(mvi, &dev->taskfileset);
+}
+
+void mvs_phys_reset(struct mvs_info *mvi, u32 phy_mask, int hard)
+{
+       u32 no;
+       for_each_phy(phy_mask, phy_mask, no) {
+               if (!(phy_mask & 1))
+                       continue;
+               MVS_CHIP_DISP->phy_reset(mvi, no, hard);
+       }
+}
+
+/* FIXME: locking? */
+int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
+                       void *funcdata)
+{
+       int rc = 0, phy_id = sas_phy->id;
+       u32 tmp, i = 0, hi;
+       struct sas_ha_struct *sha = sas_phy->ha;
+       struct mvs_info *mvi = NULL;
+
+       while (sha->sas_phy[i]) {
+               if (sha->sas_phy[i] == sas_phy)
+                       break;
+               i++;
+       }
+       hi = i/((struct mvs_prv_info *)sha->lldd_ha)->n_phy;
+       mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[hi];
+
+       switch (func) {
+       case PHY_FUNC_SET_LINK_RATE:
+               MVS_CHIP_DISP->phy_set_link_rate(mvi, phy_id, funcdata);
+               break;
+
+       case PHY_FUNC_HARD_RESET:
+               tmp = MVS_CHIP_DISP->read_phy_ctl(mvi, phy_id);
+               if (tmp & PHY_RST_HARD)
+                       break;
+               MVS_CHIP_DISP->phy_reset(mvi, phy_id, 1);
+               break;
+
+       case PHY_FUNC_LINK_RESET:
+               MVS_CHIP_DISP->phy_enable(mvi, phy_id);
+               MVS_CHIP_DISP->phy_reset(mvi, phy_id, 0);
+               break;
+
+       case PHY_FUNC_DISABLE:
+               MVS_CHIP_DISP->phy_disable(mvi, phy_id);
+               break;
+       case PHY_FUNC_RELEASE_SPINUP_HOLD:
+       default:
+               rc = -EOPNOTSUPP;
+       }
+       msleep(200);
+       return rc;
+}
+
+void __devinit mvs_set_sas_addr(struct mvs_info *mvi, int port_id,
+                               u32 off_lo, u32 off_hi, u64 sas_addr)
+{
+       u32 lo = (u32)sas_addr;
+       u32 hi = (u32)(sas_addr>>32);
+
+       MVS_CHIP_DISP->write_port_cfg_addr(mvi, port_id, off_lo);
+       MVS_CHIP_DISP->write_port_cfg_data(mvi, port_id, lo);
+       MVS_CHIP_DISP->write_port_cfg_addr(mvi, port_id, off_hi);
+       MVS_CHIP_DISP->write_port_cfg_data(mvi, port_id, hi);
+}
+
+static void mvs_bytes_dmaed(struct mvs_info *mvi, int i)
+{
+       struct mvs_phy *phy = &mvi->phy[i];
+       struct asd_sas_phy *sas_phy = &phy->sas_phy;
+       struct sas_ha_struct *sas_ha;
+       if (!phy->phy_attached)
+               return;
+
+       if (!(phy->att_dev_info & PORT_DEV_TRGT_MASK)
+               && phy->phy_type & PORT_TYPE_SAS) {
+               return;
+       }
+
+       sas_ha = mvi->sas;
+       sas_ha->notify_phy_event(sas_phy, PHYE_OOB_DONE);
+
+       if (sas_phy->phy) {
+               struct sas_phy *sphy = sas_phy->phy;
+
+               sphy->negotiated_linkrate = sas_phy->linkrate;
+               sphy->minimum_linkrate = phy->minimum_linkrate;
+               sphy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
+               sphy->maximum_linkrate = phy->maximum_linkrate;
+               sphy->maximum_linkrate_hw = MVS_CHIP_DISP->phy_max_link_rate();
+       }
+
+       if (phy->phy_type & PORT_TYPE_SAS) {
+               struct sas_identify_frame *id;
+
+               id = (struct sas_identify_frame *)phy->frame_rcvd;
+               id->dev_type = phy->identify.device_type;
+               id->initiator_bits = SAS_PROTOCOL_ALL;
+               id->target_bits = phy->identify.target_port_protocols;
+       } else if (phy->phy_type & PORT_TYPE_SATA) {
+               /*Nothing*/
+       }
+       mv_dprintk("phy %d byte dmaded.\n", i + mvi->id * mvi->chip->n_phy);
+
+       sas_phy->frame_rcvd_size = phy->frame_rcvd_size;
+
+       mvi->sas->notify_port_event(sas_phy,
+                                  PORTE_BYTES_DMAED);
+}
+
+int mvs_slave_alloc(struct scsi_device *scsi_dev)
+{
+       struct domain_device *dev = sdev_to_domain_dev(scsi_dev);
+       if (dev_is_sata(dev)) {
+               /* We don't need to rescan targets
+                * if REPORT_LUNS request is failed
+                */
+               if (scsi_dev->lun > 0)
+                       return -ENXIO;
+               scsi_dev->tagged_supported = 1;
+       }
+
+       return sas_slave_alloc(scsi_dev);
+}
+
+int mvs_slave_configure(struct scsi_device *sdev)
+{
+       struct domain_device *dev = sdev_to_domain_dev(sdev);
+       int ret = sas_slave_configure(sdev);
+
+       if (ret)
+               return ret;
+       if (dev_is_sata(dev)) {
+               /* may set PIO mode */
+       #if MV_DISABLE_NCQ
+               struct ata_port *ap = dev->sata_dev.ap;
+               struct ata_device *adev = ap->link.device;
+               adev->flags |= ATA_DFLAG_NCQ_OFF;
+               scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, 1);
+       #endif
+       }
+       return 0;
+}
+
+void mvs_scan_start(struct Scsi_Host *shost)
+{
+       int i, j;
+       unsigned short core_nr;
+       struct mvs_info *mvi;
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+
+       core_nr = ((struct mvs_prv_info *)sha->lldd_ha)->n_host;
+
+       for (j = 0; j < core_nr; j++) {
+               mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[j];
+               for (i = 0; i < mvi->chip->n_phy; ++i)
+                       mvs_bytes_dmaed(mvi, i);
+       }
+}
+
+int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time)
+{
+       /* give the phy enabling interrupt event time to come in (1s
+        * is empirically about all it takes) */
+       if (time < HZ)
+               return 0;
+       /* Wait for discovery to finish */
+       scsi_flush_work(shost);
+       return 1;
+}
+
+static int mvs_task_prep_smp(struct mvs_info *mvi,
+                            struct mvs_task_exec_info *tei)
+{
+       int elem, rc, i;
+       struct sas_task *task = tei->task;
+       struct mvs_cmd_hdr *hdr = tei->hdr;
+       struct domain_device *dev = task->dev;
+       struct asd_sas_port *sas_port = dev->port;
+       struct scatterlist *sg_req, *sg_resp;
+       u32 req_len, resp_len, tag = tei->tag;
+       void *buf_tmp;
+       u8 *buf_oaf;
+       dma_addr_t buf_tmp_dma;
+       void *buf_prd;
+       struct mvs_slot_info *slot = &mvi->slot_info[tag];
+       u32 flags = (tei->n_elem << MCH_PRD_LEN_SHIFT);
+#if _MV_DUMP
+       u8 *buf_cmd;
+       void *from;
+#endif
+       /*
+        * DMA-map SMP request, response buffers
+        */
+       sg_req = &task->smp_task.smp_req;
+       elem = dma_map_sg(mvi->dev, sg_req, 1, PCI_DMA_TODEVICE);
+       if (!elem)
+               return -ENOMEM;
+       req_len = sg_dma_len(sg_req);
+
+       sg_resp = &task->smp_task.smp_resp;
+       elem = dma_map_sg(mvi->dev, sg_resp, 1, PCI_DMA_FROMDEVICE);
+       if (!elem) {
+               rc = -ENOMEM;
+               goto err_out;
+       }
+       resp_len = SB_RFB_MAX;
+
+       /* must be in dwords */
+       if ((req_len & 0x3) || (resp_len & 0x3)) {
+               rc = -EINVAL;
+               goto err_out_2;
+       }
+
+       /*
+        * arrange MVS_SLOT_BUF_SZ-sized DMA buffer according to our needs
+        */
+
+       /* region 1: command table area (MVS_SSP_CMD_SZ bytes) ***** */
+       buf_tmp = slot->buf;
+       buf_tmp_dma = slot->buf_dma;
+
+#if _MV_DUMP
+       buf_cmd = buf_tmp;
+       hdr->cmd_tbl = cpu_to_le64(buf_tmp_dma);
+       buf_tmp += req_len;
+       buf_tmp_dma += req_len;
+       slot->cmd_size = req_len;
+#else
+       hdr->cmd_tbl = cpu_to_le64(sg_dma_address(sg_req));
+#endif
+
+       /* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
+       buf_oaf = buf_tmp;
+       hdr->open_frame = cpu_to_le64(buf_tmp_dma);
+
+       buf_tmp += MVS_OAF_SZ;
+       buf_tmp_dma += MVS_OAF_SZ;
+
+       /* region 3: PRD table *********************************** */
+       buf_prd = buf_tmp;
+       if (tei->n_elem)
+               hdr->prd_tbl = cpu_to_le64(buf_tmp_dma);
+       else
+               hdr->prd_tbl = 0;
+
+       i = MVS_CHIP_DISP->prd_size() * tei->n_elem;
+       buf_tmp += i;
+       buf_tmp_dma += i;
+
+       /* region 4: status buffer (larger the PRD, smaller this buf) ****** */
+       slot->response = buf_tmp;
+       hdr->status_buf = cpu_to_le64(buf_tmp_dma);
+       if (mvi->flags & MVF_FLAG_SOC)
+               hdr->reserved[0] = 0;
+
+       /*
+        * Fill in TX ring and command slot header
+        */
+       slot->tx = mvi->tx_prod;
+       mvi->tx[mvi->tx_prod] = cpu_to_le32((TXQ_CMD_SMP << TXQ_CMD_SHIFT) |
+                                       TXQ_MODE_I | tag |
+                                       (sas_port->phy_mask << TXQ_PHY_SHIFT));
+
+       hdr->flags |= flags;
+       hdr->lens = cpu_to_le32(((resp_len / 4) << 16) | ((req_len - 4) / 4));
+       hdr->tags = cpu_to_le32(tag);
+       hdr->data_len = 0;
+
+       /* generate open address frame hdr (first 12 bytes) */
+       /* initiator, SMP, ftype 1h */
+       buf_oaf[0] = (1 << 7) | (PROTOCOL_SMP << 4) | 0x01;
+       buf_oaf[1] = dev->linkrate & 0xf;
+       *(u16 *)(buf_oaf + 2) = 0xFFFF;         /* SAS SPEC */
+       memcpy(buf_oaf + 4, dev->sas_addr, SAS_ADDR_SIZE);
+
+       /* fill in PRD (scatter/gather) table, if any */
+       MVS_CHIP_DISP->make_prd(task->scatter, tei->n_elem, buf_prd);
+
+#if _MV_DUMP
+       /* copy cmd table */
+       from = kmap_atomic(sg_page(sg_req), KM_IRQ0);
+       memcpy(buf_cmd, from + sg_req->offset, req_len);
+       kunmap_atomic(from, KM_IRQ0);
+#endif
+       return 0;
+
+err_out_2:
+       dma_unmap_sg(mvi->dev, &tei->task->smp_task.smp_resp, 1,
+                    PCI_DMA_FROMDEVICE);
+err_out:
+       dma_unmap_sg(mvi->dev, &tei->task->smp_task.smp_req, 1,
+                    PCI_DMA_TODEVICE);
+       return rc;
+}
+
+static u32 mvs_get_ncq_tag(struct sas_task *task, u32 *tag)
+{
+       struct ata_queued_cmd *qc = task->uldd_task;
+
+       if (qc) {
+               if (qc->tf.command == ATA_CMD_FPDMA_WRITE ||
+                       qc->tf.command == ATA_CMD_FPDMA_READ) {
+                       *tag = qc->tag;
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+static int mvs_task_prep_ata(struct mvs_info *mvi,
+                            struct mvs_task_exec_info *tei)
+{
+       struct sas_task *task = tei->task;
+       struct domain_device *dev = task->dev;
+       struct mvs_device *mvi_dev = dev->lldd_dev;
+       struct mvs_cmd_hdr *hdr = tei->hdr;
+       struct asd_sas_port *sas_port = dev->port;
+       struct mvs_slot_info *slot;
+       void *buf_prd;
+       u32 tag = tei->tag, hdr_tag;
+       u32 flags, del_q;
+       void *buf_tmp;
+       u8 *buf_cmd, *buf_oaf;
+       dma_addr_t buf_tmp_dma;
+       u32 i, req_len, resp_len;
+       const u32 max_resp_len = SB_RFB_MAX;
+
+       if (mvs_assign_reg_set(mvi, mvi_dev) == MVS_ID_NOT_MAPPED) {
+               mv_dprintk("Have not enough regiset for dev %d.\n",
+                       mvi_dev->device_id);
+               return -EBUSY;
+       }
+       slot = &mvi->slot_info[tag];
+       slot->tx = mvi->tx_prod;
+       del_q = TXQ_MODE_I | tag |
+               (TXQ_CMD_STP << TXQ_CMD_SHIFT) |
+               (sas_port->phy_mask << TXQ_PHY_SHIFT) |
+               (mvi_dev->taskfileset << TXQ_SRS_SHIFT);
+       mvi->tx[mvi->tx_prod] = cpu_to_le32(del_q);
+
+#ifndef DISABLE_HOTPLUG_DMA_FIX
+       if (task->data_dir == DMA_FROM_DEVICE)
+               flags = (MVS_CHIP_DISP->prd_count() << MCH_PRD_LEN_SHIFT);
+       else
+               flags = (tei->n_elem << MCH_PRD_LEN_SHIFT);
+#else
+       flags = (tei->n_elem << MCH_PRD_LEN_SHIFT);
+#endif
+       if (task->ata_task.use_ncq)
+               flags |= MCH_FPDMA;
+       if (dev->sata_dev.command_set == ATAPI_COMMAND_SET) {
+               if (task->ata_task.fis.command != ATA_CMD_ID_ATAPI)
+                       flags |= MCH_ATAPI;
+       }
+
+       /* FIXME: fill in port multiplier number */
+
+       hdr->flags = cpu_to_le32(flags);
+
+       /* FIXME: the low order order 5 bits for the TAG if enable NCQ */
+       if (task->ata_task.use_ncq && mvs_get_ncq_tag(task, &hdr_tag))
+               task->ata_task.fis.sector_count |= (u8) (hdr_tag << 3);
+       else
+               hdr_tag = tag;
+
+       hdr->tags = cpu_to_le32(hdr_tag);
+
+       hdr->data_len = cpu_to_le32(task->total_xfer_len);
+
+       /*
+        * arrange MVS_SLOT_BUF_SZ-sized DMA buffer according to our needs
+        */
+
+       /* region 1: command table area (MVS_ATA_CMD_SZ bytes) ************** */
+       buf_cmd = buf_tmp = slot->buf;
+       buf_tmp_dma = slot->buf_dma;
+
+       hdr->cmd_tbl = cpu_to_le64(buf_tmp_dma);
+
+       buf_tmp += MVS_ATA_CMD_SZ;
+       buf_tmp_dma += MVS_ATA_CMD_SZ;
+#if _MV_DUMP
+       slot->cmd_size = MVS_ATA_CMD_SZ;
+#endif
+
+       /* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
+       /* used for STP.  unused for SATA? */
+       buf_oaf = buf_tmp;
+       hdr->open_frame = cpu_to_le64(buf_tmp_dma);
+
+       buf_tmp += MVS_OAF_SZ;
+       buf_tmp_dma += MVS_OAF_SZ;
+
+       /* region 3: PRD table ********************************************* */
+       buf_prd = buf_tmp;
+
+       if (tei->n_elem)
+               hdr->prd_tbl = cpu_to_le64(buf_tmp_dma);
+       else
+               hdr->prd_tbl = 0;
+       i = MVS_CHIP_DISP->prd_size() * MVS_CHIP_DISP->prd_count();
+
+       buf_tmp += i;
+       buf_tmp_dma += i;
+
+       /* region 4: status buffer (larger the PRD, smaller this buf) ****** */
+       /* FIXME: probably unused, for SATA.  kept here just in case
+        * we get a STP/SATA error information record
+        */
+       slot->response = buf_tmp;
+       hdr->status_buf = cpu_to_le64(buf_tmp_dma);
+       if (mvi->flags & MVF_FLAG_SOC)
+               hdr->reserved[0] = 0;
+
+       req_len = sizeof(struct host_to_dev_fis);
+       resp_len = MVS_SLOT_BUF_SZ - MVS_ATA_CMD_SZ -
+           sizeof(struct mvs_err_info) - i;
+
+       /* request, response lengths */
+       resp_len = min(resp_len, max_resp_len);
+       hdr->lens = cpu_to_le32(((resp_len / 4) << 16) | (req_len / 4));
+
+       if (likely(!task->ata_task.device_control_reg_update))
+               task->ata_task.fis.flags |= 0x80; /* C=1: update ATA cmd reg */
+       /* fill in command FIS and ATAPI CDB */
+       memcpy(buf_cmd, &task->ata_task.fis, sizeof(struct host_to_dev_fis));
+       if (dev->sata_dev.command_set == ATAPI_COMMAND_SET)
+               memcpy(buf_cmd + STP_ATAPI_CMD,
+                       task->ata_task.atapi_packet, 16);
+
+       /* generate open address frame hdr (first 12 bytes) */
+       /* initiator, STP, ftype 1h */
+       buf_oaf[0] = (1 << 7) | (PROTOCOL_STP << 4) | 0x1;
+       buf_oaf[1] = dev->linkrate & 0xf;
+       *(u16 *)(buf_oaf + 2) = cpu_to_be16(mvi_dev->device_id + 1);
+       memcpy(buf_oaf + 4, dev->sas_addr, SAS_ADDR_SIZE);
+
+       /* fill in PRD (scatter/gather) table, if any */
+       MVS_CHIP_DISP->make_prd(task->scatter, tei->n_elem, buf_prd);
+#ifndef DISABLE_HOTPLUG_DMA_FIX
+       if (task->data_dir == DMA_FROM_DEVICE)
+               MVS_CHIP_DISP->dma_fix(mvi->bulk_buffer_dma,
+                               TRASH_BUCKET_SIZE, tei->n_elem, buf_prd);
+#endif
+       return 0;
+}
+
+static int mvs_task_prep_ssp(struct mvs_info *mvi,
+                            struct mvs_task_exec_info *tei, int is_tmf,
+                            struct mvs_tmf_task *tmf)
+{
+       struct sas_task *task = tei->task;
+       struct mvs_cmd_hdr *hdr = tei->hdr;
+       struct mvs_port *port = tei->port;
+       struct domain_device *dev = task->dev;
+       struct mvs_device *mvi_dev = dev->lldd_dev;
+       struct asd_sas_port *sas_port = dev->port;
+       struct mvs_slot_info *slot;
+       void *buf_prd;
+       struct ssp_frame_hdr *ssp_hdr;
+       void *buf_tmp;
+       u8 *buf_cmd, *buf_oaf, fburst = 0;
+       dma_addr_t buf_tmp_dma;
+       u32 flags;
+       u32 resp_len, req_len, i, tag = tei->tag;
+       const u32 max_resp_len = SB_RFB_MAX;
+       u32 phy_mask;
+
+       slot = &mvi->slot_info[tag];
+
+       phy_mask = ((port->wide_port_phymap) ? port->wide_port_phymap :
+               sas_port->phy_mask) & TXQ_PHY_MASK;
+
+       slot->tx = mvi->tx_prod;
+       mvi->tx[mvi->tx_prod] = cpu_to_le32(TXQ_MODE_I | tag |
+                               (TXQ_CMD_SSP << TXQ_CMD_SHIFT) |
+                               (phy_mask << TXQ_PHY_SHIFT));
+
+       flags = MCH_RETRY;
+       if (task->ssp_task.enable_first_burst) {
+               flags |= MCH_FBURST;
+               fburst = (1 << 7);
+       }
+       if (is_tmf)
+               flags |= (MCH_SSP_FR_TASK << MCH_SSP_FR_TYPE_SHIFT);
+       else
+               flags |= (MCH_SSP_FR_CMD << MCH_SSP_FR_TYPE_SHIFT);
+       hdr->flags = cpu_to_le32(flags | (tei->n_elem << MCH_PRD_LEN_SHIFT));
+       hdr->tags = cpu_to_le32(tag);
+       hdr->data_len = cpu_to_le32(task->total_xfer_len);
+
+       /*
+        * arrange MVS_SLOT_BUF_SZ-sized DMA buffer according to our needs
+        */
+
+       /* region 1: command table area (MVS_SSP_CMD_SZ bytes) ************** */
+       buf_cmd = buf_tmp = slot->buf;
+       buf_tmp_dma = slot->buf_dma;
+
+       hdr->cmd_tbl = cpu_to_le64(buf_tmp_dma);
+
+       buf_tmp += MVS_SSP_CMD_SZ;
+       buf_tmp_dma += MVS_SSP_CMD_SZ;
+#if _MV_DUMP
+       slot->cmd_size = MVS_SSP_CMD_SZ;
+#endif
+
+       /* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */
+       buf_oaf = buf_tmp;
+       hdr->open_frame = cpu_to_le64(buf_tmp_dma);
+
+       buf_tmp += MVS_OAF_SZ;
+       buf_tmp_dma += MVS_OAF_SZ;
+
+       /* region 3: PRD table ********************************************* */
+       buf_prd = buf_tmp;
+       if (tei->n_elem)
+               hdr->prd_tbl = cpu_to_le64(buf_tmp_dma);
+       else
+               hdr->prd_tbl = 0;
+
+       i = MVS_CHIP_DISP->prd_size() * tei->n_elem;
+       buf_tmp += i;
+       buf_tmp_dma += i;
+
+       /* region 4: status buffer (larger the PRD, smaller this buf) ****** */
+       slot->response = buf_tmp;
+       hdr->status_buf = cpu_to_le64(buf_tmp_dma);
+       if (mvi->flags & MVF_FLAG_SOC)
+               hdr->reserved[0] = 0;
+
+       resp_len = MVS_SLOT_BUF_SZ - MVS_SSP_CMD_SZ - MVS_OAF_SZ -
+           sizeof(struct mvs_err_info) - i;
+       resp_len = min(resp_len, max_resp_len);
+
+       req_len = sizeof(struct ssp_frame_hdr) + 28;
+
+       /* request, response lengths */
+       hdr->lens = cpu_to_le32(((resp_len / 4) << 16) | (req_len / 4));
+
+       /* generate open address frame hdr (first 12 bytes) */
+       /* initiator, SSP, ftype 1h */
+       buf_oaf[0] = (1 << 7) | (PROTOCOL_SSP << 4) | 0x1;
+       buf_oaf[1] = dev->linkrate & 0xf;
+       *(u16 *)(buf_oaf + 2) = cpu_to_be16(mvi_dev->device_id + 1);
+       memcpy(buf_oaf + 4, dev->sas_addr, SAS_ADDR_SIZE);
+
+       /* fill in SSP frame header (Command Table.SSP frame header) */
+       ssp_hdr = (struct ssp_frame_hdr *)buf_cmd;
+
+       if (is_tmf)
+               ssp_hdr->frame_type = SSP_TASK;
+       else
+               ssp_hdr->frame_type = SSP_COMMAND;
+
+       memcpy(ssp_hdr->hashed_dest_addr, dev->hashed_sas_addr,
+              HASHED_SAS_ADDR_SIZE);
+       memcpy(ssp_hdr->hashed_src_addr,
+              dev->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
+       ssp_hdr->tag = cpu_to_be16(tag);
+
+       /* fill in IU for TASK and Command Frame */
+       buf_cmd += sizeof(*ssp_hdr);
+       memcpy(buf_cmd, &task->ssp_task.LUN, 8);
+
+       if (ssp_hdr->frame_type != SSP_TASK) {
+               buf_cmd[9] = fburst | task->ssp_task.task_attr |
+                               (task->ssp_task.task_prio << 3);
+               memcpy(buf_cmd + 12, &task->ssp_task.cdb, 16);
+       } else{
+               buf_cmd[10] = tmf->tmf;
+               switch (tmf->tmf) {
+               case TMF_ABORT_TASK:
+               case TMF_QUERY_TASK:
+                       buf_cmd[12] =
+                               (tmf->tag_of_task_to_be_managed >> 8) & 0xff;
+                       buf_cmd[13] =
+                               tmf->tag_of_task_to_be_managed & 0xff;
+                       break;
+               default:
+                       break;
+               }
+       }
+       /* fill in PRD (scatter/gather) table, if any */
+       MVS_CHIP_DISP->make_prd(task->scatter, tei->n_elem, buf_prd);
+       return 0;
+}
+
+#define        DEV_IS_GONE(mvi_dev)    ((!mvi_dev || (mvi_dev->dev_type == NO_DEVICE)))
+static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags,
+                               struct completion *completion,int is_tmf,
+                               struct mvs_tmf_task *tmf)
+{
+       struct domain_device *dev = task->dev;
+       struct mvs_device *mvi_dev = (struct mvs_device *)dev->lldd_dev;
+       struct mvs_info *mvi = mvi_dev->mvi_info;
+       struct mvs_task_exec_info tei;
+       struct sas_task *t = task;
+       struct mvs_slot_info *slot;
+       u32 tag = 0xdeadbeef, rc, n_elem = 0;
+       u32 n = num, pass = 0;
+       unsigned long flags = 0;
+
+       if (!dev->port) {
+               struct task_status_struct *tsm = &t->task_status;
+
+               tsm->resp = SAS_TASK_UNDELIVERED;
+               tsm->stat = SAS_PHY_DOWN;
+               t->task_done(t);
+               return 0;
+       }
+
+       spin_lock_irqsave(&mvi->lock, flags);
+       do {
+               dev = t->dev;
+               mvi_dev = dev->lldd_dev;
+               if (DEV_IS_GONE(mvi_dev)) {
+                       if (mvi_dev)
+                               mv_dprintk("device %d not ready.\n",
+                                       mvi_dev->device_id);
+                       else
+                               mv_dprintk("device %016llx not ready.\n",
+                                       SAS_ADDR(dev->sas_addr));
+
+                       rc = SAS_PHY_DOWN;
+                       goto out_done;
+               }
+
+               if (dev->port->id >= mvi->chip->n_phy)
+                       tei.port = &mvi->port[dev->port->id - mvi->chip->n_phy];
+               else
+                       tei.port = &mvi->port[dev->port->id];
+
+               if (!tei.port->port_attached) {
+                       if (sas_protocol_ata(t->task_proto)) {
+                               mv_dprintk("port %d does not"
+                                       "attached device.\n", dev->port->id);
+                               rc = SAS_PHY_DOWN;
+                               goto out_done;
+                       } else {
+                               struct task_status_struct *ts = &t->task_status;
+                               ts->resp = SAS_TASK_UNDELIVERED;
+                               ts->stat = SAS_PHY_DOWN;
+                               t->task_done(t);
+                               if (n > 1)
+                                       t = list_entry(t->list.next,
+                                                       struct sas_task, list);
+                               continue;
+                       }
+               }
+
+               if (!sas_protocol_ata(t->task_proto)) {
+                       if (t->num_scatter) {
+                               n_elem = dma_map_sg(mvi->dev,
+                                                   t->scatter,
+                                                   t->num_scatter,
+                                                   t->data_dir);
+                               if (!n_elem) {
+                                       rc = -ENOMEM;
+                                       goto err_out;
+                               }
+                       }
+               } else {
+                       n_elem = t->num_scatter;
+               }
+
+               rc = mvs_tag_alloc(mvi, &tag);
+               if (rc)
+                       goto err_out;
+
+               slot = &mvi->slot_info[tag];
+
+
+               t->lldd_task = NULL;
+               slot->n_elem = n_elem;
+               slot->slot_tag = tag;
+               memset(slot->buf, 0, MVS_SLOT_BUF_SZ);
+
+               tei.task = t;
+               tei.hdr = &mvi->slot[tag];
+               tei.tag = tag;
+               tei.n_elem = n_elem;
+               switch (t->task_proto) {
+               case SAS_PROTOCOL_SMP:
+                       rc = mvs_task_prep_smp(mvi, &tei);
+                       break;
+               case SAS_PROTOCOL_SSP:
+                       rc = mvs_task_prep_ssp(mvi, &tei, is_tmf, tmf);
+                       break;
+               case SAS_PROTOCOL_SATA:
+               case SAS_PROTOCOL_STP:
+               case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+                       rc = mvs_task_prep_ata(mvi, &tei);
+                       break;
+               default:
+                       dev_printk(KERN_ERR, mvi->dev,
+                               "unknown sas_task proto: 0x%x\n",
+                               t->task_proto);
+                       rc = -EINVAL;
+                       break;
+               }
+
+               if (rc) {
+                       mv_dprintk("rc is %x\n", rc);
+                       goto err_out_tag;
+               }
+               slot->task = t;
+               slot->port = tei.port;
+               t->lldd_task = slot;
+               list_add_tail(&slot->entry, &tei.port->list);
+               /* TODO: select normal or high priority */
+               spin_lock(&t->task_state_lock);
+               t->task_state_flags |= SAS_TASK_AT_INITIATOR;
+               spin_unlock(&t->task_state_lock);
+
+               mvs_hba_memory_dump(mvi, tag, t->task_proto);
+               mvi_dev->runing_req++;
+               ++pass;
+               mvi->tx_prod = (mvi->tx_prod + 1) & (MVS_CHIP_SLOT_SZ - 1);
+               if (n > 1)
+                       t = list_entry(t->list.next, struct sas_task, list);
+       } while (--n);
+       rc = 0;
+       goto out_done;
+
+err_out_tag:
+       mvs_tag_free(mvi, tag);
+err_out:
+
+       dev_printk(KERN_ERR, mvi->dev, "mvsas exec failed[%d]!\n", rc);
+       if (!sas_protocol_ata(t->task_proto))
+               if (n_elem)
+                       dma_unmap_sg(mvi->dev, t->scatter, n_elem,
+                                    t->data_dir);
+out_done:
+       if (likely(pass)) {
+               MVS_CHIP_DISP->start_delivery(mvi,
+                       (mvi->tx_prod - 1) & (MVS_CHIP_SLOT_SZ - 1));
+       }
+       spin_unlock_irqrestore(&mvi->lock, flags);
+       return rc;
+}
+
+int mvs_queue_command(struct sas_task *task, const int num,
+                       gfp_t gfp_flags)
+{
+       return mvs_task_exec(task, num, gfp_flags, NULL, 0, NULL);
+}
+
+static void mvs_slot_free(struct mvs_info *mvi, u32 rx_desc)
+{
+       u32 slot_idx = rx_desc & RXQ_SLOT_MASK;
+       mvs_tag_clear(mvi, slot_idx);
+}
+
+static void mvs_slot_task_free(struct mvs_info *mvi, struct sas_task *task,
+                         struct mvs_slot_info *slot, u32 slot_idx)
+{
+       if (!slot->task)
+               return;
+       if (!sas_protocol_ata(task->task_proto))
+               if (slot->n_elem)
+                       dma_unmap_sg(mvi->dev, task->scatter,
+                                    slot->n_elem, task->data_dir);
+
+       switch (task->task_proto) {
+       case SAS_PROTOCOL_SMP:
+               dma_unmap_sg(mvi->dev, &task->smp_task.smp_resp, 1,
+                            PCI_DMA_FROMDEVICE);
+               dma_unmap_sg(mvi->dev, &task->smp_task.smp_req, 1,
+                            PCI_DMA_TODEVICE);
+               break;
+
+       case SAS_PROTOCOL_SATA:
+       case SAS_PROTOCOL_STP:
+       case SAS_PROTOCOL_SSP:
+       default:
+               /* do nothing */
+               break;
+       }
+       list_del_init(&slot->entry);
+       task->lldd_task = NULL;
+       slot->task = NULL;
+       slot->port = NULL;
+       slot->slot_tag = 0xFFFFFFFF;
+       mvs_slot_free(mvi, slot_idx);
+}
+
+static void mvs_update_wideport(struct mvs_info *mvi, int i)
+{
+       struct mvs_phy *phy = &mvi->phy[i];
+       struct mvs_port *port = phy->port;
+       int j, no;
+
+       for_each_phy(port->wide_port_phymap, j, no) {
+               if (j & 1) {
+                       MVS_CHIP_DISP->write_port_cfg_addr(mvi, no,
+                                               PHYR_WIDE_PORT);
+                       MVS_CHIP_DISP->write_port_cfg_data(mvi, no,
+                                               port->wide_port_phymap);
+               } else {
+                       MVS_CHIP_DISP->write_port_cfg_addr(mvi, no,
+                                               PHYR_WIDE_PORT);
+                       MVS_CHIP_DISP->write_port_cfg_data(mvi, no,
+                                               0);
+               }
+       }
+}
+
+static u32 mvs_is_phy_ready(struct mvs_info *mvi, int i)
+{
+       u32 tmp;
+       struct mvs_phy *phy = &mvi->phy[i];
+       struct mvs_port *port = phy->port;
+
+       tmp = MVS_CHIP_DISP->read_phy_ctl(mvi, i);
+       if ((tmp & PHY_READY_MASK) && !(phy->irq_status & PHYEV_POOF)) {
+               if (!port)
+                       phy->phy_attached = 1;
+               return tmp;
+       }
+
+       if (port) {
+               if (phy->phy_type & PORT_TYPE_SAS) {
+                       port->wide_port_phymap &= ~(1U << i);
+                       if (!port->wide_port_phymap)
+                               port->port_attached = 0;
+                       mvs_update_wideport(mvi, i);
+               } else if (phy->phy_type & PORT_TYPE_SATA)
+                       port->port_attached = 0;
+               phy->port = NULL;
+               phy->phy_attached = 0;
+               phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
+       }
+       return 0;
+}
+
+static void *mvs_get_d2h_reg(struct mvs_info *mvi, int i, void *buf)
+{
+       u32 *s = (u32 *) buf;
+
+       if (!s)
+               return NULL;
+
+       MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG3);
+       s[3] = MVS_CHIP_DISP->read_port_cfg_data(mvi, i);
+
+       MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG2);
+       s[2] = MVS_CHIP_DISP->read_port_cfg_data(mvi, i);
+
+       MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG1);
+       s[1] = MVS_CHIP_DISP->read_port_cfg_data(mvi, i);
+
+       MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG0);
+       s[0] = MVS_CHIP_DISP->read_port_cfg_data(mvi, i);
+
+       /* Workaround: take some ATAPI devices for ATA */
+       if (((s[1] & 0x00FFFFFF) == 0x00EB1401) && (*(u8 *)&s[3] == 0x01))
+               s[1] = 0x00EB1401 | (*((u8 *)&s[1] + 3) & 0x10);
+
+       return s;
+}
+
+static u32 mvs_is_sig_fis_received(u32 irq_status)
+{
+       return irq_status & PHYEV_SIG_FIS;
+}
+
+void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st)
+{
+       struct mvs_phy *phy = &mvi->phy[i];
+       struct sas_identify_frame *id;
+
+       id = (struct sas_identify_frame *)phy->frame_rcvd;
+
+       if (get_st) {
+               phy->irq_status = MVS_CHIP_DISP->read_port_irq_stat(mvi, i);
+               phy->phy_status = mvs_is_phy_ready(mvi, i);
+       }
+
+       if (phy->phy_status) {
+               int oob_done = 0;
+               struct asd_sas_phy *sas_phy = &mvi->phy[i].sas_phy;
+
+               oob_done = MVS_CHIP_DISP->oob_done(mvi, i);
+
+               MVS_CHIP_DISP->fix_phy_info(mvi, i, id);
+               if (phy->phy_type & PORT_TYPE_SATA) {
+                       phy->identify.target_port_protocols = SAS_PROTOCOL_STP;
+                       if (mvs_is_sig_fis_received(phy->irq_status)) {
+                               phy->phy_attached = 1;
+                               phy->att_dev_sas_addr =
+                                       i + mvi->id * mvi->chip->n_phy;
+                               if (oob_done)
+                                       sas_phy->oob_mode = SATA_OOB_MODE;
+                               phy->frame_rcvd_size =
+                                   sizeof(struct dev_to_host_fis);
+                               mvs_get_d2h_reg(mvi, i, id);
+                       } else {
+                               u32 tmp;
+                               dev_printk(KERN_DEBUG, mvi->dev,
+                                       "Phy%d : No sig fis\n", i);
+                               tmp = MVS_CHIP_DISP->read_port_irq_mask(mvi, i);
+                               MVS_CHIP_DISP->write_port_irq_mask(mvi, i,
+                                               tmp | PHYEV_SIG_FIS);
+                               phy->phy_attached = 0;
+                               phy->phy_type &= ~PORT_TYPE_SATA;
+                               MVS_CHIP_DISP->phy_reset(mvi, i, 0);
+                               goto out_done;
+                       }
+               }               else if (phy->phy_type & PORT_TYPE_SAS
+                       || phy->att_dev_info & PORT_SSP_INIT_MASK) {
+                       phy->phy_attached = 1;
+                       phy->identify.device_type =
+                               phy->att_dev_info & PORT_DEV_TYPE_MASK;
+
+                       if (phy->identify.device_type == SAS_END_DEV)
+                               phy->identify.target_port_protocols =
+                                                       SAS_PROTOCOL_SSP;
+                       else if (phy->identify.device_type != NO_DEVICE)
+                               phy->identify.target_port_protocols =
+                                                       SAS_PROTOCOL_SMP;
+                       if (oob_done)
+                               sas_phy->oob_mode = SAS_OOB_MODE;
+                       phy->frame_rcvd_size =
+                           sizeof(struct sas_identify_frame);
+               }
+               memcpy(sas_phy->attached_sas_addr,
+                       &phy->att_dev_sas_addr, SAS_ADDR_SIZE);
+
+               if (MVS_CHIP_DISP->phy_work_around)
+                       MVS_CHIP_DISP->phy_work_around(mvi, i);
+       }
+       mv_dprintk("port %d attach dev info is %x\n",
+               i + mvi->id * mvi->chip->n_phy, phy->att_dev_info);
+       mv_dprintk("port %d attach sas addr is %llx\n",
+               i + mvi->id * mvi->chip->n_phy, phy->att_dev_sas_addr);
+out_done:
+       if (get_st)
+               MVS_CHIP_DISP->write_port_irq_stat(mvi, i, phy->irq_status);
+}
+
+static void mvs_port_notify_formed(struct asd_sas_phy *sas_phy, int lock)
+{
+       struct sas_ha_struct *sas_ha = sas_phy->ha;
+       struct mvs_info *mvi = NULL; int i = 0, hi;
+       struct mvs_phy *phy = sas_phy->lldd_phy;
+       struct asd_sas_port *sas_port = sas_phy->port;
+       struct mvs_port *port;
+       unsigned long flags = 0;
+       if (!sas_port)
+               return;
+
+       while (sas_ha->sas_phy[i]) {
+               if (sas_ha->sas_phy[i] == sas_phy)
+                       break;
+               i++;
+       }
+       hi = i/((struct mvs_prv_info *)sas_ha->lldd_ha)->n_phy;
+       mvi = ((struct mvs_prv_info *)sas_ha->lldd_ha)->mvi[hi];
+       if (sas_port->id >= mvi->chip->n_phy)
+               port = &mvi->port[sas_port->id - mvi->chip->n_phy];
+       else
+               port = &mvi->port[sas_port->id];
+       if (lock)
+               spin_lock_irqsave(&mvi->lock, flags);
+       port->port_attached = 1;
+       phy->port = port;
+       if (phy->phy_type & PORT_TYPE_SAS) {
+               port->wide_port_phymap = sas_port->phy_mask;
+               mv_printk("set wide port phy map %x\n", sas_port->phy_mask);
+               mvs_update_wideport(mvi, sas_phy->id);
+       }
+       if (lock)
+               spin_unlock_irqrestore(&mvi->lock, flags);
+}
+
+static void mvs_port_notify_deformed(struct asd_sas_phy *sas_phy, int lock)
+{
+       /*Nothing*/
+}
+
+
+void mvs_port_formed(struct asd_sas_phy *sas_phy)
+{
+       mvs_port_notify_formed(sas_phy, 1);
+}
+
+void mvs_port_deformed(struct asd_sas_phy *sas_phy)
+{
+       mvs_port_notify_deformed(sas_phy, 1);
+}
+
+struct mvs_device *mvs_alloc_dev(struct mvs_info *mvi)
+{
+       u32 dev;
+       for (dev = 0; dev < MVS_MAX_DEVICES; dev++) {
+               if (mvi->devices[dev].dev_type == NO_DEVICE) {
+                       mvi->devices[dev].device_id = dev;
+                       return &mvi->devices[dev];
+               }
+       }
+
+       if (dev == MVS_MAX_DEVICES)
+               mv_printk("max support %d devices, ignore ..\n",
+                       MVS_MAX_DEVICES);
+
+       return NULL;
+}
+
+void mvs_free_dev(struct mvs_device *mvi_dev)
+{
+       u32 id = mvi_dev->device_id;
+       memset(mvi_dev, 0, sizeof(*mvi_dev));
+       mvi_dev->device_id = id;
+       mvi_dev->dev_type = NO_DEVICE;
+       mvi_dev->dev_status = MVS_DEV_NORMAL;
+       mvi_dev->taskfileset = MVS_ID_NOT_MAPPED;
+}
+
+int mvs_dev_found_notify(struct domain_device *dev, int lock)
+{
+       unsigned long flags = 0;
+       int res = 0;
+       struct mvs_info *mvi = NULL;
+       struct domain_device *parent_dev = dev->parent;
+       struct mvs_device *mvi_device;
+
+       mvi = mvs_find_dev_mvi(dev);
+
+       if (lock)
+               spin_lock_irqsave(&mvi->lock, flags);
+
+       mvi_device = mvs_alloc_dev(mvi);
+       if (!mvi_device) {
+               res = -1;
+               goto found_out;
+       }
+       dev->lldd_dev = mvi_device;
+       mvi_device->dev_type = dev->dev_type;
+       mvi_device->mvi_info = mvi;
+       if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type)) {
+               int phy_id;
+               u8 phy_num = parent_dev->ex_dev.num_phys;
+               struct ex_phy *phy;
+               for (phy_id = 0; phy_id < phy_num; phy_id++) {
+                       phy = &parent_dev->ex_dev.ex_phy[phy_id];
+                       if (SAS_ADDR(phy->attached_sas_addr) ==
+                               SAS_ADDR(dev->sas_addr)) {
+                               mvi_device->attached_phy = phy_id;
+                               break;
+                       }
+               }
+
+               if (phy_id == phy_num) {
+                       mv_printk("Error: no attached dev:%016llx"
+                               "at ex:%016llx.\n",
+                               SAS_ADDR(dev->sas_addr),
+                               SAS_ADDR(parent_dev->sas_addr));
+                       res = -1;
+               }
+       }
+
+found_out:
+       if (lock)
+               spin_unlock_irqrestore(&mvi->lock, flags);
+       return res;
+}
+
+int mvs_dev_found(struct domain_device *dev)
+{
+       return mvs_dev_found_notify(dev, 1);
+}
+
+void mvs_dev_gone_notify(struct domain_device *dev, int lock)
+{
+       unsigned long flags = 0;
+       struct mvs_device *mvi_dev = dev->lldd_dev;
+       struct mvs_info *mvi = mvi_dev->mvi_info;
+
+       if (lock)
+               spin_lock_irqsave(&mvi->lock, flags);
+
+       if (mvi_dev) {
+               mv_dprintk("found dev[%d:%x] is gone.\n",
+                       mvi_dev->device_id, mvi_dev->dev_type);
+               mvs_free_reg_set(mvi, mvi_dev);
+               mvs_free_dev(mvi_dev);
+       } else {
+               mv_dprintk("found dev has gone.\n");
+       }
+       dev->lldd_dev = NULL;
+
+       if (lock)
+               spin_unlock_irqrestore(&mvi->lock, flags);
+}
+
+
+void mvs_dev_gone(struct domain_device *dev)
+{
+       mvs_dev_gone_notify(dev, 1);
+}
+
+static  struct sas_task *mvs_alloc_task(void)
+{
+       struct sas_task *task = kzalloc(sizeof(struct sas_task), GFP_KERNEL);
+
+       if (task) {
+               INIT_LIST_HEAD(&task->list);
+               spin_lock_init(&task->task_state_lock);
+               task->task_state_flags = SAS_TASK_STATE_PENDING;
+               init_timer(&task->timer);
+               init_completion(&task->completion);
+       }
+       return task;
+}
+
+static  void mvs_free_task(struct sas_task *task)
+{
+       if (task) {
+               BUG_ON(!list_empty(&task->list));
+               kfree(task);
+       }
+}
+
+static void mvs_task_done(struct sas_task *task)
+{
+       if (!del_timer(&task->timer))
+               return;
+       complete(&task->completion);
+}
+
+static void mvs_tmf_timedout(unsigned long data)
+{
+       struct sas_task *task = (struct sas_task *)data;
+
+       task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+       complete(&task->completion);
+}
+
+/* XXX */
+#define MVS_TASK_TIMEOUT 20
+static int mvs_exec_internal_tmf_task(struct domain_device *dev,
+                       void *parameter, u32 para_len, struct mvs_tmf_task *tmf)
+{
+       int res, retry;
+       struct sas_task *task = NULL;
+
+       for (retry = 0; retry < 3; retry++) {
+               task = mvs_alloc_task();
+               if (!task)
+                       return -ENOMEM;
+
+               task->dev = dev;
+               task->task_proto = dev->tproto;
+
+               memcpy(&task->ssp_task, parameter, para_len);
+               task->task_done = mvs_task_done;
+
+               task->timer.data = (unsigned long) task;
+               task->timer.function = mvs_tmf_timedout;
+               task->timer.expires = jiffies + MVS_TASK_TIMEOUT*HZ;
+               add_timer(&task->timer);
+
+               res = mvs_task_exec(task, 1, GFP_KERNEL, NULL, 1, tmf);
+
+               if (res) {
+                       del_timer(&task->timer);
+                       mv_printk("executing internel task failed:%d\n", res);
+                       goto ex_err;
+               }
+
+               wait_for_completion(&task->completion);
+               res = -TMF_RESP_FUNC_FAILED;
+               /* Even TMF timed out, return direct. */
+               if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
+                       if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
+                               mv_printk("TMF task[%x] timeout.\n", tmf->tmf);
+                               goto ex_err;
+                       }
+               }
+
+               if (task->task_status.resp == SAS_TASK_COMPLETE &&
+                   task->task_status.stat == SAM_GOOD) {
+                       res = TMF_RESP_FUNC_COMPLETE;
+                       break;
+               }
+
+               if (task->task_status.resp == SAS_TASK_COMPLETE &&
+                     task->task_status.stat == SAS_DATA_UNDERRUN) {
+                       /* no error, but return the number of bytes of
+                        * underrun */
+                       res = task->task_status.residual;
+                       break;
+               }
+
+               if (task->task_status.resp == SAS_TASK_COMPLETE &&
+                     task->task_status.stat == SAS_DATA_OVERRUN) {
+                       mv_dprintk("blocked task error.\n");
+                       res = -EMSGSIZE;
+                       break;
+               } else {
+                       mv_dprintk(" task to dev %016llx response: 0x%x "
+                                   "status 0x%x\n",
+                                   SAS_ADDR(dev->sas_addr),
+                                   task->task_status.resp,
+                                   task->task_status.stat);
+                       mvs_free_task(task);
+                       task = NULL;
+
+               }
+       }
+ex_err:
+       BUG_ON(retry == 3 && task != NULL);
+       if (task != NULL)
+               mvs_free_task(task);
+       return res;
+}
+
+static int mvs_debug_issue_ssp_tmf(struct domain_device *dev,
+                               u8 *lun, struct mvs_tmf_task *tmf)
+{
+       struct sas_ssp_task ssp_task;
+       DECLARE_COMPLETION_ONSTACK(completion);
+       if (!(dev->tproto & SAS_PROTOCOL_SSP))
+               return TMF_RESP_FUNC_ESUPP;
+
+       strncpy((u8 *)&ssp_task.LUN, lun, 8);
+
+       return mvs_exec_internal_tmf_task(dev, &ssp_task,
+                               sizeof(ssp_task), tmf);
+}
+
+
+/*  Standard mandates link reset for ATA  (type 0)
+    and hard reset for SSP (type 1) , only for RECOVERY */
+static int mvs_debug_I_T_nexus_reset(struct domain_device *dev)
+{
+       int rc;
+       struct sas_phy *phy = sas_find_local_phy(dev);
+       int reset_type = (dev->dev_type == SATA_DEV ||
+                       (dev->tproto & SAS_PROTOCOL_STP)) ? 0 : 1;
+       rc = sas_phy_reset(phy, reset_type);
+       msleep(2000);
+       return rc;
+}
+
+/* mandatory SAM-3 */
+int mvs_lu_reset(struct domain_device *dev, u8 *lun)
+{
+       unsigned long flags;
+       int i, phyno[WIDE_PORT_MAX_PHY], num , rc = TMF_RESP_FUNC_FAILED;
+       struct mvs_tmf_task tmf_task;
+       struct mvs_device * mvi_dev = dev->lldd_dev;
+       struct mvs_info *mvi = mvi_dev->mvi_info;
+
+       tmf_task.tmf = TMF_LU_RESET;
+       mvi_dev->dev_status = MVS_DEV_EH;
+       rc = mvs_debug_issue_ssp_tmf(dev, lun, &tmf_task);
+       if (rc == TMF_RESP_FUNC_COMPLETE) {
+               num = mvs_find_dev_phyno(dev, phyno);
+               spin_lock_irqsave(&mvi->lock, flags);
+               for (i = 0; i < num; i++)
+                       mvs_release_task(mvi, phyno[i], dev);
+               spin_unlock_irqrestore(&mvi->lock, flags);
+       }
+       /* If failed, fall-through I_T_Nexus reset */
+       mv_printk("%s for device[%x]:rc= %d\n", __func__,
+                       mvi_dev->device_id, rc);
+       return rc;
+}
+
+int mvs_I_T_nexus_reset(struct domain_device *dev)
+{
+       unsigned long flags;
+       int i, phyno[WIDE_PORT_MAX_PHY], num , rc = TMF_RESP_FUNC_FAILED;
+       struct mvs_device * mvi_dev = (struct mvs_device *)dev->lldd_dev;
+       struct mvs_info *mvi = mvi_dev->mvi_info;
+
+       if (mvi_dev->dev_status != MVS_DEV_EH)
+               return TMF_RESP_FUNC_COMPLETE;
+       rc = mvs_debug_I_T_nexus_reset(dev);
+       mv_printk("%s for device[%x]:rc= %d\n",
+               __func__, mvi_dev->device_id, rc);
+
+       /* housekeeper */
+       num = mvs_find_dev_phyno(dev, phyno);
+       spin_lock_irqsave(&mvi->lock, flags);
+       for (i = 0; i < num; i++)
+               mvs_release_task(mvi, phyno[i], dev);
+       spin_unlock_irqrestore(&mvi->lock, flags);
+
+       return rc;
+}
+/* optional SAM-3 */
+int mvs_query_task(struct sas_task *task)
+{
+       u32 tag;
+       struct scsi_lun lun;
+       struct mvs_tmf_task tmf_task;
+       int rc = TMF_RESP_FUNC_FAILED;
+
+       if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) {
+               struct scsi_cmnd * cmnd = (struct scsi_cmnd *)task->uldd_task;
+               struct domain_device *dev = task->dev;
+               struct mvs_device *mvi_dev = (struct mvs_device *)dev->lldd_dev;
+               struct mvs_info *mvi = mvi_dev->mvi_info;
+
+               int_to_scsilun(cmnd->device->lun, &lun);
+               rc = mvs_find_tag(mvi, task, &tag);
+               if (rc == 0) {
+                       rc = TMF_RESP_FUNC_FAILED;
+                       return rc;
+               }
+
+               tmf_task.tmf = TMF_QUERY_TASK;
+               tmf_task.tag_of_task_to_be_managed = cpu_to_le16(tag);
+
+               rc = mvs_debug_issue_ssp_tmf(dev, lun.scsi_lun, &tmf_task);
+               switch (rc) {
+               /* The task is still in Lun, release it then */
+               case TMF_RESP_FUNC_SUCC:
+               /* The task is not in Lun or failed, reset the phy */
+               case TMF_RESP_FUNC_FAILED:
+               case TMF_RESP_FUNC_COMPLETE:
+                       break;
+               }
+       }
+       mv_printk("%s:rc= %d\n", __func__, rc);
+       return rc;
+}
+
+/*  mandatory SAM-3, still need free task/slot info */
+int mvs_abort_task(struct sas_task *task)
+{
+       struct scsi_lun lun;
+       struct mvs_tmf_task tmf_task;
+       struct domain_device *dev = task->dev;
+       struct mvs_device *mvi_dev = (struct mvs_device *)dev->lldd_dev;
+       struct mvs_info *mvi = mvi_dev->mvi_info;
+       int rc = TMF_RESP_FUNC_FAILED;
+       unsigned long flags;
+       u32 tag;
+
+       if (mvi->exp_req)
+               mvi->exp_req--;
+       spin_lock_irqsave(&task->task_state_lock, flags);
+       if (task->task_state_flags & SAS_TASK_STATE_DONE) {
+               spin_unlock_irqrestore(&task->task_state_lock, flags);
+               rc = TMF_RESP_FUNC_COMPLETE;
+               goto out;
+       }
+       spin_unlock_irqrestore(&task->task_state_lock, flags);
+       if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) {
+               struct scsi_cmnd * cmnd = (struct scsi_cmnd *)task->uldd_task;
+
+               int_to_scsilun(cmnd->device->lun, &lun);
+               rc = mvs_find_tag(mvi, task, &tag);
+               if (rc == 0) {
+                       mv_printk("No such tag in %s\n", __func__);
+                       rc = TMF_RESP_FUNC_FAILED;
+                       return rc;
+               }
+
+               tmf_task.tmf = TMF_ABORT_TASK;
+               tmf_task.tag_of_task_to_be_managed = cpu_to_le16(tag);
+
+               rc = mvs_debug_issue_ssp_tmf(dev, lun.scsi_lun, &tmf_task);
+
+               /* if successful, clear the task and callback forwards.*/
+               if (rc == TMF_RESP_FUNC_COMPLETE) {
+                       u32 slot_no;
+                       struct mvs_slot_info *slot;
+
+                       if (task->lldd_task) {
+                               slot = task->lldd_task;
+                               slot_no = (u32) (slot - mvi->slot_info);
+                               mvs_slot_complete(mvi, slot_no, 1);
+                       }
+               }
+       } else if (task->task_proto & SAS_PROTOCOL_SATA ||
+               task->task_proto & SAS_PROTOCOL_STP) {
+               /* to do free register_set */
+       } else {
+               /* SMP */
+
+       }
+out:
+       if (rc != TMF_RESP_FUNC_COMPLETE)
+               mv_printk("%s:rc= %d\n", __func__, rc);
+       return rc;
+}
+
+int mvs_abort_task_set(struct domain_device *dev, u8 *lun)
+{
+       int rc = TMF_RESP_FUNC_FAILED;
+       struct mvs_tmf_task tmf_task;
+
+       tmf_task.tmf = TMF_ABORT_TASK_SET;
+       rc = mvs_debug_issue_ssp_tmf(dev, lun, &tmf_task);
+
+       return rc;
+}
+
+int mvs_clear_aca(struct domain_device *dev, u8 *lun)
+{
+       int rc = TMF_RESP_FUNC_FAILED;
+       struct mvs_tmf_task tmf_task;
+
+       tmf_task.tmf = TMF_CLEAR_ACA;
+       rc = mvs_debug_issue_ssp_tmf(dev, lun, &tmf_task);
+
+       return rc;
+}
+
+int mvs_clear_task_set(struct domain_device *dev, u8 *lun)
+{
+       int rc = TMF_RESP_FUNC_FAILED;
+       struct mvs_tmf_task tmf_task;
+
+       tmf_task.tmf = TMF_CLEAR_TASK_SET;
+       rc = mvs_debug_issue_ssp_tmf(dev, lun, &tmf_task);
+
+       return rc;
+}
+
+static int mvs_sata_done(struct mvs_info *mvi, struct sas_task *task,
+                       u32 slot_idx, int err)
+{
+       struct mvs_device *mvi_dev = task->dev->lldd_dev;
+       struct task_status_struct *tstat = &task->task_status;
+       struct ata_task_resp *resp = (struct ata_task_resp *)tstat->buf;
+       int stat = SAM_GOOD;
+
+
+       resp->frame_len = sizeof(struct dev_to_host_fis);
+       memcpy(&resp->ending_fis[0],
+              SATA_RECEIVED_D2H_FIS(mvi_dev->taskfileset),
+              sizeof(struct dev_to_host_fis));
+       tstat->buf_valid_size = sizeof(*resp);
+       if (unlikely(err))
+               stat = SAS_PROTO_RESPONSE;
+       return stat;
+}
+
+static int mvs_slot_err(struct mvs_info *mvi, struct sas_task *task,
+                        u32 slot_idx)
+{
+       struct mvs_slot_info *slot = &mvi->slot_info[slot_idx];
+       int stat;
+       u32 err_dw0 = le32_to_cpu(*(u32 *) (slot->response));
+       u32 tfs = 0;
+       enum mvs_port_type type = PORT_TYPE_SAS;
+
+       if (err_dw0 & CMD_ISS_STPD)
+               MVS_CHIP_DISP->issue_stop(mvi, type, tfs);
+
+       MVS_CHIP_DISP->command_active(mvi, slot_idx);
+
+       stat = SAM_CHECK_COND;
+       switch (task->task_proto) {
+       case SAS_PROTOCOL_SSP:
+               stat = SAS_ABORTED_TASK;
+               break;
+       case SAS_PROTOCOL_SMP:
+               stat = SAM_CHECK_COND;
+               break;
+
+       case SAS_PROTOCOL_SATA:
+       case SAS_PROTOCOL_STP:
+       case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+       {
+               if (err_dw0 == 0x80400002)
+                       mv_printk("find reserved error, why?\n");
+
+               task->ata_task.use_ncq = 0;
+               stat = SAS_PROTO_RESPONSE;
+               mvs_sata_done(mvi, task, slot_idx, 1);
+
+       }
+               break;
+       default:
+               break;
+       }
+
+       return stat;
+}
+
+int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags)
+{
+       u32 slot_idx = rx_desc & RXQ_SLOT_MASK;
+       struct mvs_slot_info *slot = &mvi->slot_info[slot_idx];
+       struct sas_task *task = slot->task;
+       struct mvs_device *mvi_dev = NULL;
+       struct task_status_struct *tstat;
+
+       bool aborted;
+       void *to;
+       enum exec_status sts;
+
+       if (mvi->exp_req)
+               mvi->exp_req--;
+       if (unlikely(!task || !task->lldd_task))
+               return -1;
+
+       tstat = &task->task_status;
+       mvi_dev = task->dev->lldd_dev;
+
+       mvs_hba_cq_dump(mvi);
+
+       spin_lock(&task->task_state_lock);
+       task->task_state_flags &=
+               ~(SAS_TASK_STATE_PENDING | SAS_TASK_AT_INITIATOR);
+       task->task_state_flags |= SAS_TASK_STATE_DONE;
+       /* race condition*/
+       aborted = task->task_state_flags & SAS_TASK_STATE_ABORTED;
+       spin_unlock(&task->task_state_lock);
+
+       memset(tstat, 0, sizeof(*tstat));
+       tstat->resp = SAS_TASK_COMPLETE;
+
+       if (unlikely(aborted)) {
+               tstat->stat = SAS_ABORTED_TASK;
+               if (mvi_dev)
+                       mvi_dev->runing_req--;
+               if (sas_protocol_ata(task->task_proto))
+                       mvs_free_reg_set(mvi, mvi_dev);
+
+               mvs_slot_task_free(mvi, task, slot, slot_idx);
+               return -1;
+       }
+
+       if (unlikely(!mvi_dev || !slot->port->port_attached || flags)) {
+               mv_dprintk("port has not device.\n");
+               tstat->stat = SAS_PHY_DOWN;
+               goto out;
+       }
+
+       /*
+       if (unlikely((rx_desc & RXQ_ERR) || (*(u64 *) slot->response))) {
+                mv_dprintk("Find device[%016llx] RXQ_ERR %X,
+                err info:%016llx\n",
+                SAS_ADDR(task->dev->sas_addr),
+                rx_desc, (u64)(*(u64 *) slot->response));
+       }
+       */
+
+       /* error info record present */
+       if (unlikely((rx_desc & RXQ_ERR) && (*(u64 *) slot->response))) {
+               tstat->stat = mvs_slot_err(mvi, task, slot_idx);
+               goto out;
+       }
+
+       switch (task->task_proto) {
+       case SAS_PROTOCOL_SSP:
+               /* hw says status == 0, datapres == 0 */
+               if (rx_desc & RXQ_GOOD) {
+                       tstat->stat = SAM_GOOD;
+                       tstat->resp = SAS_TASK_COMPLETE;
+               }
+               /* response frame present */
+               else if (rx_desc & RXQ_RSP) {
+                       struct ssp_response_iu *iu = slot->response +
+                                               sizeof(struct mvs_err_info);
+                       sas_ssp_task_response(mvi->dev, task, iu);
+               } else
+                       tstat->stat = SAM_CHECK_COND;
+               break;
+
+       case SAS_PROTOCOL_SMP: {
+                       struct scatterlist *sg_resp = &task->smp_task.smp_resp;
+                       tstat->stat = SAM_GOOD;
+                       to = kmap_atomic(sg_page(sg_resp), KM_IRQ0);
+                       memcpy(to + sg_resp->offset,
+                               slot->response + sizeof(struct mvs_err_info),
+                               sg_dma_len(sg_resp));
+                       kunmap_atomic(to, KM_IRQ0);
+                       break;
+               }
+
+       case SAS_PROTOCOL_SATA:
+       case SAS_PROTOCOL_STP:
+       case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP: {
+                       tstat->stat = mvs_sata_done(mvi, task, slot_idx, 0);
+                       break;
+               }
+
+       default:
+               tstat->stat = SAM_CHECK_COND;
+               break;
+       }
+
+out:
+       if (mvi_dev) {
+               mvi_dev->runing_req--;
+               if (sas_protocol_ata(task->task_proto))
+                       mvs_free_reg_set(mvi, mvi_dev);
+       }
+       mvs_slot_task_free(mvi, task, slot, slot_idx);
+       sts = tstat->stat;
+
+       spin_unlock(&mvi->lock);
+       if (task->task_done)
+               task->task_done(task);
+       else
+               mv_dprintk("why has not task_done.\n");
+       spin_lock(&mvi->lock);
+
+       return sts;
+}
+
+void mvs_release_task(struct mvs_info *mvi,
+               int phy_no, struct domain_device *dev)
+{
+       int i = 0; u32 slot_idx;
+       struct mvs_phy *phy;
+       struct mvs_port *port;
+       struct mvs_slot_info *slot, *slot2;
+
+       phy = &mvi->phy[phy_no];
+       port = phy->port;
+       if (!port)
+               return;
+
+       list_for_each_entry_safe(slot, slot2, &port->list, entry) {
+               struct sas_task *task;
+               slot_idx = (u32) (slot - mvi->slot_info);
+               task = slot->task;
+
+               if (dev && task->dev != dev)
+                       continue;
+
+               mv_printk("Release slot [%x] tag[%x], task [%p]:\n",
+                       slot_idx, slot->slot_tag, task);
+
+               if (task->task_proto & SAS_PROTOCOL_SSP) {
+                       mv_printk("attached with SSP task CDB[");
+                       for (i = 0; i < 16; i++)
+                               mv_printk(" %02x", task->ssp_task.cdb[i]);
+                       mv_printk(" ]\n");
+               }
+
+               mvs_slot_complete(mvi, slot_idx, 1);
+       }
+}
+
+static void mvs_phy_disconnected(struct mvs_phy *phy)
+{
+       phy->phy_attached = 0;
+       phy->att_dev_info = 0;
+       phy->att_dev_sas_addr = 0;
+}
+
+static void mvs_work_queue(struct work_struct *work)
+{
+       struct delayed_work *dw = container_of(work, struct delayed_work, work);
+       struct mvs_wq *mwq = container_of(dw, struct mvs_wq, work_q);
+       struct mvs_info *mvi = mwq->mvi;
+       unsigned long flags;
+
+       spin_lock_irqsave(&mvi->lock, flags);
+       if (mwq->handler & PHY_PLUG_EVENT) {
+               u32 phy_no = (unsigned long) mwq->data;
+               struct sas_ha_struct *sas_ha = mvi->sas;
+               struct mvs_phy *phy = &mvi->phy[phy_no];
+               struct asd_sas_phy *sas_phy = &phy->sas_phy;
+
+               if (phy->phy_event & PHY_PLUG_OUT) {
+                       u32 tmp;
+                       struct sas_identify_frame *id;
+                       id = (struct sas_identify_frame *)phy->frame_rcvd;
+                       tmp = MVS_CHIP_DISP->read_phy_ctl(mvi, phy_no);
+                       phy->phy_event &= ~PHY_PLUG_OUT;
+                       if (!(tmp & PHY_READY_MASK)) {
+                               sas_phy_disconnected(sas_phy);
+                               mvs_phy_disconnected(phy);
+                               sas_ha->notify_phy_event(sas_phy,
+                                       PHYE_LOSS_OF_SIGNAL);
+                               mv_dprintk("phy%d Removed Device\n", phy_no);
+                       } else {
+                               MVS_CHIP_DISP->detect_porttype(mvi, phy_no);
+                               mvs_update_phyinfo(mvi, phy_no, 1);
+                               mvs_bytes_dmaed(mvi, phy_no);
+                               mvs_port_notify_formed(sas_phy, 0);
+                               mv_dprintk("phy%d Attached Device\n", phy_no);
+                       }
+               }
+       }
+       list_del(&mwq->entry);
+       spin_unlock_irqrestore(&mvi->lock, flags);
+       kfree(mwq);
+}
+
+static int mvs_handle_event(struct mvs_info *mvi, void *data, int handler)
+{
+       struct mvs_wq *mwq;
+       int ret = 0;
+
+       mwq = kmalloc(sizeof(struct mvs_wq), GFP_ATOMIC);
+       if (mwq) {
+               mwq->mvi = mvi;
+               mwq->data = data;
+               mwq->handler = handler;
+               MV_INIT_DELAYED_WORK(&mwq->work_q, mvs_work_queue, mwq);
+               list_add_tail(&mwq->entry, &mvi->wq_list);
+               schedule_delayed_work(&mwq->work_q, HZ * 2);
+       } else
+               ret = -ENOMEM;
+
+       return ret;
+}
+
+static void mvs_sig_time_out(unsigned long tphy)
+{
+       struct mvs_phy *phy = (struct mvs_phy *)tphy;
+       struct mvs_info *mvi = phy->mvi;
+       u8 phy_no;
+
+       for (phy_no = 0; phy_no < mvi->chip->n_phy; phy_no++) {
+               if (&mvi->phy[phy_no] == phy) {
+                       mv_dprintk("Get signature time out, reset phy %d\n",
+                               phy_no+mvi->id*mvi->chip->n_phy);
+                       MVS_CHIP_DISP->phy_reset(mvi, phy_no, 1);
+               }
+       }
+}
+
+static void mvs_sig_remove_timer(struct mvs_phy *phy)
+{
+       if (phy->timer.function)
+               del_timer(&phy->timer);
+       phy->timer.function = NULL;
+}
+
+void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
+{
+       u32 tmp;
+       struct sas_ha_struct *sas_ha = mvi->sas;
+       struct mvs_phy *phy = &mvi->phy[phy_no];
+       struct asd_sas_phy *sas_phy = &phy->sas_phy;
+
+       phy->irq_status = MVS_CHIP_DISP->read_port_irq_stat(mvi, phy_no);
+       mv_dprintk("port %d ctrl sts=0x%X.\n", phy_no+mvi->id*mvi->chip->n_phy,
+               MVS_CHIP_DISP->read_phy_ctl(mvi, phy_no));
+       mv_dprintk("Port %d irq sts = 0x%X\n", phy_no+mvi->id*mvi->chip->n_phy,
+               phy->irq_status);
+
+       /*
+       * events is port event now ,
+       * we need check the interrupt status which belongs to per port.
+       */
+
+       if (phy->irq_status & PHYEV_DCDR_ERR)
+               mv_dprintk("port %d STP decoding error.\n",
+               phy_no+mvi->id*mvi->chip->n_phy);
+
+       if (phy->irq_status & PHYEV_POOF) {
+               if (!(phy->phy_event & PHY_PLUG_OUT)) {
+                       int dev_sata = phy->phy_type & PORT_TYPE_SATA;
+                       int ready;
+                       mvs_release_task(mvi, phy_no, NULL);
+                       phy->phy_event |= PHY_PLUG_OUT;
+                       mvs_handle_event(mvi,
+                               (void *)(unsigned long)phy_no,
+                               PHY_PLUG_EVENT);
+                       ready = mvs_is_phy_ready(mvi, phy_no);
+                       if (!ready)
+                               mv_dprintk("phy%d Unplug Notice\n",
+                                       phy_no +
+                                       mvi->id * mvi->chip->n_phy);
+                       if (ready || dev_sata) {
+                               if (MVS_CHIP_DISP->stp_reset)
+                                       MVS_CHIP_DISP->stp_reset(mvi,
+                                                       phy_no);
+                               else
+                                       MVS_CHIP_DISP->phy_reset(mvi,
+                                                       phy_no, 0);
+                               return;
+                       }
+               }
+       }
+
+       if (phy->irq_status & PHYEV_COMWAKE) {
+               tmp = MVS_CHIP_DISP->read_port_irq_mask(mvi, phy_no);
+               MVS_CHIP_DISP->write_port_irq_mask(mvi, phy_no,
+                                       tmp | PHYEV_SIG_FIS);
+               if (phy->timer.function == NULL) {
+                       phy->timer.data = (unsigned long)phy;
+                       phy->timer.function = mvs_sig_time_out;
+                       phy->timer.expires = jiffies + 10*HZ;
+                       add_timer(&phy->timer);
+               }
+       }
+       if (phy->irq_status & (PHYEV_SIG_FIS | PHYEV_ID_DONE)) {
+               phy->phy_status = mvs_is_phy_ready(mvi, phy_no);
+               mvs_sig_remove_timer(phy);
+               mv_dprintk("notify plug in on phy[%d]\n", phy_no);
+               if (phy->phy_status) {
+                       mdelay(10);
+                       MVS_CHIP_DISP->detect_porttype(mvi, phy_no);
+                       if (phy->phy_type & PORT_TYPE_SATA) {
+                               tmp = MVS_CHIP_DISP->read_port_irq_mask(
+                                               mvi, phy_no);
+                               tmp &= ~PHYEV_SIG_FIS;
+                               MVS_CHIP_DISP->write_port_irq_mask(mvi,
+                                                       phy_no, tmp);
+                       }
+                       mvs_update_phyinfo(mvi, phy_no, 0);
+                       mvs_bytes_dmaed(mvi, phy_no);
+                       /* whether driver is going to handle hot plug */
+                       if (phy->phy_event & PHY_PLUG_OUT) {
+                               mvs_port_notify_formed(sas_phy, 0);
+                               phy->phy_event &= ~PHY_PLUG_OUT;
+                       }
+               } else {
+                       mv_dprintk("plugin interrupt but phy%d is gone\n",
+                               phy_no + mvi->id*mvi->chip->n_phy);
+               }
+       } else if (phy->irq_status & PHYEV_BROAD_CH) {
+               mv_dprintk("port %d broadcast change.\n",
+                       phy_no + mvi->id*mvi->chip->n_phy);
+               /* exception for Samsung disk drive*/
+               mdelay(1000);
+               sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
+       }
+       MVS_CHIP_DISP->write_port_irq_stat(mvi, phy_no, phy->irq_status);
+}
+
+int mvs_int_rx(struct mvs_info *mvi, bool self_clear)
+{
+       u32 rx_prod_idx, rx_desc;
+       bool attn = false;
+
+       /* the first dword in the RX ring is special: it contains
+        * a mirror of the hardware's RX producer index, so that
+        * we don't have to stall the CPU reading that register.
+        * The actual RX ring is offset by one dword, due to this.
+        */
+       rx_prod_idx = mvi->rx_cons;
+       mvi->rx_cons = le32_to_cpu(mvi->rx[0]);
+       if (mvi->rx_cons == 0xfff)      /* h/w hasn't touched RX ring yet */
+               return 0;
+
+       /* The CMPL_Q may come late, read from register and try again
+       * note: if coalescing is enabled,
+       * it will need to read from register every time for sure
+       */
+       if (unlikely(mvi->rx_cons == rx_prod_idx))
+               mvi->rx_cons = MVS_CHIP_DISP->rx_update(mvi) & RX_RING_SZ_MASK;
+
+       if (mvi->rx_cons == rx_prod_idx)
+               return 0;
+
+       while (mvi->rx_cons != rx_prod_idx) {
+               /* increment our internal RX consumer pointer */
+               rx_prod_idx = (rx_prod_idx + 1) & (MVS_RX_RING_SZ - 1);
+               rx_desc = le32_to_cpu(mvi->rx[rx_prod_idx + 1]);
+
+               if (likely(rx_desc & RXQ_DONE))
+                       mvs_slot_complete(mvi, rx_desc, 0);
+               if (rx_desc & RXQ_ATTN) {
+                       attn = true;
+               } else if (rx_desc & RXQ_ERR) {
+                       if (!(rx_desc & RXQ_DONE))
+                               mvs_slot_complete(mvi, rx_desc, 0);
+               } else if (rx_desc & RXQ_SLOT_RESET) {
+                       mvs_slot_free(mvi, rx_desc);
+               }
+       }
+
+       if (attn && self_clear)
+               MVS_CHIP_DISP->int_full(mvi);
+       return 0;
+}
+
diff --git a/drivers/scsi/mvsas/mv_sas.h b/drivers/scsi/mvsas/mv_sas.h
new file mode 100644 (file)
index 0000000..aa2270a
--- /dev/null
@@ -0,0 +1,406 @@
+/*
+ * Marvell 88SE64xx/88SE94xx main function head file
+ *
+ * Copyright 2007 Red Hat, Inc.
+ * Copyright 2008 Marvell. <kewei@marvell.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This program is free software; 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.
+ *
+ * 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 _MV_SAS_H_
+#define _MV_SAS_H_
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/ctype.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/vmalloc.h>
+#include <scsi/libsas.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/sas_ata.h>
+#include <linux/version.h>
+#include "mv_defs.h"
+
+#define DRV_NAME               "mvsas"
+#define DRV_VERSION            "0.8.2"
+#define _MV_DUMP               0
+#define MVS_ID_NOT_MAPPED      0x7f
+/* #define DISABLE_HOTPLUG_DMA_FIX */
+#define MAX_EXP_RUNNING_REQ    2
+#define WIDE_PORT_MAX_PHY              4
+#define        MV_DISABLE_NCQ  0
+#define mv_printk(fmt, arg ...)        \
+       printk(KERN_DEBUG"%s %d:" fmt, __FILE__, __LINE__, ## arg)
+#ifdef MV_DEBUG
+#define mv_dprintk(format, arg...)     \
+       printk(KERN_DEBUG"%s %d:" format, __FILE__, __LINE__, ## arg)
+#else
+#define mv_dprintk(format, arg...)
+#endif
+#define MV_MAX_U32                     0xffffffff
+
+extern struct mvs_tgt_initiator mvs_tgt;
+extern struct mvs_info *tgt_mvi;
+extern const struct mvs_dispatch mvs_64xx_dispatch;
+extern const struct mvs_dispatch mvs_94xx_dispatch;
+
+#define DEV_IS_EXPANDER(type)  \
+       ((type == EDGE_DEV) || (type == FANOUT_DEV))
+
+#define bit(n) ((u32)1 << n)
+
+#define for_each_phy(__lseq_mask, __mc, __lseq)                        \
+       for ((__mc) = (__lseq_mask), (__lseq) = 0;              \
+                                       (__mc) != 0 ;           \
+                                       (++__lseq), (__mc) >>= 1)
+
+#define MV_INIT_DELAYED_WORK(w, f, d)  INIT_DELAYED_WORK(w, f)
+#define UNASSOC_D2H_FIS(id)            \
+       ((void *) mvi->rx_fis + 0x100 * id)
+#define SATA_RECEIVED_FIS_LIST(reg_set)        \
+       ((void *) mvi->rx_fis + mvi->chip->fis_offs + 0x100 * reg_set)
+#define SATA_RECEIVED_SDB_FIS(reg_set) \
+       (SATA_RECEIVED_FIS_LIST(reg_set) + 0x58)
+#define SATA_RECEIVED_D2H_FIS(reg_set) \
+       (SATA_RECEIVED_FIS_LIST(reg_set) + 0x40)
+#define SATA_RECEIVED_PIO_FIS(reg_set) \
+       (SATA_RECEIVED_FIS_LIST(reg_set) + 0x20)
+#define SATA_RECEIVED_DMA_FIS(reg_set) \
+       (SATA_RECEIVED_FIS_LIST(reg_set) + 0x00)
+
+enum dev_status {
+       MVS_DEV_NORMAL = 0x0,
+       MVS_DEV_EH      = 0x1,
+};
+
+
+struct mvs_info;
+
+struct mvs_dispatch {
+       char *name;
+       int (*chip_init)(struct mvs_info *mvi);
+       int (*spi_init)(struct mvs_info *mvi);
+       int (*chip_ioremap)(struct mvs_info *mvi);
+       void (*chip_iounmap)(struct mvs_info *mvi);
+       irqreturn_t (*isr)(struct mvs_info *mvi, int irq, u32 stat);
+       u32 (*isr_status)(struct mvs_info *mvi, int irq);
+       void (*interrupt_enable)(struct mvs_info *mvi);
+       void (*interrupt_disable)(struct mvs_info *mvi);
+
+       u32 (*read_phy_ctl)(struct mvs_info *mvi, u32 port);
+       void (*write_phy_ctl)(struct mvs_info *mvi, u32 port, u32 val);
+
+       u32 (*read_port_cfg_data)(struct mvs_info *mvi, u32 port);
+       void (*write_port_cfg_data)(struct mvs_info *mvi, u32 port, u32 val);
+       void (*write_port_cfg_addr)(struct mvs_info *mvi, u32 port, u32 addr);
+
+       u32 (*read_port_vsr_data)(struct mvs_info *mvi, u32 port);
+       void (*write_port_vsr_data)(struct mvs_info *mvi, u32 port, u32 val);
+       void (*write_port_vsr_addr)(struct mvs_info *mvi, u32 port, u32 addr);
+
+       u32 (*read_port_irq_stat)(struct mvs_info *mvi, u32 port);
+       void (*write_port_irq_stat)(struct mvs_info *mvi, u32 port, u32 val);
+
+       u32 (*read_port_irq_mask)(struct mvs_info *mvi, u32 port);
+       void (*write_port_irq_mask)(struct mvs_info *mvi, u32 port, u32 val);
+
+       void (*get_sas_addr)(void *buf, u32 buflen);
+       void (*command_active)(struct mvs_info *mvi, u32 slot_idx);
+       void (*issue_stop)(struct mvs_info *mvi, enum mvs_port_type type,
+                               u32 tfs);
+       void (*start_delivery)(struct mvs_info *mvi, u32 tx);
+       u32 (*rx_update)(struct mvs_info *mvi);
+       void (*int_full)(struct mvs_info *mvi);
+       u8 (*assign_reg_set)(struct mvs_info *mvi, u8 *tfs);
+       void (*free_reg_set)(struct mvs_info *mvi, u8 *tfs);
+       u32 (*prd_size)(void);
+       u32 (*prd_count)(void);
+       void (*make_prd)(struct scatterlist *scatter, int nr, void *prd);
+       void (*detect_porttype)(struct mvs_info *mvi, int i);
+       int (*oob_done)(struct mvs_info *mvi, int i);
+       void (*fix_phy_info)(struct mvs_info *mvi, int i,
+                               struct sas_identify_frame *id);
+       void (*phy_work_around)(struct mvs_info *mvi, int i);
+       void (*phy_set_link_rate)(struct mvs_info *mvi, u32 phy_id,
+                               struct sas_phy_linkrates *rates);
+       u32 (*phy_max_link_rate)(void);
+       void (*phy_disable)(struct mvs_info *mvi, u32 phy_id);
+       void (*phy_enable)(struct mvs_info *mvi, u32 phy_id);
+       void (*phy_reset)(struct mvs_info *mvi, u32 phy_id, int hard);
+       void (*stp_reset)(struct mvs_info *mvi, u32 phy_id);
+       void (*clear_active_cmds)(struct mvs_info *mvi);
+       u32 (*spi_read_data)(struct mvs_info *mvi);
+       void (*spi_write_data)(struct mvs_info *mvi, u32 data);
+       int (*spi_buildcmd)(struct mvs_info *mvi,
+                                               u32      *dwCmd,
+                                               u8       cmd,
+                                               u8       read,
+                                               u8       length,
+                                               u32      addr
+                                               );
+       int (*spi_issuecmd)(struct mvs_info *mvi, u32 cmd);
+       int (*spi_waitdataready)(struct mvs_info *mvi, u32 timeout);
+#ifndef DISABLE_HOTPLUG_DMA_FIX
+       void (*dma_fix)(dma_addr_t buf_dma, int buf_len, int from, void *prd);
+#endif
+
+};
+
+struct mvs_chip_info {
+       u32             n_host;
+       u32             n_phy;
+       u32             fis_offs;
+       u32             fis_count;
+       u32             srs_sz;
+       u32             slot_width;
+       const struct mvs_dispatch *dispatch;
+};
+#define MVS_CHIP_SLOT_SZ       (1U << mvi->chip->slot_width)
+#define MVS_RX_FISL_SZ         \
+       (mvi->chip->fis_offs + (mvi->chip->fis_count * 0x100))
+#define MVS_CHIP_DISP          (mvi->chip->dispatch)
+
+struct mvs_err_info {
+       __le32                  flags;
+       __le32                  flags2;
+};
+
+struct mvs_cmd_hdr {
+       __le32                  flags;  /* PRD tbl len; SAS, SATA ctl */
+       __le32                  lens;   /* cmd, max resp frame len */
+       __le32                  tags;   /* targ port xfer tag; tag */
+       __le32                  data_len;       /* data xfer len */
+       __le64                  cmd_tbl;        /* command table address */
+       __le64                  open_frame;     /* open addr frame address */
+       __le64                  status_buf;     /* status buffer address */
+       __le64                  prd_tbl;                /* PRD tbl address */
+       __le32                  reserved[4];
+};
+
+struct mvs_port {
+       struct asd_sas_port     sas_port;
+       u8                      port_attached;
+       u8                      wide_port_phymap;
+       struct list_head        list;
+};
+
+struct mvs_phy {
+       struct mvs_info                 *mvi;
+       struct mvs_port         *port;
+       struct asd_sas_phy      sas_phy;
+       struct sas_identify     identify;
+       struct scsi_device      *sdev;
+       struct timer_list timer;
+       u64             dev_sas_addr;
+       u64             att_dev_sas_addr;
+       u32             att_dev_info;
+       u32             dev_info;
+       u32             phy_type;
+       u32             phy_status;
+       u32             irq_status;
+       u32             frame_rcvd_size;
+       u8              frame_rcvd[32];
+       u8              phy_attached;
+       u8              phy_mode;
+       u8              reserved[2];
+       u32             phy_event;
+       enum sas_linkrate       minimum_linkrate;
+       enum sas_linkrate       maximum_linkrate;
+};
+
+struct mvs_device {
+       struct list_head                dev_entry;
+       enum sas_dev_type dev_type;
+       struct mvs_info *mvi_info;
+       struct domain_device *sas_device;
+       u32 attached_phy;
+       u32 device_id;
+       u32 runing_req;
+       u8 taskfileset;
+       u8 dev_status;
+       u16 reserved;
+};
+
+struct mvs_slot_info {
+       struct list_head entry;
+       union {
+               struct sas_task *task;
+               void *tdata;
+       };
+       u32 n_elem;
+       u32 tx;
+       u32 slot_tag;
+
+       /* DMA buffer for storing cmd tbl, open addr frame, status buffer,
+        * and PRD table
+        */
+       void *buf;
+       dma_addr_t buf_dma;
+#if _MV_DUMP
+       u32 cmd_size;
+#endif
+       void *response;
+       struct mvs_port *port;
+       struct mvs_device       *device;
+       void *open_frame;
+};
+
+struct mvs_info {
+       unsigned long flags;
+
+       /* host-wide lock */
+       spinlock_t lock;
+
+       /* our device */
+       struct pci_dev *pdev;
+       struct device *dev;
+
+       /* enhanced mode registers */
+       void __iomem *regs;
+
+       /* peripheral or soc registers */
+       void __iomem *regs_ex;
+       u8 sas_addr[SAS_ADDR_SIZE];
+
+       /* SCSI/SAS glue */
+       struct sas_ha_struct *sas;
+       struct Scsi_Host *shost;
+
+       /* TX (delivery) DMA ring */
+       __le32 *tx;
+       dma_addr_t tx_dma;
+
+       /* cached next-producer idx */
+       u32 tx_prod;
+
+       /* RX (completion) DMA ring */
+       __le32  *rx;
+       dma_addr_t rx_dma;
+
+       /* RX consumer idx */
+       u32 rx_cons;
+
+       /* RX'd FIS area */
+       __le32 *rx_fis;
+       dma_addr_t rx_fis_dma;
+
+       /* DMA command header slots */
+       struct mvs_cmd_hdr *slot;
+       dma_addr_t slot_dma;
+
+       u32 chip_id;
+       const struct mvs_chip_info *chip;
+
+       int tags_num;
+       DECLARE_BITMAP(tags, MVS_SLOTS);
+       /* further per-slot information */
+       struct mvs_phy phy[MVS_MAX_PHYS];
+       struct mvs_port port[MVS_MAX_PHYS];
+       u32 irq;
+       u32 exp_req;
+       u32 id;
+       u64 sata_reg_set;
+       struct list_head *hba_list;
+       struct list_head soc_entry;
+       struct list_head wq_list;
+       unsigned long instance;
+       u16 flashid;
+       u32 flashsize;
+       u32 flashsectSize;
+
+       void *addon;
+       struct mvs_device       devices[MVS_MAX_DEVICES];
+#ifndef DISABLE_HOTPLUG_DMA_FIX
+       void *bulk_buffer;
+       dma_addr_t bulk_buffer_dma;
+#define TRASH_BUCKET_SIZE      0x20000
+#endif
+       struct mvs_slot_info slot_info[0];
+};
+
+struct mvs_prv_info{
+       u8 n_host;
+       u8 n_phy;
+       u16 reserve;
+       struct mvs_info *mvi[2];
+};
+
+struct mvs_wq {
+       struct delayed_work work_q;
+       struct mvs_info *mvi;
+       void *data;
+       int handler;
+       struct list_head entry;
+};
+
+struct mvs_task_exec_info {
+       struct sas_task *task;
+       struct mvs_cmd_hdr *hdr;
+       struct mvs_port *port;
+       u32 tag;
+       int n_elem;
+};
+
+
+/******************** function prototype *********************/
+void mvs_get_sas_addr(void *buf, u32 buflen);
+void mvs_tag_clear(struct mvs_info *mvi, u32 tag);
+void mvs_tag_free(struct mvs_info *mvi, u32 tag);
+void mvs_tag_set(struct mvs_info *mvi, unsigned int tag);
+int mvs_tag_alloc(struct mvs_info *mvi, u32 *tag_out);
+void mvs_tag_init(struct mvs_info *mvi);
+void mvs_iounmap(void __iomem *regs);
+int mvs_ioremap(struct mvs_info *mvi, int bar, int bar_ex);
+void mvs_phys_reset(struct mvs_info *mvi, u32 phy_mask, int hard);
+int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
+                       void *funcdata);
+void __devinit mvs_set_sas_addr(struct mvs_info *mvi, int port_id,
+                               u32 off_lo, u32 off_hi, u64 sas_addr);
+int mvs_slave_alloc(struct scsi_device *scsi_dev);
+int mvs_slave_configure(struct scsi_device *sdev);
+void mvs_scan_start(struct Scsi_Host *shost);
+int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time);
+int mvs_queue_command(struct sas_task *task, const int num,
+                       gfp_t gfp_flags);
+int mvs_abort_task(struct sas_task *task);
+int mvs_abort_task_set(struct domain_device *dev, u8 *lun);
+int mvs_clear_aca(struct domain_device *dev, u8 *lun);
+int mvs_clear_task_set(struct domain_device *dev, u8 * lun);
+void mvs_port_formed(struct asd_sas_phy *sas_phy);
+void mvs_port_deformed(struct asd_sas_phy *sas_phy);
+int mvs_dev_found(struct domain_device *dev);
+void mvs_dev_gone(struct domain_device *dev);
+int mvs_lu_reset(struct domain_device *dev, u8 *lun);
+int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags);
+int mvs_I_T_nexus_reset(struct domain_device *dev);
+int mvs_query_task(struct sas_task *task);
+void mvs_release_task(struct mvs_info *mvi, int phy_no,
+                       struct domain_device *dev);
+void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events);
+void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st);
+int mvs_int_rx(struct mvs_info *mvi, bool self_clear);
+void mvs_hexdump(u32 size, u8 *data, u32 baseaddr);
+#endif
+
index 0e207aa67d16e3e8e4c8c6e4c1b2f0d6c88fe8c8..5fd73d77c3af86b17344c1e544dd4ed22bdd6105 100644 (file)
 # it under the terms of the GNU General Public License version 2
 #
 
-ifneq ($(OSD_INC),)
-# we are built out-of-tree Kconfigure everything as on
-
-CONFIG_SCSI_OSD_INITIATOR=m
-ccflags-y += -DCONFIG_SCSI_OSD_INITIATOR -DCONFIG_SCSI_OSD_INITIATOR_MODULE
-
-CONFIG_SCSI_OSD_ULD=m
-ccflags-y += -DCONFIG_SCSI_OSD_ULD -DCONFIG_SCSI_OSD_ULD_MODULE
-
-# CONFIG_SCSI_OSD_DPRINT_SENSE =
-#      0 - no print of errors
-#      1 - print errors
-#      2 - errors + warrnings
-ccflags-y += -DCONFIG_SCSI_OSD_DPRINT_SENSE=1
-
-# Uncomment to turn debug on
-# ccflags-y += -DCONFIG_SCSI_OSD_DEBUG
-
-# if we are built out-of-tree and the hosting kernel has OSD headers
-# then "ccflags-y +=" will not pick the out-off-tree headers. Only by doing
-# this it will work. This might break in future kernels
-LINUXINCLUDE := -I$(OSD_INC) $(LINUXINCLUDE)
-
-endif
-
 # libosd.ko - osd-initiator library
 libosd-y := osd_initiator.o
 obj-$(CONFIG_SCSI_OSD_INITIATOR) += libosd.o
diff --git a/drivers/scsi/osd/Makefile b/drivers/scsi/osd/Makefile
deleted file mode 100755 (executable)
index d905344..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# Makefile for the OSD modules (out of tree)
-#
-# Copyright (C) 2008 Panasas Inc.  All rights reserved.
-#
-# Authors:
-#   Boaz Harrosh <bharrosh@panasas.com>
-#   Benny Halevy <bhalevy@panasas.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2
-#
-# This Makefile is used to call the kernel Makefile in case of an out-of-tree
-# build.
-# $KSRC should point to a Kernel source tree otherwise host's default is
-# used. (eg. /lib/modules/`uname -r`/build)
-
-# include path for out-of-tree Headers
-OSD_INC ?= `pwd`/../../../include
-
-# allow users to override these
-# e.g. to compile for a kernel that you aren't currently running
-KSRC ?= /lib/modules/$(shell uname -r)/build
-KBUILD_OUTPUT ?=
-ARCH ?=
-V ?= 0
-
-# this is the basic Kbuild out-of-tree invocation, with the M= option
-KBUILD_BASE = +$(MAKE) -C $(KSRC) M=`pwd` KBUILD_OUTPUT=$(KBUILD_OUTPUT) ARCH=$(ARCH) V=$(V)
-
-all: libosd
-
-libosd: ;
-       $(KBUILD_BASE) OSD_INC=$(OSD_INC) modules
-
-clean:
-       $(KBUILD_BASE) clean
index 5776b2ab6b12a5fa54fe974b11e31619d8ee18e0..7a117c18114cd757de416045da2d453327742131 100644 (file)
@@ -118,39 +118,39 @@ static int _osd_print_system_info(struct osd_dev *od, void *caps)
                _osd_ver_desc(or));
 
        pFirst = get_attrs[a++].val_ptr;
-       OSD_INFO("OSD_ATTR_RI_VENDOR_IDENTIFICATION [%s]\n",
+       OSD_INFO("VENDOR_IDENTIFICATION  [%s]\n",
                (char *)pFirst);
 
        pFirst = get_attrs[a++].val_ptr;
-       OSD_INFO("OSD_ATTR_RI_PRODUCT_IDENTIFICATION [%s]\n",
+       OSD_INFO("PRODUCT_IDENTIFICATION [%s]\n",
                (char *)pFirst);
 
        pFirst = get_attrs[a++].val_ptr;
-       OSD_INFO("OSD_ATTR_RI_PRODUCT_MODEL [%s]\n",
+       OSD_INFO("PRODUCT_MODEL          [%s]\n",
                (char *)pFirst);
 
        pFirst = get_attrs[a++].val_ptr;
-       OSD_INFO("OSD_ATTR_RI_PRODUCT_REVISION_LEVEL [%u]\n",
+       OSD_INFO("PRODUCT_REVISION_LEVEL [%u]\n",
                pFirst ? get_unaligned_be32(pFirst) : ~0U);
 
        pFirst = get_attrs[a++].val_ptr;
-       OSD_INFO("OSD_ATTR_RI_PRODUCT_SERIAL_NUMBER [%s]\n",
+       OSD_INFO("PRODUCT_SERIAL_NUMBER  [%s]\n",
                (char *)pFirst);
 
        pFirst = get_attrs[a].val_ptr;
-       OSD_INFO("OSD_ATTR_RI_OSD_NAME [%s]\n", (char *)pFirst);
+       OSD_INFO("OSD_NAME               [%s]\n", (char *)pFirst);
        a++;
 
        pFirst = get_attrs[a++].val_ptr;
-       OSD_INFO("OSD_ATTR_RI_TOTAL_CAPACITY [0x%llx]\n",
+       OSD_INFO("TOTAL_CAPACITY         [0x%llx]\n",
                pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL);
 
        pFirst = get_attrs[a++].val_ptr;
-       OSD_INFO("OSD_ATTR_RI_USED_CAPACITY [0x%llx]\n",
+       OSD_INFO("USED_CAPACITY          [0x%llx]\n",
                pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL);
 
        pFirst = get_attrs[a++].val_ptr;
-       OSD_INFO("OSD_ATTR_RI_NUMBER_OF_PARTITIONS [%llu]\n",
+       OSD_INFO("NUMBER_OF_PARTITIONS   [%llu]\n",
                pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL);
 
        if (a >= nelem)
@@ -158,7 +158,7 @@ static int _osd_print_system_info(struct osd_dev *od, void *caps)
 
        /* FIXME: Where are the time utilities */
        pFirst = get_attrs[a++].val_ptr;
-       OSD_INFO("OSD_ATTR_RI_CLOCK [0x%02x%02x%02x%02x%02x%02x]\n",
+       OSD_INFO("CLOCK                  [0x%02x%02x%02x%02x%02x%02x]\n",
                ((char *)pFirst)[0], ((char *)pFirst)[1],
                ((char *)pFirst)[2], ((char *)pFirst)[3],
                ((char *)pFirst)[4], ((char *)pFirst)[5]);
@@ -169,7 +169,8 @@ static int _osd_print_system_info(struct osd_dev *od, void *caps)
 
                hex_dump_to_buffer(get_attrs[a].val_ptr, len, 32, 1,
                                   sid_dump, sizeof(sid_dump), true);
-               OSD_INFO("OSD_ATTR_RI_OSD_SYSTEM_ID(%d) [%s]\n", len, sid_dump);
+               OSD_INFO("OSD_SYSTEM_ID(%d)\n"
+                        "        [%s]\n", len, sid_dump);
                a++;
        }
 out:
@@ -669,7 +670,7 @@ static int _osd_req_list_objects(struct osd_request *or,
        __be16 action, const struct osd_obj_id *obj, osd_id initial_id,
        struct osd_obj_id_list *list, unsigned nelem)
 {
-       struct request_queue *q = or->osd_dev->scsi_device->request_queue;
+       struct request_queue *q = osd_request_queue(or->osd_dev);
        u64 len = nelem * sizeof(osd_id) + sizeof(*list);
        struct bio *bio;
 
@@ -778,16 +779,32 @@ EXPORT_SYMBOL(osd_req_remove_object);
 */
 
 void osd_req_write(struct osd_request *or,
-       const struct osd_obj_id *obj, struct bio *bio, u64 offset)
+       const struct osd_obj_id *obj, u64 offset,
+       struct bio *bio, u64 len)
 {
-       _osd_req_encode_common(or, OSD_ACT_WRITE, obj, offset, bio->bi_size);
+       _osd_req_encode_common(or, OSD_ACT_WRITE, obj, offset, len);
        WARN_ON(or->out.bio || or->out.total_bytes);
-       bio->bi_rw |= (1 << BIO_RW);
+       WARN_ON(0 ==  bio_rw_flagged(bio, BIO_RW));
        or->out.bio = bio;
-       or->out.total_bytes = bio->bi_size;
+       or->out.total_bytes = len;
 }
 EXPORT_SYMBOL(osd_req_write);
 
+int osd_req_write_kern(struct osd_request *or,
+       const struct osd_obj_id *obj, u64 offset, void* buff, u64 len)
+{
+       struct request_queue *req_q = osd_request_queue(or->osd_dev);
+       struct bio *bio = bio_map_kern(req_q, buff, len, GFP_KERNEL);
+
+       if (IS_ERR(bio))
+               return PTR_ERR(bio);
+
+       bio->bi_rw |= (1 << BIO_RW); /* FIXME: bio_set_dir() */
+       osd_req_write(or, obj, offset, bio, len);
+       return 0;
+}
+EXPORT_SYMBOL(osd_req_write_kern);
+
 /*TODO: void osd_req_append(struct osd_request *,
        const struct osd_obj_id *, struct bio *data_out); */
 /*TODO: void osd_req_create_write(struct osd_request *,
@@ -813,16 +830,31 @@ void osd_req_flush_object(struct osd_request *or,
 EXPORT_SYMBOL(osd_req_flush_object);
 
 void osd_req_read(struct osd_request *or,
-       const struct osd_obj_id *obj, struct bio *bio, u64 offset)
+       const struct osd_obj_id *obj, u64 offset,
+       struct bio *bio, u64 len)
 {
-       _osd_req_encode_common(or, OSD_ACT_READ, obj, offset, bio->bi_size);
+       _osd_req_encode_common(or, OSD_ACT_READ, obj, offset, len);
        WARN_ON(or->in.bio || or->in.total_bytes);
-       bio->bi_rw &= ~(1 << BIO_RW);
+       WARN_ON(1 == bio_rw_flagged(bio, BIO_RW));
        or->in.bio = bio;
-       or->in.total_bytes = bio->bi_size;
+       or->in.total_bytes = len;
 }
 EXPORT_SYMBOL(osd_req_read);
 
+int osd_req_read_kern(struct osd_request *or,
+       const struct osd_obj_id *obj, u64 offset, void* buff, u64 len)
+{
+       struct request_queue *req_q = osd_request_queue(or->osd_dev);
+       struct bio *bio = bio_map_kern(req_q, buff, len, GFP_KERNEL);
+
+       if (IS_ERR(bio))
+               return PTR_ERR(bio);
+
+       osd_req_read(or, obj, offset, bio, len);
+       return 0;
+}
+EXPORT_SYMBOL(osd_req_read_kern);
+
 void osd_req_get_attributes(struct osd_request *or,
        const struct osd_obj_id *obj)
 {
@@ -1213,7 +1245,7 @@ static inline void osd_sec_parms_set_in_offset(bool is_v1,
 }
 
 static int _osd_req_finalize_data_integrity(struct osd_request *or,
-       bool has_in, bool has_out, const u8 *cap_key)
+       bool has_in, bool has_out, u64 out_data_bytes, const u8 *cap_key)
 {
        struct osd_security_parameters *sec_parms = _osd_req_sec_params(or);
        int ret;
@@ -1228,8 +1260,7 @@ static int _osd_req_finalize_data_integrity(struct osd_request *or,
                };
                unsigned pad;
 
-               or->out_data_integ.data_bytes = cpu_to_be64(
-                       or->out.bio ? or->out.bio->bi_size : 0);
+               or->out_data_integ.data_bytes = cpu_to_be64(out_data_bytes);
                or->out_data_integ.set_attributes_bytes = cpu_to_be64(
                        or->set_attr.total_bytes);
                or->out_data_integ.get_attributes_bytes = cpu_to_be64(
@@ -1306,6 +1337,8 @@ static int _init_blk_request(struct osd_request *or,
 
        or->request = req;
        req->cmd_type = REQ_TYPE_BLOCK_PC;
+       req->cmd_flags |= REQ_QUIET;
+
        req->timeout = or->timeout;
        req->retries = or->retries;
        req->sense = or->sense;
@@ -1339,6 +1372,7 @@ int osd_finalize_request(struct osd_request *or,
 {
        struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
        bool has_in, has_out;
+       u64 out_data_bytes = or->out.total_bytes;
        int ret;
 
        if (options & OSD_REQ_FUA)
@@ -1388,7 +1422,8 @@ int osd_finalize_request(struct osd_request *or,
                }
        }
 
-       ret = _osd_req_finalize_data_integrity(or, has_in, has_out, cap_key);
+       ret = _osd_req_finalize_data_integrity(or, has_in, has_out,
+                                              out_data_bytes, cap_key);
        if (ret)
                return ret;
 
index 22b59e13ba83fd7d91b71c6b78f7b945291ab5da..0bdef339090288d409e23557bf3c0956f400f241 100644 (file)
@@ -49,6 +49,7 @@
 #include <linux/device.h>
 #include <linux/idr.h>
 #include <linux/major.h>
+#include <linux/file.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_driver.h>
@@ -175,10 +176,9 @@ static const struct file_operations osd_fops = {
 
 struct osd_dev *osduld_path_lookup(const char *name)
 {
-       struct path path;
-       struct inode *inode;
-       struct cdev *cdev;
-       struct osd_uld_device *uninitialized_var(oud);
+       struct osd_uld_device *oud;
+       struct osd_dev *od;
+       struct file *file;
        int error;
 
        if (!name || !*name) {
@@ -186,52 +186,46 @@ struct osd_dev *osduld_path_lookup(const char *name)
                return ERR_PTR(-EINVAL);
        }
 
-       error = kern_path(name, LOOKUP_FOLLOW, &path);
-       if (error) {
-               OSD_ERR("path_lookup of %s failed=>%d\n", name, error);
-               return ERR_PTR(error);
-       }
+       od = kzalloc(sizeof(*od), GFP_KERNEL);
+       if (!od)
+               return ERR_PTR(-ENOMEM);
 
-       inode = path.dentry->d_inode;
-       error = -EINVAL; /* Not the right device e.g osd_uld_device */
-       if (!S_ISCHR(inode->i_mode)) {
-               OSD_DEBUG("!S_ISCHR()\n");
-               goto out;
+       file = filp_open(name, O_RDWR, 0);
+       if (IS_ERR(file)) {
+               error = PTR_ERR(file);
+               goto free_od;
        }
 
-       cdev = inode->i_cdev;
-       if (!cdev) {
-               OSD_ERR("Before mounting an OSD Based filesystem\n");
-               OSD_ERR("  user-mode must open+close the %s device\n", name);
-               OSD_ERR("  Example: bash: echo < %s\n", name);
-               goto out;
+       if (file->f_op != &osd_fops){
+               error = -EINVAL;
+               goto close_file;
        }
 
-       /* The Magic wand. Is it our char-dev */
-       /* TODO: Support sg devices */
-       if (cdev->owner != THIS_MODULE) {
-               OSD_ERR("Error mounting %s - is not an OSD device\n", name);
-               goto out;
-       }
+       oud = file->private_data;
 
-       oud = container_of(cdev, struct osd_uld_device, cdev);
+       *od = oud->od;
+       od->file = file;
 
-       __uld_get(oud);
-       error = 0;
+       return od;
 
-out:
-       path_put(&path);
-       return error ? ERR_PTR(error) : &oud->od;
+close_file:
+       fput(file);
+free_od:
+       kfree(od);
+       return ERR_PTR(error);
 }
 EXPORT_SYMBOL(osduld_path_lookup);
 
 void osduld_put_device(struct osd_dev *od)
 {
-       if (od) {
-               struct osd_uld_device *oud = container_of(od,
-                                               struct osd_uld_device, od);
 
-               __uld_put(oud);
+       if (od && !IS_ERR(od)) {
+               struct osd_uld_device *oud = od->file->private_data;
+
+               BUG_ON(od->scsi_device != oud->od.scsi_device);
+
+               fput(od->file);
+               kfree(od);
        }
 }
 EXPORT_SYMBOL(osduld_put_device);
index 5defe5ea5eda64937f605ebd6dceae3d6958c76f..8371d917a9a2408242a2439c9c69f298cc012964 100644 (file)
 * General Public License for more details.
 *
 ******************************************************************************/
-#define QLA1280_VERSION      "3.26"
+#define QLA1280_VERSION      "3.27"
 /*****************************************************************************
     Revision History:
+    Rev  3.27, February 10, 2009, Michael Reed
+       - General code cleanup.
+       - Improve error recovery.
     Rev  3.26, January 16, 2006 Jes Sorensen
        - Ditch all < 2.6 support
     Rev  3.25.1, February 10, 2005 Christoph Hellwig
@@ -435,7 +438,6 @@ static int qla1280_mailbox_command(struct scsi_qla_host *,
                                   uint8_t, uint16_t *);
 static int qla1280_bus_reset(struct scsi_qla_host *, int);
 static int qla1280_device_reset(struct scsi_qla_host *, int, int);
-static int qla1280_abort_device(struct scsi_qla_host *, int, int, int);
 static int qla1280_abort_command(struct scsi_qla_host *, struct srb *, int);
 static int qla1280_abort_isp(struct scsi_qla_host *);
 #ifdef QLA_64BIT_PTR
@@ -698,7 +700,7 @@ qla1280_info(struct Scsi_Host *host)
 }
 
 /**************************************************************************
- *   qla1200_queuecommand
+ *   qla1280_queuecommand
  *     Queue a command to the controller.
  *
  * Note:
@@ -713,12 +715,14 @@ qla1280_queuecommand(struct scsi_cmnd *cmd, void (*fn)(struct scsi_cmnd *))
 {
        struct Scsi_Host *host = cmd->device->host;
        struct scsi_qla_host *ha = (struct scsi_qla_host *)host->hostdata;
-       struct srb *sp = (struct srb *)&cmd->SCp;
+       struct srb *sp = (struct srb *)CMD_SP(cmd);
        int status;
 
        cmd->scsi_done = fn;
        sp->cmd = cmd;
        sp->flags = 0;
+       sp->wait = NULL;
+       CMD_HANDLE(cmd) = (unsigned char *)NULL;
 
        qla1280_print_scsi_cmd(5, cmd);
 
@@ -738,21 +742,11 @@ qla1280_queuecommand(struct scsi_cmnd *cmd, void (*fn)(struct scsi_cmnd *))
 
 enum action {
        ABORT_COMMAND,
-       ABORT_DEVICE,
        DEVICE_RESET,
        BUS_RESET,
        ADAPTER_RESET,
-       FAIL
 };
 
-/* timer action for error action processor */
-static void qla1280_error_wait_timeout(unsigned long __data)
-{
-       struct scsi_cmnd *cmd = (struct scsi_cmnd *)__data;
-       struct srb *sp = (struct srb *)CMD_SP(cmd);
-
-       complete(sp->wait);
-}
 
 static void qla1280_mailbox_timeout(unsigned long __data)
 {
@@ -767,8 +761,67 @@ static void qla1280_mailbox_timeout(unsigned long __data)
        complete(ha->mailbox_wait);
 }
 
+static int
+_qla1280_wait_for_single_command(struct scsi_qla_host *ha, struct srb *sp,
+                                struct completion *wait)
+{
+       int     status = FAILED;
+       struct scsi_cmnd *cmd = sp->cmd;
+
+       spin_unlock_irq(ha->host->host_lock);
+       wait_for_completion_timeout(wait, 4*HZ);
+       spin_lock_irq(ha->host->host_lock);
+       sp->wait = NULL;
+       if(CMD_HANDLE(cmd) == COMPLETED_HANDLE) {
+               status = SUCCESS;
+               (*cmd->scsi_done)(cmd);
+       }
+       return status;
+}
+
+static int
+qla1280_wait_for_single_command(struct scsi_qla_host *ha, struct srb *sp)
+{
+       DECLARE_COMPLETION_ONSTACK(wait);
+
+       sp->wait = &wait;
+       return _qla1280_wait_for_single_command(ha, sp, &wait);
+}
+
+static int
+qla1280_wait_for_pending_commands(struct scsi_qla_host *ha, int bus, int target)
+{
+       int             cnt;
+       int             status;
+       struct srb      *sp;
+       struct scsi_cmnd *cmd;
+
+       status = SUCCESS;
+
+       /*
+        * Wait for all commands with the designated bus/target
+        * to be completed by the firmware
+        */
+       for (cnt = 0; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+               sp = ha->outstanding_cmds[cnt];
+               if (sp) {
+                       cmd = sp->cmd;
+
+                       if (bus >= 0 && SCSI_BUS_32(cmd) != bus)
+                               continue;
+                       if (target >= 0 && SCSI_TCN_32(cmd) != target)
+                               continue;
+
+                       status = qla1280_wait_for_single_command(ha, sp);
+                       if (status == FAILED)
+                               break;
+               }
+       }
+       return status;
+}
+
 /**************************************************************************
- * qla1200_error_action
+ * qla1280_error_action
  *    The function will attempt to perform a specified error action and
  *    wait for the results (or time out).
  *
@@ -780,11 +833,6 @@ static void qla1280_mailbox_timeout(unsigned long __data)
  * Returns:
  *      SUCCESS or FAILED
  *
- * Note:
- *      Resetting the bus always succeeds - is has to, otherwise the
- *      kernel will panic! Try a surgical technique - sending a BUS
- *      DEVICE RESET message - on the offending target before pulling
- *      the SCSI bus reset line.
  **************************************************************************/
 static int
 qla1280_error_action(struct scsi_cmnd *cmd, enum action action)
@@ -792,13 +840,19 @@ qla1280_error_action(struct scsi_cmnd *cmd, enum action action)
        struct scsi_qla_host *ha;
        int bus, target, lun;
        struct srb *sp;
-       uint16_t data;
-       unsigned char *handle;
-       int result, i;
+       int i, found;
+       int result=FAILED;
+       int wait_for_bus=-1;
+       int wait_for_target = -1;
        DECLARE_COMPLETION_ONSTACK(wait);
-       struct timer_list timer;
+
+       ENTER("qla1280_error_action");
 
        ha = (struct scsi_qla_host *)(CMD_HOST(cmd)->hostdata);
+       sp = (struct srb *)CMD_SP(cmd);
+       bus = SCSI_BUS_32(cmd);
+       target = SCSI_TCN_32(cmd);
+       lun = SCSI_LUN_32(cmd);
 
        dprintk(4, "error_action %i, istatus 0x%04x\n", action,
                RD_REG_WORD(&ha->iobase->istatus));
@@ -807,99 +861,47 @@ qla1280_error_action(struct scsi_cmnd *cmd, enum action action)
                RD_REG_WORD(&ha->iobase->host_cmd),
                RD_REG_WORD(&ha->iobase->ictrl), jiffies);
 
-       ENTER("qla1280_error_action");
        if (qla1280_verbose)
                printk(KERN_INFO "scsi(%li): Resetting Cmnd=0x%p, "
                       "Handle=0x%p, action=0x%x\n",
                       ha->host_no, cmd, CMD_HANDLE(cmd), action);
 
-       if (cmd == NULL) {
-               printk(KERN_WARNING "(scsi?:?:?:?) Reset called with NULL "
-                      "si_Cmnd pointer, failing.\n");
-               LEAVE("qla1280_error_action");
-               return FAILED;
-       }
-
-       ha = (struct scsi_qla_host *)cmd->device->host->hostdata;
-       sp = (struct srb *)CMD_SP(cmd);
-       handle = CMD_HANDLE(cmd);
-
-       /* Check for pending interrupts. */
-       data = qla1280_debounce_register(&ha->iobase->istatus);
-       /*
-        * The io_request_lock is held when the reset handler is called, hence
-        * the interrupt handler cannot be running in parallel as it also
-        * grabs the lock. /Jes
-        */
-       if (data & RISC_INT)
-               qla1280_isr(ha, &ha->done_q);
-
        /*
-        * Determine the suggested action that the mid-level driver wants
-        * us to perform.
+        * Check to see if we have the command in the outstanding_cmds[]
+        * array.  If not then it must have completed before this error
+        * action was initiated.  If the error_action isn't ABORT_COMMAND
+        * then the driver must proceed with the requested action.
         */
-       if (handle == (unsigned char *)INVALID_HANDLE || handle == NULL) {
-               if(action == ABORT_COMMAND) {
-                       /* we never got this command */
-                       printk(KERN_INFO "qla1280: Aborting a NULL handle\n");
-                       return SUCCESS; /* no action - we don't have command */
+       found = -1;
+       for (i = 0; i < MAX_OUTSTANDING_COMMANDS; i++) {
+               if (sp == ha->outstanding_cmds[i]) {
+                       found = i;
+                       sp->wait = &wait; /* we'll wait for it to complete */
+                       break;
                }
-       } else {
-               sp->wait = &wait;
        }
 
-       bus = SCSI_BUS_32(cmd);
-       target = SCSI_TCN_32(cmd);
-       lun = SCSI_LUN_32(cmd);
+       if (found < 0) {        /* driver doesn't have command */
+               result = SUCCESS;
+               if (qla1280_verbose) {
+                       printk(KERN_INFO
+                              "scsi(%ld:%d:%d:%d): specified command has "
+                              "already completed.\n", ha->host_no, bus,
+                               target, lun);
+               }
+       }
 
-       /* Overloading result.  Here it means the success or fail of the
-        * *issue* of the action.  When we return from the routine, it must
-        * mean the actual success or fail of the action */
-       result = FAILED;
        switch (action) {
-       case FAIL:
-               break;
 
        case ABORT_COMMAND:
-               if ((sp->flags & SRB_ABORT_PENDING)) {
-                       printk(KERN_WARNING
-                              "scsi(): Command has a pending abort "
-                              "message - ABORT_PENDING.\n");
-                       /* This should technically be impossible since we
-                        * now wait for abort completion */
-                       break;
-               }
-
-               for (i = 0; i < MAX_OUTSTANDING_COMMANDS; i++) {
-                       if (sp == ha->outstanding_cmds[i]) {
-                               dprintk(1, "qla1280: RISC aborting command\n");
-                               if (qla1280_abort_command(ha, sp, i) == 0)
-                                       result = SUCCESS;
-                               else {
-                                       /*
-                                        * Since we don't know what might
-                                        * have happend to the command, it
-                                        * is unsafe to remove it from the
-                                        * device's queue at this point.
-                                        * Wait and let the escalation
-                                        * process take care of it.
-                                        */
-                                       printk(KERN_WARNING
-                                              "scsi(%li:%i:%i:%i): Unable"
-                                              " to abort command!\n",
-                                              ha->host_no, bus, target, lun);
-                               }
-                       }
-               }
-               break;
-
-       case ABORT_DEVICE:
-               if (qla1280_verbose)
-                       printk(KERN_INFO
-                              "scsi(%ld:%d:%d:%d): Queueing abort device "
-                              "command.\n", ha->host_no, bus, target, lun);
-               if (qla1280_abort_device(ha, bus, target, lun) == 0)
-                       result = SUCCESS;
+               dprintk(1, "qla1280: RISC aborting command\n");
+               /*
+                * The abort might fail due to race when the host_lock
+                * is released to issue the abort.  As such, we
+                * don't bother to check the return status.
+                */
+               if (found >= 0)
+                       qla1280_abort_command(ha, sp, found);
                break;
 
        case DEVICE_RESET:
@@ -907,16 +909,21 @@ qla1280_error_action(struct scsi_cmnd *cmd, enum action action)
                        printk(KERN_INFO
                               "scsi(%ld:%d:%d:%d): Queueing device reset "
                               "command.\n", ha->host_no, bus, target, lun);
-               if (qla1280_device_reset(ha, bus, target) == 0)
-                       result = SUCCESS;
+               if (qla1280_device_reset(ha, bus, target) == 0) {
+                       /* issued device reset, set wait conditions */
+                       wait_for_bus = bus;
+                       wait_for_target = target;
+               }
                break;
 
        case BUS_RESET:
                if (qla1280_verbose)
                        printk(KERN_INFO "qla1280(%ld:%d): Issued bus "
                               "reset.\n", ha->host_no, bus);
-               if (qla1280_bus_reset(ha, bus) == 0)
-                       result = SUCCESS;
+               if (qla1280_bus_reset(ha, bus) == 0) {
+                       /* issued bus reset, set wait conditions */
+                       wait_for_bus = bus;
+               }
                break;
 
        case ADAPTER_RESET:
@@ -929,55 +936,48 @@ qla1280_error_action(struct scsi_cmnd *cmd, enum action action)
                               "continue automatically\n", ha->host_no);
                }
                ha->flags.reset_active = 1;
-               /*
-                * We restarted all of the commands automatically, so the
-                * mid-level code can expect completions momentitarily.
-                */
-               if (qla1280_abort_isp(ha) == 0)
-                       result = SUCCESS;
+
+               if (qla1280_abort_isp(ha) != 0) {       /* it's dead */
+                       result = FAILED;
+               }
 
                ha->flags.reset_active = 0;
        }
 
-       if (!list_empty(&ha->done_q))
-               qla1280_done(ha);
-
-       /* If we didn't manage to issue the action, or we have no
-        * command to wait for, exit here */
-       if (result == FAILED || handle == NULL ||
-           handle == (unsigned char *)INVALID_HANDLE) {
-               /*
-                * Clear completion queue to avoid qla1280_done() trying
-                * to complete the command at a later stage after we
-                * have exited the current context
-                */
-               sp->wait = NULL;
-               goto leave;
-       }
+       /*
+        * At this point, the host_lock has been released and retaken
+        * by the issuance of the mailbox command.
+        * Wait for the command passed in by the mid-layer if it
+        * was found by the driver.  It might have been returned
+        * between eh recovery steps, hence the check of the "found"
+        * variable.
+        */
 
-       /* set up a timer just in case we're really jammed */
-       init_timer(&timer);
-       timer.expires = jiffies + 4*HZ;
-       timer.data = (unsigned long)cmd;
-       timer.function = qla1280_error_wait_timeout;
-       add_timer(&timer);
+       if (found >= 0)
+               result = _qla1280_wait_for_single_command(ha, sp, &wait);
 
-       /* wait for the action to complete (or the timer to expire) */
-       spin_unlock_irq(ha->host->host_lock);
-       wait_for_completion(&wait);
-       del_timer_sync(&timer);
-       spin_lock_irq(ha->host->host_lock);
-       sp->wait = NULL;
+       if (action == ABORT_COMMAND && result != SUCCESS) {
+               printk(KERN_WARNING
+                      "scsi(%li:%i:%i:%i): "
+                      "Unable to abort command!\n",
+                      ha->host_no, bus, target, lun);
+       }
 
-       /* the only action we might get a fail for is abort */
-       if (action == ABORT_COMMAND) {
-               if(sp->flags & SRB_ABORTED)
-                       result = SUCCESS;
-               else
-                       result = FAILED;
+       /*
+        * If the command passed in by the mid-layer has been
+        * returned by the board, then wait for any additional
+        * commands which are supposed to complete based upon
+        * the error action.
+        *
+        * All commands are unconditionally returned during a
+        * call to qla1280_abort_isp(), ADAPTER_RESET.  No need
+        * to wait for them.
+        */
+       if (result == SUCCESS && wait_for_bus >= 0) {
+               result = qla1280_wait_for_pending_commands(ha,
+                                       wait_for_bus, wait_for_target);
        }
 
- leave:
        dprintk(1, "RESET returning %d\n", result);
 
        LEAVE("qla1280_error_action");
@@ -1280,13 +1280,12 @@ qla1280_done(struct scsi_qla_host *ha)
                switch ((CMD_RESULT(cmd) >> 16)) {
                case DID_RESET:
                        /* Issue marker command. */
-                       qla1280_marker(ha, bus, target, 0, MK_SYNC_ID);
+                       if (!ha->flags.abort_isp_active)
+                               qla1280_marker(ha, bus, target, 0, MK_SYNC_ID);
                        break;
                case DID_ABORT:
                        sp->flags &= ~SRB_ABORT_PENDING;
                        sp->flags |= SRB_ABORTED;
-                       if (sp->flags & SRB_TIMEOUT)
-                               CMD_RESULT(sp->cmd) = DID_TIME_OUT << 16;
                        break;
                default:
                        break;
@@ -1296,12 +1295,11 @@ qla1280_done(struct scsi_qla_host *ha)
                scsi_dma_unmap(cmd);
 
                /* Call the mid-level driver interrupt handler */
-               CMD_HANDLE(sp->cmd) = (unsigned char *)INVALID_HANDLE;
                ha->actthreads--;
 
-               (*(cmd)->scsi_done)(cmd);
-
-               if(sp->wait != NULL)
+               if (sp->wait == NULL)
+                       (*(cmd)->scsi_done)(cmd);
+               else
                        complete(sp->wait);
        }
        LEAVE("qla1280_done");
@@ -2417,9 +2415,6 @@ static int
 qla1280_mailbox_command(struct scsi_qla_host *ha, uint8_t mr, uint16_t *mb)
 {
        struct device_reg __iomem *reg = ha->iobase;
-#if 0
-       LIST_HEAD(done_q);
-#endif
        int status = 0;
        int cnt;
        uint16_t *optr, *iptr;
@@ -2493,19 +2488,9 @@ qla1280_mailbox_command(struct scsi_qla_host *ha, uint8_t mr, uint16_t *mb)
        mr = MAILBOX_REGISTER_COUNT;
        memcpy(optr, iptr, MAILBOX_REGISTER_COUNT * sizeof(uint16_t));
 
-#if 0
-       /* Go check for any response interrupts pending. */
-       qla1280_isr(ha, &done_q);
-#endif
-
        if (ha->flags.reset_marker)
                qla1280_rst_aen(ha);
 
-#if 0
-       if (!list_empty(&done_q))
-               qla1280_done(ha, &done_q);
-#endif
-
        if (status)
                dprintk(2, "qla1280_mailbox_command: **** FAILED, mailbox0 = "
                        "0x%x ****\n", mb[0]);
@@ -2640,41 +2625,6 @@ qla1280_device_reset(struct scsi_qla_host *ha, int bus, int target)
        return status;
 }
 
-/*
- * qla1280_abort_device
- *      Issue an abort message to the device
- *
- * Input:
- *      ha     = adapter block pointer.
- *      bus    = SCSI BUS.
- *      target = SCSI ID.
- *      lun    = SCSI LUN.
- *
- * Returns:
- *      0 = success
- */
-static int
-qla1280_abort_device(struct scsi_qla_host *ha, int bus, int target, int lun)
-{
-       uint16_t mb[MAILBOX_REGISTER_COUNT];
-       int status;
-
-       ENTER("qla1280_abort_device");
-
-       mb[0] = MBC_ABORT_DEVICE;
-       mb[1] = (bus ? target | BIT_7 : target) << 8 | lun;
-       status = qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]);
-
-       /* Issue marker command. */
-       qla1280_marker(ha, bus, target, lun, MK_SYNC_ID_LUN);
-
-       if (status)
-               dprintk(2, "qla1280_abort_device: **** FAILED ****\n");
-
-       LEAVE("qla1280_abort_device");
-       return status;
-}
-
 /*
  * qla1280_abort_command
  *      Abort command aborts a specified IOCB.
@@ -2833,7 +2783,7 @@ qla1280_64bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp)
 
        /* If room for request in request ring. */
        if ((req_cnt + 2) >= ha->req_q_cnt) {
-               status = 1;
+               status = SCSI_MLQUEUE_HOST_BUSY;
                dprintk(2, "qla1280_start_scsi: in-ptr=0x%x  req_q_cnt="
                        "0x%xreq_cnt=0x%x", ha->req_ring_index, ha->req_q_cnt,
                        req_cnt);
@@ -2845,7 +2795,7 @@ qla1280_64bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp)
                     ha->outstanding_cmds[cnt] != NULL; cnt++);
 
        if (cnt >= MAX_OUTSTANDING_COMMANDS) {
-               status = 1;
+               status = SCSI_MLQUEUE_HOST_BUSY;
                dprintk(2, "qla1280_start_scsi: NO ROOM IN "
                        "OUTSTANDING ARRAY, req_q_cnt=0x%x", ha->req_q_cnt);
                goto out;
@@ -3108,7 +3058,7 @@ qla1280_32bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp)
                ha->req_q_cnt, seg_cnt);
        /* If room for request in request ring. */
        if ((req_cnt + 2) >= ha->req_q_cnt) {
-               status = 1;
+               status = SCSI_MLQUEUE_HOST_BUSY;
                dprintk(2, "qla1280_32bit_start_scsi: in-ptr=0x%x, "
                        "req_q_cnt=0x%x, req_cnt=0x%x", ha->req_ring_index,
                        ha->req_q_cnt, req_cnt);
@@ -3120,7 +3070,7 @@ qla1280_32bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp)
                     (ha->outstanding_cmds[cnt] != 0); cnt++) ;
 
        if (cnt >= MAX_OUTSTANDING_COMMANDS) {
-               status = 1;
+               status = SCSI_MLQUEUE_HOST_BUSY;
                dprintk(2, "qla1280_32bit_start_scsi: NO ROOM IN OUTSTANDING "
                        "ARRAY, req_q_cnt=0x%x\n", ha->req_q_cnt);
                goto out;
@@ -3487,6 +3437,7 @@ qla1280_isr(struct scsi_qla_host *ha, struct list_head *done_q)
 
                                        /* Save ISP completion status */
                                        CMD_RESULT(sp->cmd) = 0;
+                                       CMD_HANDLE(sp->cmd) = COMPLETED_HANDLE;
 
                                        /* Place block on done queue */
                                        list_add_tail(&sp->list, done_q);
@@ -3495,7 +3446,7 @@ qla1280_isr(struct scsi_qla_host *ha, struct list_head *done_q)
                                         * If we get here we have a real problem!
                                         */
                                        printk(KERN_WARNING
-                                              "qla1280: ISP invalid handle");
+                                              "qla1280: ISP invalid handle\n");
                                }
                        }
                        break;
@@ -3753,6 +3704,8 @@ qla1280_status_entry(struct scsi_qla_host *ha, struct response *pkt,
                }
        }
 
+       CMD_HANDLE(sp->cmd) = COMPLETED_HANDLE;
+
        /* Place command on done queue. */
        list_add_tail(&sp->list, done_q);
  out:
@@ -3808,6 +3761,8 @@ qla1280_error_entry(struct scsi_qla_host *ha, struct response *pkt,
                        CMD_RESULT(sp->cmd) = DID_ERROR << 16;
                }
 
+               CMD_HANDLE(sp->cmd) = COMPLETED_HANDLE;
+
                /* Place command on done queue. */
                list_add_tail(&sp->list, done_q);
        }
@@ -3858,19 +3813,16 @@ qla1280_abort_isp(struct scsi_qla_host *ha)
                struct scsi_cmnd *cmd;
                sp = ha->outstanding_cmds[cnt];
                if (sp) {
-
                        cmd = sp->cmd;
                        CMD_RESULT(cmd) = DID_RESET << 16;
-
-                       sp->cmd = NULL;
+                       CMD_HANDLE(cmd) = COMPLETED_HANDLE;
                        ha->outstanding_cmds[cnt] = NULL;
-
-                       (*cmd->scsi_done)(cmd);
-
-                       sp->flags = 0;
+                       list_add_tail(&sp->list, &ha->done_q);
                }
        }
 
+       qla1280_done(ha);
+
        status = qla1280_load_firmware(ha);
        if (status)
                goto out;
@@ -3955,13 +3907,6 @@ qla1280_check_for_dead_scsi_bus(struct scsi_qla_host *ha, unsigned int bus)
 
                if (scsi_control == SCSI_PHASE_INVALID) {
                        ha->bus_settings[bus].scsi_bus_dead = 1;
-#if 0
-                       CMD_RESULT(cp) = DID_NO_CONNECT << 16;
-                       CMD_HANDLE(cp) = INVALID_HANDLE;
-                       /* ha->actthreads--; */
-
-                       (*(cp)->scsi_done)(cp);
-#endif
                        return 1;       /* bus is dead */
                } else {
                        ha->bus_settings[bus].scsi_bus_dead = 0;
index d7c44b8d2b4f5c9ef9c92c5bc8bb4a3029afc095..834884b9eed5b579e62a622ce4725c80ad897a7b 100644 (file)
@@ -88,7 +88,8 @@
 
 /* Maximum outstanding commands in ISP queues */
 #define MAX_OUTSTANDING_COMMANDS       512
-#define INVALID_HANDLE                 (MAX_OUTSTANDING_COMMANDS + 2)
+#define COMPLETED_HANDLE               ((unsigned char *) \
+                                       (MAX_OUTSTANDING_COMMANDS + 2))
 
 /* ISP request and response entry counts (37-65535) */
 #define REQUEST_ENTRY_CNT              255 /* Number of request entries. */
index b09993a06576cfbf37ead3608b53d5256bf084f4..0f8796201504c25a9f736f38f4ec8094b2578640 100644 (file)
@@ -97,7 +97,7 @@ qla2x00_sysfs_read_nvram(struct kobject *kobj,
                return 0;
 
        if (IS_NOCACHE_VPD_TYPE(ha))
-               ha->isp_ops->read_optrom(vha, ha->vpd, ha->flt_region_nvram << 2,
+               ha->isp_ops->read_optrom(vha, ha->nvram, ha->flt_region_nvram << 2,
                    ha->nvram_size);
        return memory_read_from_buffer(buf, count, &off, ha->nvram,
                                        ha->nvram_size);
@@ -692,6 +692,109 @@ static struct bin_attribute sysfs_edc_status_attr = {
        .read = qla2x00_sysfs_read_edc_status,
 };
 
+static ssize_t
+qla2x00_sysfs_read_xgmac_stats(struct kobject *kobj,
+                      struct bin_attribute *bin_attr,
+                      char *buf, loff_t off, size_t count)
+{
+       struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
+           struct device, kobj)));
+       struct qla_hw_data *ha = vha->hw;
+       int rval;
+       uint16_t actual_size;
+
+       if (!capable(CAP_SYS_ADMIN) || off != 0 || count > XGMAC_DATA_SIZE)
+               return 0;
+
+       if (ha->xgmac_data)
+               goto do_read;
+
+       ha->xgmac_data = dma_alloc_coherent(&ha->pdev->dev, XGMAC_DATA_SIZE,
+           &ha->xgmac_data_dma, GFP_KERNEL);
+       if (!ha->xgmac_data) {
+               qla_printk(KERN_WARNING, ha,
+                   "Unable to allocate memory for XGMAC read-data.\n");
+               return 0;
+       }
+
+do_read:
+       actual_size = 0;
+       memset(ha->xgmac_data, 0, XGMAC_DATA_SIZE);
+
+       rval = qla2x00_get_xgmac_stats(vha, ha->xgmac_data_dma,
+           XGMAC_DATA_SIZE, &actual_size);
+       if (rval != QLA_SUCCESS) {
+               qla_printk(KERN_WARNING, ha,
+                   "Unable to read XGMAC data (%x).\n", rval);
+               count = 0;
+       }
+
+       count = actual_size > count ? count: actual_size;
+       memcpy(buf, ha->xgmac_data, count);
+
+       return count;
+}
+
+static struct bin_attribute sysfs_xgmac_stats_attr = {
+       .attr = {
+               .name = "xgmac_stats",
+               .mode = S_IRUSR,
+       },
+       .size = 0,
+       .read = qla2x00_sysfs_read_xgmac_stats,
+};
+
+static ssize_t
+qla2x00_sysfs_read_dcbx_tlv(struct kobject *kobj,
+                      struct bin_attribute *bin_attr,
+                      char *buf, loff_t off, size_t count)
+{
+       struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
+           struct device, kobj)));
+       struct qla_hw_data *ha = vha->hw;
+       int rval;
+       uint16_t actual_size;
+
+       if (!capable(CAP_SYS_ADMIN) || off != 0 || count > DCBX_TLV_DATA_SIZE)
+               return 0;
+
+       if (ha->dcbx_tlv)
+               goto do_read;
+
+       ha->dcbx_tlv = dma_alloc_coherent(&ha->pdev->dev, DCBX_TLV_DATA_SIZE,
+           &ha->dcbx_tlv_dma, GFP_KERNEL);
+       if (!ha->dcbx_tlv) {
+               qla_printk(KERN_WARNING, ha,
+                   "Unable to allocate memory for DCBX TLV read-data.\n");
+               return 0;
+       }
+
+do_read:
+       actual_size = 0;
+       memset(ha->dcbx_tlv, 0, DCBX_TLV_DATA_SIZE);
+
+       rval = qla2x00_get_dcbx_params(vha, ha->dcbx_tlv_dma,
+           DCBX_TLV_DATA_SIZE);
+       if (rval != QLA_SUCCESS) {
+               qla_printk(KERN_WARNING, ha,
+                   "Unable to read DCBX TLV data (%x).\n", rval);
+               count = 0;
+       }
+
+       memcpy(buf, ha->dcbx_tlv, count);
+
+       return count;
+}
+
+static struct bin_attribute sysfs_dcbx_tlv_attr = {
+       .attr = {
+               .name = "dcbx_tlv",
+               .mode = S_IRUSR,
+       },
+       .size = 0,
+       .read = qla2x00_sysfs_read_dcbx_tlv,
+};
+
 static struct sysfs_entry {
        char *name;
        struct bin_attribute *attr;
@@ -706,6 +809,8 @@ static struct sysfs_entry {
        { "reset", &sysfs_reset_attr, },
        { "edc", &sysfs_edc_attr, 2 },
        { "edc_status", &sysfs_edc_status_attr, 2 },
+       { "xgmac_stats", &sysfs_xgmac_stats_attr, 3 },
+       { "dcbx_tlv", &sysfs_dcbx_tlv_attr, 3 },
        { NULL },
 };
 
@@ -721,6 +826,8 @@ qla2x00_alloc_sysfs_attr(scsi_qla_host_t *vha)
                        continue;
                if (iter->is4GBp_only == 2 && !IS_QLA25XX(vha->hw))
                        continue;
+               if (iter->is4GBp_only == 3 && !IS_QLA81XX(vha->hw))
+                       continue;
 
                ret = sysfs_create_bin_file(&host->shost_gendev.kobj,
                    iter->attr);
@@ -743,6 +850,8 @@ qla2x00_free_sysfs_attr(scsi_qla_host_t *vha)
                        continue;
                if (iter->is4GBp_only == 2 && !IS_QLA25XX(ha))
                        continue;
+               if (iter->is4GBp_only == 3 && !IS_QLA81XX(ha))
+                       continue;
 
                sysfs_remove_bin_file(&host->shost_gendev.kobj,
                    iter->attr);
@@ -1088,6 +1197,58 @@ qla2x00_flash_block_size_show(struct device *dev,
        return snprintf(buf, PAGE_SIZE, "0x%x\n", ha->fdt_block_size);
 }
 
+static ssize_t
+qla2x00_vlan_id_show(struct device *dev, struct device_attribute *attr,
+    char *buf)
+{
+       scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+
+       if (!IS_QLA81XX(vha->hw))
+               return snprintf(buf, PAGE_SIZE, "\n");
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", vha->fcoe_vlan_id);
+}
+
+static ssize_t
+qla2x00_vn_port_mac_address_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+       scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+
+       if (!IS_QLA81XX(vha->hw))
+               return snprintf(buf, PAGE_SIZE, "\n");
+
+       return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+           vha->fcoe_vn_port_mac[5], vha->fcoe_vn_port_mac[4],
+           vha->fcoe_vn_port_mac[3], vha->fcoe_vn_port_mac[2],
+           vha->fcoe_vn_port_mac[1], vha->fcoe_vn_port_mac[0]);
+}
+
+static ssize_t
+qla2x00_fabric_param_show(struct device *dev, struct device_attribute *attr,
+    char *buf)
+{
+       scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", vha->hw->switch_cap);
+}
+
+static ssize_t
+qla2x00_fw_state_show(struct device *dev, struct device_attribute *attr,
+    char *buf)
+{
+       scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+       int rval;
+       uint16_t state[5];
+
+       rval = qla2x00_get_firmware_state(vha, state);
+       if (rval != QLA_SUCCESS)
+               memset(state, -1, sizeof(state));
+
+       return snprintf(buf, PAGE_SIZE, "0x%x 0x%x 0x%x 0x%x 0x%x\n", state[0],
+           state[1], state[2], state[3], state[4]);
+}
+
 static DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, NULL);
 static DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL);
 static DEVICE_ATTR(serial_num, S_IRUGO, qla2x00_serial_num_show, NULL);
@@ -1116,6 +1277,11 @@ static DEVICE_ATTR(mpi_version, S_IRUGO, qla2x00_mpi_version_show, NULL);
 static DEVICE_ATTR(phy_version, S_IRUGO, qla2x00_phy_version_show, NULL);
 static DEVICE_ATTR(flash_block_size, S_IRUGO, qla2x00_flash_block_size_show,
                   NULL);
+static DEVICE_ATTR(vlan_id, S_IRUGO, qla2x00_vlan_id_show, NULL);
+static DEVICE_ATTR(vn_port_mac_address, S_IRUGO,
+                  qla2x00_vn_port_mac_address_show, NULL);
+static DEVICE_ATTR(fabric_param, S_IRUGO, qla2x00_fabric_param_show, NULL);
+static DEVICE_ATTR(fw_state, S_IRUGO, qla2x00_fw_state_show, NULL);
 
 struct device_attribute *qla2x00_host_attrs[] = {
        &dev_attr_driver_version,
@@ -1138,6 +1304,10 @@ struct device_attribute *qla2x00_host_attrs[] = {
        &dev_attr_mpi_version,
        &dev_attr_phy_version,
        &dev_attr_flash_block_size,
+       &dev_attr_vlan_id,
+       &dev_attr_vn_port_mac_address,
+       &dev_attr_fabric_param,
+       &dev_attr_fw_state,
        NULL,
 };
 
@@ -1313,7 +1483,8 @@ qla2x00_terminate_rport_io(struct fc_rport *rport)
         * At this point all fcport's software-states are cleared.  Perform any
         * final cleanup of firmware resources (PCBs and XCBs).
         */
-       if (fcport->loop_id != FC_NO_LOOP_ID)
+       if (fcport->loop_id != FC_NO_LOOP_ID &&
+           !test_bit(UNLOADING, &fcport->vha->dpc_flags))
                fcport->vha->hw->isp_ops->fabric_logout(fcport->vha,
                        fcport->loop_id, fcport->d_id.b.domain,
                        fcport->d_id.b.area, fcport->d_id.b.al_pa);
@@ -1437,11 +1608,13 @@ static int
 qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
 {
        int     ret = 0;
-       int     cnt = 0;
-       uint8_t qos = QLA_DEFAULT_QUE_QOS;
+       uint8_t qos = 0;
        scsi_qla_host_t *base_vha = shost_priv(fc_vport->shost);
        scsi_qla_host_t *vha = NULL;
        struct qla_hw_data *ha = base_vha->hw;
+       uint16_t options = 0;
+       int     cnt;
+       struct req_que *req = ha->req_q_map[0];
 
        ret = qla24xx_vport_create_req_sanity_check(fc_vport);
        if (ret) {
@@ -1497,23 +1670,39 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
 
        qla24xx_vport_disable(fc_vport, disable);
 
-       /* Create a queue pair for the vport */
-       if (ha->mqenable) {
-               if (ha->npiv_info) {
-                       for (; cnt < ha->nvram_npiv_size; cnt++) {
-                               if (ha->npiv_info[cnt].port_name ==
-                                       vha->port_name &&
-                                       ha->npiv_info[cnt].node_name ==
-                                       vha->node_name) {
-                                       qos = ha->npiv_info[cnt].q_qos;
-                                       break;
-                               }
-                       }
+       if (ql2xmultique_tag) {
+               req = ha->req_q_map[1];
+               goto vport_queue;
+       } else if (ql2xmaxqueues == 1 || !ha->npiv_info)
+               goto vport_queue;
+       /* Create a request queue in QoS mode for the vport */
+       for (cnt = 0; cnt < ha->nvram_npiv_size; cnt++) {
+               if (memcmp(ha->npiv_info[cnt].port_name, vha->port_name, 8) == 0
+                       && memcmp(ha->npiv_info[cnt].node_name, vha->node_name,
+                                       8) == 0) {
+                       qos = ha->npiv_info[cnt].q_qos;
+                       break;
+               }
+       }
+       if (qos) {
+               ret = qla25xx_create_req_que(ha, options, vha->vp_idx, 0, 0,
+                       qos);
+               if (!ret)
+                       qla_printk(KERN_WARNING, ha,
+                       "Can't create request queue for vp_idx:%d\n",
+                       vha->vp_idx);
+               else {
+                       DEBUG2(qla_printk(KERN_INFO, ha,
+                       "Request Que:%d (QoS: %d) created for vp_idx:%d\n",
+                       ret, qos, vha->vp_idx));
+                       req = ha->req_q_map[ret];
                }
-               qla25xx_create_queues(vha, qos);
        }
 
+vport_queue:
+       vha->req = req;
        return 0;
+
 vport_create_failed_2:
        qla24xx_disable_vp(vha);
        qla24xx_deallocate_vp_id(vha);
@@ -1554,8 +1743,8 @@ qla24xx_vport_delete(struct fc_vport *fc_vport)
                    vha->host_no, vha->vp_idx, vha));
         }
 
-       if (ha->mqenable) {
-               if (qla25xx_delete_queues(vha, 0) != QLA_SUCCESS)
+       if (vha->req->id && !ql2xmultique_tag) {
+               if (qla25xx_delete_req_que(vha, vha->req) != QLA_SUCCESS)
                        qla_printk(KERN_WARNING, ha,
                                "Queue delete failed.\n");
        }
index 34760f8d4f1768f37a0687770d5f1a4d23e5d7da..4a990f4da4ea95254fda1e6c233e202e86ad6cea 100644 (file)
@@ -149,11 +149,9 @@ qla24xx_pause_risc(struct device_reg_24xx __iomem *reg)
        int rval = QLA_SUCCESS;
        uint32_t cnt;
 
-       if (RD_REG_DWORD(&reg->hccr) & HCCRX_RISC_PAUSE)
-               return rval;
-
        WRT_REG_DWORD(&reg->hccr, HCCRX_SET_RISC_PAUSE);
-       for (cnt = 30000; (RD_REG_DWORD(&reg->hccr) & HCCRX_RISC_PAUSE) == 0 &&
+       for (cnt = 30000;
+           ((RD_REG_DWORD(&reg->host_status) & HSRX_RISC_PAUSED) == 0) &&
            rval == QLA_SUCCESS; cnt--) {
                if (cnt)
                        udelay(100);
@@ -351,7 +349,7 @@ static inline void *
 qla25xx_copy_mq(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain)
 {
        uint32_t cnt, que_idx;
-       uint8_t req_cnt, rsp_cnt, que_cnt;
+       uint8_t que_cnt;
        struct qla2xxx_mq_chain *mq = ptr;
        struct device_reg_25xxmq __iomem *reg;
 
@@ -363,9 +361,8 @@ qla25xx_copy_mq(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain)
        mq->type = __constant_htonl(DUMP_CHAIN_MQ);
        mq->chain_size = __constant_htonl(sizeof(struct qla2xxx_mq_chain));
 
-       req_cnt = find_first_zero_bit(ha->req_qid_map, ha->max_queues);
-       rsp_cnt = find_first_zero_bit(ha->rsp_qid_map, ha->max_queues);
-       que_cnt = req_cnt > rsp_cnt ? req_cnt : rsp_cnt;
+       que_cnt = ha->max_req_queues > ha->max_rsp_queues ?
+               ha->max_req_queues : ha->max_rsp_queues;
        mq->count = htonl(que_cnt);
        for (cnt = 0; cnt < que_cnt; cnt++) {
                reg = (struct device_reg_25xxmq *) ((void *)
index 714ee67567e1c814080342665e4d19dde48513ee..00aa48d975a698edcb73b1024c3f302f29a66a6c 100644 (file)
@@ -93,6 +93,7 @@
 #define LSD(x) ((uint32_t)((uint64_t)(x)))
 #define MSD(x) ((uint32_t)((((uint64_t)(x)) >> 16) >> 16))
 
+#define MAKE_HANDLE(x, y) ((uint32_t)((((uint32_t)(x)) << 16) | (uint32_t)(y)))
 
 /*
  * I/O register
 #define REQUEST_ENTRY_CNT_24XX         2048    /* Number of request entries. */
 #define RESPONSE_ENTRY_CNT_2100                64      /* Number of response entries.*/
 #define RESPONSE_ENTRY_CNT_2300                512     /* Number of response entries.*/
+#define RESPONSE_ENTRY_CNT_MQ          128     /* Number of response entries.*/
 
 struct req_que;
 
@@ -186,7 +188,6 @@ struct req_que;
  * SCSI Request Block
  */
 typedef struct srb {
-       struct req_que *que;
        struct fc_port *fcport;
 
        struct scsi_cmnd *cmd;          /* Linux SCSI command pkt */
@@ -2008,7 +2009,7 @@ typedef struct vport_params {
 #define VP_RET_CODE_NOT_FOUND          6
 
 struct qla_hw_data;
-
+struct rsp_que;
 /*
  * ISP operations
  */
@@ -2030,10 +2031,9 @@ struct isp_operations {
        void (*enable_intrs) (struct qla_hw_data *);
        void (*disable_intrs) (struct qla_hw_data *);
 
-       int (*abort_command) (struct scsi_qla_host *, srb_t *,
-               struct req_que *);
-       int (*target_reset) (struct fc_port *, unsigned int);
-       int (*lun_reset) (struct fc_port *, unsigned int);
+       int (*abort_command) (srb_t *);
+       int (*target_reset) (struct fc_port *, unsigned int, int);
+       int (*lun_reset) (struct fc_port *, unsigned int, int);
        int (*fabric_login) (struct scsi_qla_host *, uint16_t, uint8_t,
                uint8_t, uint8_t, uint16_t *, uint8_t);
        int (*fabric_logout) (struct scsi_qla_host *, uint16_t, uint8_t,
@@ -2079,7 +2079,6 @@ struct isp_operations {
 #define QLA_PCI_MSIX_CONTROL   0xa2
 
 struct scsi_qla_host;
-struct rsp_que;
 
 struct qla_msix_entry {
        int have_irq;
@@ -2140,7 +2139,6 @@ struct qla_statistics {
 #define MBC_INITIALIZE_MULTIQ 0x1f
 #define QLA_QUE_PAGE 0X1000
 #define QLA_MQ_SIZE 32
-#define QLA_MAX_HOST_QUES 16
 #define QLA_MAX_QUEUES 256
 #define ISP_QUE_REG(ha, id) \
        ((ha->mqenable) ? \
@@ -2170,6 +2168,8 @@ struct rsp_que {
        struct qla_hw_data *hw;
        struct qla_msix_entry *msix;
        struct req_que *req;
+       srb_t *status_srb; /* status continuation entry */
+       struct work_struct q_work;
 };
 
 /* Request queue data structure */
@@ -2222,6 +2222,8 @@ struct qla_hw_data {
                uint32_t        fce_enabled             :1;
                uint32_t        fac_supported           :1;
                uint32_t        chip_reset_done         :1;
+               uint32_t        port0                   :1;
+               uint32_t        running_gold_fw         :1;
        } flags;
 
        /* This spinlock is used to protect "io transactions", you must
@@ -2246,7 +2248,8 @@ struct qla_hw_data {
        struct rsp_que **rsp_q_map;
        unsigned long req_qid_map[(QLA_MAX_QUEUES / 8) / sizeof(unsigned long)];
        unsigned long rsp_qid_map[(QLA_MAX_QUEUES / 8) / sizeof(unsigned long)];
-       uint16_t        max_queues;
+       uint8_t         max_req_queues;
+       uint8_t         max_rsp_queues;
        struct qla_npiv_entry *npiv_info;
        uint16_t        nvram_npiv_size;
 
@@ -2255,6 +2258,9 @@ struct qla_hw_data {
 #define FLOGI_MID_SUPPORT       BIT_10
 #define FLOGI_VSAN_SUPPORT      BIT_12
 #define FLOGI_SP_SUPPORT        BIT_13
+
+       uint8_t         port_no;                /* Physical port of adapter */
+
        /* Timeout timers. */
        uint8_t         loop_down_abort_time;    /* port down timer */
        atomic_t        loop_down_timer;         /* loop down timer */
@@ -2392,6 +2398,14 @@ struct qla_hw_data {
        dma_addr_t      edc_data_dma;
        uint16_t        edc_data_len;
 
+#define XGMAC_DATA_SIZE        PAGE_SIZE
+       void            *xgmac_data;
+       dma_addr_t      xgmac_data_dma;
+
+#define DCBX_TLV_DATA_SIZE PAGE_SIZE
+       void            *dcbx_tlv;
+       dma_addr_t      dcbx_tlv_dma;
+
        struct task_struct      *dpc_thread;
        uint8_t dpc_active;                  /* DPC routine is active */
 
@@ -2510,6 +2524,7 @@ struct qla_hw_data {
        uint32_t        flt_region_vpd;
        uint32_t        flt_region_nvram;
        uint32_t        flt_region_npiv_conf;
+       uint32_t        flt_region_gold_fw;
 
        /* Needed for BEACON */
        uint16_t        beacon_blink_led;
@@ -2536,6 +2551,7 @@ struct qla_hw_data {
        struct qla_chip_state_84xx *cs84xx;
        struct qla_statistics qla_stats;
        struct isp_operations *isp_ops;
+       struct workqueue_struct *wq;
 };
 
 /*
@@ -2545,6 +2561,8 @@ typedef struct scsi_qla_host {
        struct list_head list;
        struct list_head vp_fcports;    /* list of fcports */
        struct list_head work_list;
+       spinlock_t work_lock;
+
        /* Commonly used flags and state information. */
        struct Scsi_Host *host;
        unsigned long   host_no;
@@ -2591,8 +2609,6 @@ typedef struct scsi_qla_host {
 #define SWITCH_FOUND           BIT_0
 #define DFLG_NO_CABLE          BIT_1
 
-       srb_t           *status_srb;    /* Status continuation entry. */
-
        /* ISP configuration data. */
        uint16_t        loop_id;                /* Host adapter loop id */
 
@@ -2618,6 +2634,11 @@ typedef struct scsi_qla_host {
        uint8_t         node_name[WWN_SIZE];
        uint8_t         port_name[WWN_SIZE];
        uint8_t         fabric_node_name[WWN_SIZE];
+
+       uint16_t        fcoe_vlan_id;
+       uint16_t        fcoe_fcf_idx;
+       uint8_t         fcoe_vn_port_mac[6];
+
        uint32_t        vp_abort_cnt;
 
        struct fc_vport *fc_vport;      /* holds fc_vport * for each vport */
@@ -2643,7 +2664,7 @@ typedef struct scsi_qla_host {
 #define VP_ERR_FAB_LOGOUT      4
 #define VP_ERR_ADAP_NORESOURCES        5
        struct qla_hw_data *hw;
-       int     req_ques[QLA_MAX_HOST_QUES];
+       struct req_que *req;
 } scsi_qla_host_t;
 
 /*
index 96ccb9642ba09e27c17e6a86ab1b3071f1e40323..dfde2dd865cbfea7008c9f104fc9c69873e24e20 100644 (file)
@@ -878,7 +878,6 @@ struct device_reg_24xx {
                                        /* HCCR statuses. */
 #define HCCRX_HOST_INT         BIT_6   /* Host to RISC interrupt bit. */
 #define HCCRX_RISC_RESET       BIT_5   /* RISC Reset mode bit. */
-#define HCCRX_RISC_PAUSE       BIT_4   /* RISC Pause mode bit. */
                                        /* HCCR commands. */
                                        /* NOOP. */
 #define HCCRX_NOOP             0x00000000
@@ -1241,6 +1240,7 @@ struct qla_flt_header {
 #define FLT_REG_HW_EVENT_1     0x1f
 #define FLT_REG_NPIV_CONF_0    0x29
 #define FLT_REG_NPIV_CONF_1    0x2a
+#define FLT_REG_GOLD_FW                0x2f
 
 struct qla_flt_region {
        uint32_t code;
@@ -1405,6 +1405,8 @@ struct access_chip_rsp_84xx {
 #define MBC_IDC_ACK            0x101
 #define MBC_RESTART_MPI_FW     0x3d
 #define MBC_FLASH_ACCESS_CTRL  0x3e    /* Control flash access. */
+#define MBC_GET_XGMAC_STATS    0x7a
+#define MBC_GET_DCBX_PARAMS    0x51
 
 /* Flash access control option field bit definitions */
 #define FAC_OPT_FORCE_SEMAPHORE                BIT_15
@@ -1711,7 +1713,7 @@ struct ex_init_cb_81xx {
 #define FA_VPD0_ADDR_81                0xD0000
 #define FA_VPD1_ADDR_81                0xD0400
 #define FA_NVRAM0_ADDR_81      0xD0080
-#define FA_NVRAM1_ADDR_81      0xD0480
+#define FA_NVRAM1_ADDR_81      0xD0180
 #define FA_FEATURE_ADDR_81     0xD4000
 #define FA_FLASH_DESCR_ADDR_81 0xD8000
 #define FA_FLASH_LAYOUT_ADDR_81        0xD8400
index 528913f6bed99ffc010eb3ec6d680660403683c7..65b12d82867c9ff6a6b128785b0b0034d16e9dd3 100644 (file)
@@ -65,8 +65,11 @@ extern int ql2xfdmienable;
 extern int ql2xallocfwdump;
 extern int ql2xextended_error_logging;
 extern int ql2xqfullrampup;
+extern int ql2xqfulltracking;
 extern int ql2xiidmaenable;
 extern int ql2xmaxqueues;
+extern int ql2xmultique_tag;
+extern int ql2xfwloadbin;
 
 extern int qla2x00_loop_reset(scsi_qla_host_t *);
 extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int);
@@ -145,7 +148,7 @@ qla2x00_dump_ram(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t);
 extern int
 qla2x00_execute_fw(scsi_qla_host_t *, uint32_t);
 
-extern void
+extern int
 qla2x00_get_fw_version(scsi_qla_host_t *, uint16_t *, uint16_t *, uint16_t *,
     uint16_t *, uint32_t *, uint8_t *, uint32_t *, uint8_t *);
 
@@ -165,13 +168,13 @@ extern int
 qla2x00_issue_iocb(scsi_qla_host_t *, void *, dma_addr_t, size_t);
 
 extern int
-qla2x00_abort_command(scsi_qla_host_t *, srb_t *, struct req_que *);
+qla2x00_abort_command(srb_t *);
 
 extern int
-qla2x00_abort_target(struct fc_port *, unsigned int);
+qla2x00_abort_target(struct fc_port *, unsigned int, int);
 
 extern int
-qla2x00_lun_reset(struct fc_port *, unsigned int);
+qla2x00_lun_reset(struct fc_port *, unsigned int, int);
 
 extern int
 qla2x00_get_adapter_id(scsi_qla_host_t *, uint16_t *, uint8_t *, uint8_t *,
@@ -236,9 +239,11 @@ extern int
 qla24xx_get_isp_stats(scsi_qla_host_t *, struct link_statistics *,
     dma_addr_t);
 
-extern int qla24xx_abort_command(scsi_qla_host_t *, srb_t *, struct req_que *);
-extern int qla24xx_abort_target(struct fc_port *, unsigned int);
-extern int qla24xx_lun_reset(struct fc_port *, unsigned int);
+extern int qla24xx_abort_command(srb_t *);
+extern int
+qla24xx_abort_target(struct fc_port *, unsigned int, int);
+extern int
+qla24xx_lun_reset(struct fc_port *, unsigned int, int);
 
 extern int
 qla2x00_system_error(scsi_qla_host_t *);
@@ -288,6 +293,18 @@ qla81xx_fac_do_write_enable(scsi_qla_host_t *, int);
 extern int
 qla81xx_fac_erase_sector(scsi_qla_host_t *, uint32_t, uint32_t);
 
+extern int
+qla2x00_get_xgmac_stats(scsi_qla_host_t *, dma_addr_t, uint16_t, uint16_t *);
+
+extern int
+qla2x00_get_dcbx_params(scsi_qla_host_t *, dma_addr_t, uint16_t);
+
+extern int
+qla2x00_read_ram_word(scsi_qla_host_t *, uint32_t, uint32_t *);
+
+extern int
+qla2x00_write_ram_word(scsi_qla_host_t *, uint32_t, uint32_t);
+
 /*
  * Global Function Prototypes in qla_isr.c source file.
  */
@@ -295,8 +312,8 @@ extern irqreturn_t qla2100_intr_handler(int, void *);
 extern irqreturn_t qla2300_intr_handler(int, void *);
 extern irqreturn_t qla24xx_intr_handler(int, void *);
 extern void qla2x00_process_response_queue(struct rsp_que *);
-extern void qla24xx_process_response_queue(struct rsp_que *);
-
+extern void
+qla24xx_process_response_queue(struct scsi_qla_host *, struct rsp_que *);
 extern int qla2x00_request_irqs(struct qla_hw_data *, struct rsp_que *);
 extern void qla2x00_free_irqs(scsi_qla_host_t *);
 
@@ -401,19 +418,21 @@ extern int qla25xx_request_irq(struct rsp_que *);
 extern int qla25xx_init_req_que(struct scsi_qla_host *, struct req_que *);
 extern int qla25xx_init_rsp_que(struct scsi_qla_host *, struct rsp_que *);
 extern int qla25xx_create_req_que(struct qla_hw_data *, uint16_t, uint8_t,
-       uint16_t, uint8_t, uint8_t);
+       uint16_t, int, uint8_t);
 extern int qla25xx_create_rsp_que(struct qla_hw_data *, uint16_t, uint8_t,
-       uint16_t);
+       uint16_t, int);
 extern int qla25xx_update_req_que(struct scsi_qla_host *, uint8_t, uint8_t);
 extern void qla2x00_init_response_q_entries(struct rsp_que *);
 extern int qla25xx_delete_req_que(struct scsi_qla_host *, struct req_que *);
 extern int qla25xx_delete_rsp_que(struct scsi_qla_host *, struct rsp_que *);
 extern int qla25xx_create_queues(struct scsi_qla_host *, uint8_t);
-extern int qla25xx_delete_queues(struct scsi_qla_host *, uint8_t);
+extern int qla25xx_delete_queues(struct scsi_qla_host *);
 extern uint16_t qla24xx_rd_req_reg(struct qla_hw_data *, uint16_t);
 extern uint16_t qla25xx_rd_req_reg(struct qla_hw_data *, uint16_t);
 extern void qla24xx_wrt_req_reg(struct qla_hw_data *, uint16_t, uint16_t);
 extern void qla25xx_wrt_req_reg(struct qla_hw_data *, uint16_t, uint16_t);
 extern void qla25xx_wrt_rsp_reg(struct qla_hw_data *, uint16_t, uint16_t);
 extern void qla24xx_wrt_rsp_reg(struct qla_hw_data *, uint16_t, uint16_t);
+extern struct scsi_qla_host * qla25xx_get_host(struct rsp_que *);
+
 #endif /* _QLA_GBL_H */
index 557f58d5bf88074174c5b3c7213d6d7eed9c96d1..917534b9f2216c50784888d514d764e950c7a572 100644 (file)
@@ -1107,7 +1107,7 @@ qla2x00_mgmt_svr_login(scsi_qla_host_t *vha)
                return ret;
 
        ha->isp_ops->fabric_login(vha, vha->mgmt_svr_loop_id, 0xff, 0xff, 0xfa,
-           mb, BIT_1);
+           mb, BIT_1|BIT_0);
        if (mb[0] != MBS_COMMAND_COMPLETE) {
                DEBUG2_13(printk("%s(%ld): Failed MANAGEMENT_SERVER login: "
                    "loop_id=%x mb[0]=%x mb[1]=%x mb[2]=%x mb[6]=%x mb[7]=%x\n",
@@ -1879,6 +1879,9 @@ qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list)
                        case BIT_13:
                                list[i].fp_speed = PORT_SPEED_4GB;
                                break;
+                       case BIT_12:
+                               list[i].fp_speed = PORT_SPEED_10GB;
+                               break;
                        case BIT_11:
                                list[i].fp_speed = PORT_SPEED_8GB;
                                break;
index bd7dd84c06485b4947ef4f38b66a799ff7230305..26202612932534cbd2b0bebcec847d1cb7d3ce6c 100644 (file)
@@ -634,7 +634,7 @@ qla2x00_chip_diag(scsi_qla_host_t *vha)
                goto chip_diag_failed;
 
        DEBUG3(printk("scsi(%ld): Reset register cleared by chip reset\n",
-           ha->host_no));
+           vha->host_no));
 
        /* Reset RISC processor. */
        WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
@@ -655,7 +655,7 @@ qla2x00_chip_diag(scsi_qla_host_t *vha)
                goto chip_diag_failed;
 
        /* Check product ID of chip */
-       DEBUG3(printk("scsi(%ld): Checking product ID of chip\n", ha->host_no));
+       DEBUG3(printk("scsi(%ld): Checking product ID of chip\n", vha->host_no));
 
        mb[1] = RD_MAILBOX_REG(ha, reg, 1);
        mb[2] = RD_MAILBOX_REG(ha, reg, 2);
@@ -730,9 +730,6 @@ qla24xx_chip_diag(scsi_qla_host_t *vha)
        struct qla_hw_data *ha = vha->hw;
        struct req_que *req = ha->req_q_map[0];
 
-       /* Perform RISC reset. */
-       qla24xx_reset_risc(vha);
-
        ha->fw_transfer_size = REQUEST_ENTRY_SIZE * req->length;
 
        rval = qla2x00_mbx_reg_test(vha);
@@ -786,7 +783,6 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
                    sizeof(uint32_t);
                if (ha->mqenable)
                        mq_size = sizeof(struct qla2xxx_mq_chain);
-
                /* Allocate memory for Fibre Channel Event Buffer. */
                if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha))
                        goto try_eft;
@@ -850,8 +846,7 @@ cont_alloc:
        rsp_q_size = rsp->length * sizeof(response_t);
 
        dump_size = offsetof(struct qla2xxx_fw_dump, isp);
-       dump_size += fixed_size + mem_size + req_q_size + rsp_q_size +
-           eft_size;
+       dump_size += fixed_size + mem_size + req_q_size + rsp_q_size + eft_size;
        ha->chain_offset = dump_size;
        dump_size += mq_size + fce_size;
 
@@ -891,6 +886,56 @@ cont_alloc:
            htonl(offsetof(struct qla2xxx_fw_dump, isp));
 }
 
+static int
+qla81xx_mpi_sync(scsi_qla_host_t *vha)
+{
+#define MPS_MASK       0xe0
+       int rval;
+       uint16_t dc;
+       uint32_t dw;
+       struct qla_hw_data *ha = vha->hw;
+
+       if (!IS_QLA81XX(vha->hw))
+               return QLA_SUCCESS;
+
+       rval = qla2x00_write_ram_word(vha, 0x7c00, 1);
+       if (rval != QLA_SUCCESS) {
+               DEBUG2(qla_printk(KERN_WARNING, ha,
+                   "Sync-MPI: Unable to acquire semaphore.\n"));
+               goto done;
+       }
+
+       pci_read_config_word(vha->hw->pdev, 0x54, &dc);
+       rval = qla2x00_read_ram_word(vha, 0x7a15, &dw);
+       if (rval != QLA_SUCCESS) {
+               DEBUG2(qla_printk(KERN_WARNING, ha,
+                   "Sync-MPI: Unable to read sync.\n"));
+               goto done_release;
+       }
+
+       dc &= MPS_MASK;
+       if (dc == (dw & MPS_MASK))
+               goto done_release;
+
+       dw &= ~MPS_MASK;
+       dw |= dc;
+       rval = qla2x00_write_ram_word(vha, 0x7a15, dw);
+       if (rval != QLA_SUCCESS) {
+               DEBUG2(qla_printk(KERN_WARNING, ha,
+                   "Sync-MPI: Unable to gain sync.\n"));
+       }
+
+done_release:
+       rval = qla2x00_write_ram_word(vha, 0x7c00, 0);
+       if (rval != QLA_SUCCESS) {
+               DEBUG2(qla_printk(KERN_WARNING, ha,
+                   "Sync-MPI: Unable to release semaphore.\n"));
+       }
+
+done:
+       return rval;
+}
+
 /**
  * qla2x00_setup_chip() - Load and start RISC firmware.
  * @ha: HA context
@@ -915,6 +960,8 @@ qla2x00_setup_chip(scsi_qla_host_t *vha)
                spin_unlock_irqrestore(&ha->hardware_lock, flags);
        }
 
+       qla81xx_mpi_sync(vha);
+
        /* Load firmware sequences */
        rval = ha->isp_ops->load_risc(vha, &srisc_address);
        if (rval == QLA_SUCCESS) {
@@ -931,13 +978,16 @@ qla2x00_setup_chip(scsi_qla_host_t *vha)
                        /* Retrieve firmware information. */
                        if (rval == QLA_SUCCESS) {
                                fw_major_version = ha->fw_major_version;
-                               qla2x00_get_fw_version(vha,
+                               rval = qla2x00_get_fw_version(vha,
                                    &ha->fw_major_version,
                                    &ha->fw_minor_version,
                                    &ha->fw_subminor_version,
                                    &ha->fw_attributes, &ha->fw_memory_size,
                                    ha->mpi_version, &ha->mpi_capabilities,
                                    ha->phy_version);
+                               if (rval != QLA_SUCCESS)
+                                       goto failed;
+
                                ha->flags.npiv_supported = 0;
                                if (IS_QLA2XXX_MIDTYPE(ha) &&
                                         (ha->fw_attributes & BIT_2)) {
@@ -989,7 +1039,7 @@ qla2x00_setup_chip(scsi_qla_host_t *vha)
                            ha->fw_subminor_version);
                }
        }
-
+failed:
        if (rval) {
                DEBUG2_3(printk("scsi(%ld): Setup chip **** FAILED ****.\n",
                    vha->host_no));
@@ -1013,12 +1063,14 @@ qla2x00_init_response_q_entries(struct rsp_que *rsp)
        uint16_t cnt;
        response_t *pkt;
 
+       rsp->ring_ptr = rsp->ring;
+       rsp->ring_index    = 0;
+       rsp->status_srb = NULL;
        pkt = rsp->ring_ptr;
        for (cnt = 0; cnt < rsp->length; cnt++) {
                pkt->signature = RESPONSE_PROCESSED;
                pkt++;
        }
-
 }
 
 /**
@@ -1176,7 +1228,7 @@ qla24xx_config_rings(struct scsi_qla_host *vha)
                if (ha->flags.msix_enabled) {
                        msix = &ha->msix_entries[1];
                        DEBUG2_17(printk(KERN_INFO
-                       "Reistering vector 0x%x for base que\n", msix->entry));
+                       "Registering vector 0x%x for base que\n", msix->entry));
                        icb->msix = cpu_to_le16(msix->entry);
                }
                /* Use alternate PCI bus number */
@@ -1230,14 +1282,14 @@ qla2x00_init_rings(scsi_qla_host_t *vha)
        spin_lock_irqsave(&ha->hardware_lock, flags);
 
        /* Clear outstanding commands array. */
-       for (que = 0; que < ha->max_queues; que++) {
+       for (que = 0; que < ha->max_req_queues; que++) {
                req = ha->req_q_map[que];
                if (!req)
                        continue;
-               for (cnt = 0; cnt < MAX_OUTSTANDING_COMMANDS; cnt++)
+               for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++)
                        req->outstanding_cmds[cnt] = NULL;
 
-               req->current_outstanding_cmd = 0;
+               req->current_outstanding_cmd = 1;
 
                /* Initialize firmware. */
                req->ring_ptr  = req->ring;
@@ -1245,13 +1297,10 @@ qla2x00_init_rings(scsi_qla_host_t *vha)
                req->cnt      = req->length;
        }
 
-       for (que = 0; que < ha->max_queues; que++) {
+       for (que = 0; que < ha->max_rsp_queues; que++) {
                rsp = ha->rsp_q_map[que];
                if (!rsp)
                        continue;
-               rsp->ring_ptr = rsp->ring;
-               rsp->ring_index    = 0;
-
                /* Initialize response queue entries */
                qla2x00_init_response_q_entries(rsp);
        }
@@ -1307,7 +1356,7 @@ qla2x00_fw_ready(scsi_qla_host_t *vha)
        unsigned long   wtime, mtime, cs84xx_time;
        uint16_t        min_wait;       /* Minimum wait time if loop is down */
        uint16_t        wait_time;      /* Wait time if loop is coming ready */
-       uint16_t        state[3];
+       uint16_t        state[5];
        struct qla_hw_data *ha = vha->hw;
 
        rval = QLA_SUCCESS;
@@ -1406,8 +1455,9 @@ qla2x00_fw_ready(scsi_qla_host_t *vha)
                    vha->host_no, state[0], jiffies));
        } while (1);
 
-       DEBUG(printk("scsi(%ld): fw_state=%x curr time=%lx.\n",
-           vha->host_no, state[0], jiffies));
+       DEBUG(printk("scsi(%ld): fw_state=%x (%x, %x, %x, %x) curr time=%lx.\n",
+           vha->host_no, state[0], state[1], state[2], state[3], state[4],
+           jiffies));
 
        if (rval) {
                DEBUG2_3(printk("scsi(%ld): Firmware ready **** FAILED ****.\n",
@@ -1541,6 +1591,7 @@ qla2x00_set_model_info(scsi_qla_host_t *vha, uint8_t *model, size_t len,
        char *st, *en;
        uint16_t index;
        struct qla_hw_data *ha = vha->hw;
+       int use_tbl = !IS_QLA25XX(ha) && !IS_QLA81XX(ha);
 
        if (memcmp(model, BINZERO, len) != 0) {
                strncpy(ha->model_number, model, len);
@@ -1553,14 +1604,16 @@ qla2x00_set_model_info(scsi_qla_host_t *vha, uint8_t *model, size_t len,
                }
 
                index = (ha->pdev->subsystem_device & 0xff);
-               if (ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC &&
+               if (use_tbl &&
+                   ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC &&
                    index < QLA_MODEL_NAMES)
                        strncpy(ha->model_desc,
                            qla2x00_model_name[index * 2 + 1],
                            sizeof(ha->model_desc) - 1);
        } else {
                index = (ha->pdev->subsystem_device & 0xff);
-               if (ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC &&
+               if (use_tbl &&
+                   ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC &&
                    index < QLA_MODEL_NAMES) {
                        strcpy(ha->model_number,
                            qla2x00_model_name[index * 2]);
@@ -2061,8 +2114,10 @@ qla2x00_configure_loop(scsi_qla_host_t *vha)
        if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) {
                if (test_bit(LOCAL_LOOP_UPDATE, &save_flags))
                        set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
-               if (test_bit(RSCN_UPDATE, &save_flags))
+               if (test_bit(RSCN_UPDATE, &save_flags)) {
                        set_bit(RSCN_UPDATE, &vha->dpc_flags);
+                       vha->flags.rscn_queue_overflow = 1;
+               }
        }
 
        return (rval);
@@ -2110,7 +2165,7 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha)
                goto cleanup_allocation;
 
        DEBUG3(printk("scsi(%ld): Entries in ID list (%d)\n",
-           ha->host_no, entries));
+           vha->host_no, entries));
        DEBUG3(qla2x00_dump_buffer((uint8_t *)ha->gid_list,
            entries * sizeof(struct gid_list_info)));
 
@@ -2243,7 +2298,8 @@ static void
 qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
 {
 #define LS_UNKNOWN      2
-       static char *link_speeds[5] = { "1", "2", "?", "4", "8" };
+       static char *link_speeds[] = { "1", "2", "?", "4", "8", "10" };
+       char *link_speed;
        int rval;
        uint16_t mb[6];
        struct qla_hw_data *ha = vha->hw;
@@ -2266,10 +2322,15 @@ qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
                    fcport->port_name[6], fcport->port_name[7], rval,
                    fcport->fp_speed, mb[0], mb[1]));
        } else {
+               link_speed = link_speeds[LS_UNKNOWN];
+               if (fcport->fp_speed < 5)
+                       link_speed = link_speeds[fcport->fp_speed];
+               else if (fcport->fp_speed == 0x13)
+                       link_speed = link_speeds[5];
                DEBUG2(qla_printk(KERN_INFO, ha,
                    "iIDMA adjusted to %s GB/s on "
                    "%02x%02x%02x%02x%02x%02x%02x%02x.\n",
-                   link_speeds[fcport->fp_speed], fcport->port_name[0],
+                   link_speed, fcport->port_name[0],
                    fcport->port_name[1], fcport->port_name[2],
                    fcport->port_name[3], fcport->port_name[4],
                    fcport->port_name[5], fcport->port_name[6],
@@ -3180,9 +3241,14 @@ qla2x00_loop_resync(scsi_qla_host_t *vha)
 {
        int rval = QLA_SUCCESS;
        uint32_t wait_time;
-       struct qla_hw_data *ha = vha->hw;
-       struct req_que *req = ha->req_q_map[vha->req_ques[0]];
-       struct rsp_que *rsp = req->rsp;
+       struct req_que *req;
+       struct rsp_que *rsp;
+
+       if (ql2xmultique_tag)
+               req = vha->hw->req_q_map[0];
+       else
+               req = vha->req;
+       rsp = req->rsp;
 
        atomic_set(&vha->loop_state, LOOP_UPDATE);
        clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
@@ -3448,7 +3514,7 @@ qla25xx_init_queues(struct qla_hw_data *ha)
        int ret = -1;
        int i;
 
-       for (i = 1; i < ha->max_queues; i++) {
+       for (i = 1; i < ha->max_rsp_queues; i++) {
                rsp = ha->rsp_q_map[i];
                if (rsp) {
                        rsp->options &= ~BIT_0;
@@ -3462,6 +3528,8 @@ qla25xx_init_queues(struct qla_hw_data *ha)
                                        "%s Rsp que:%d inited\n", __func__,
                                                rsp->id));
                }
+       }
+       for (i = 1; i < ha->max_req_queues; i++) {
                req = ha->req_q_map[i];
                if (req) {
                /* Clear outstanding commands array. */
@@ -3566,14 +3634,15 @@ qla24xx_nvram_config(scsi_qla_host_t *vha)
        nv = ha->nvram;
 
        /* Determine NVRAM starting address. */
-       ha->nvram_size = sizeof(struct nvram_24xx);
-       ha->nvram_base = FA_NVRAM_FUNC0_ADDR;
-       ha->vpd_size = FA_NVRAM_VPD_SIZE;
-       ha->vpd_base = FA_NVRAM_VPD0_ADDR;
-       if (PCI_FUNC(ha->pdev->devfn)) {
+       if (ha->flags.port0) {
+               ha->nvram_base = FA_NVRAM_FUNC0_ADDR;
+               ha->vpd_base = FA_NVRAM_VPD0_ADDR;
+       } else {
                ha->nvram_base = FA_NVRAM_FUNC1_ADDR;
                ha->vpd_base = FA_NVRAM_VPD1_ADDR;
        }
+       ha->nvram_size = sizeof(struct nvram_24xx);
+       ha->vpd_size = FA_NVRAM_VPD_SIZE;
 
        /* Get VPD data into cache */
        ha->vpd = ha->nvram + VPD_OFFSET;
@@ -3587,7 +3656,7 @@ qla24xx_nvram_config(scsi_qla_host_t *vha)
        for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++)
                chksum += le32_to_cpu(*dptr++);
 
-       DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", ha->host_no));
+       DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", vha->host_no));
        DEBUG5(qla2x00_dump_buffer((uint8_t *)nv, ha->nvram_size));
 
        /* Bad NVRAM data, set defaults parameters. */
@@ -3612,7 +3681,7 @@ qla24xx_nvram_config(scsi_qla_host_t *vha)
                nv->exchange_count = __constant_cpu_to_le16(0);
                nv->hard_address = __constant_cpu_to_le16(124);
                nv->port_name[0] = 0x21;
-               nv->port_name[1] = 0x00 + PCI_FUNC(ha->pdev->devfn);
+               nv->port_name[1] = 0x00 + ha->port_no;
                nv->port_name[2] = 0x00;
                nv->port_name[3] = 0xe0;
                nv->port_name[4] = 0x8b;
@@ -3798,11 +3867,11 @@ qla24xx_nvram_config(scsi_qla_host_t *vha)
 }
 
 static int
-qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr)
+qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr,
+    uint32_t faddr)
 {
        int     rval = QLA_SUCCESS;
        int     segments, fragment;
-       uint32_t faddr;
        uint32_t *dcode, dlen;
        uint32_t risc_addr;
        uint32_t risc_size;
@@ -3811,12 +3880,11 @@ qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr)
        struct req_que *req = ha->req_q_map[0];
 
        qla_printk(KERN_INFO, ha,
-           "FW: Loading from flash (%x)...\n", ha->flt_region_fw);
+           "FW: Loading from flash (%x)...\n", faddr);
 
        rval = QLA_SUCCESS;
 
        segments = FA_RISC_CODE_SEGMENTS;
-       faddr = ha->flt_region_fw;
        dcode = (uint32_t *)req->ring;
        *srisc_addr = 0;
 
@@ -4104,6 +4172,9 @@ qla24xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
 {
        int rval;
 
+       if (ql2xfwloadbin == 1)
+               return qla81xx_load_risc(vha, srisc_addr);
+
        /*
         * FW Load priority:
         * 1) Firmware via request-firmware interface (.bin file).
@@ -4113,24 +4184,45 @@ qla24xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
        if (rval == QLA_SUCCESS)
                return rval;
 
-       return qla24xx_load_risc_flash(vha, srisc_addr);
+       return qla24xx_load_risc_flash(vha, srisc_addr,
+           vha->hw->flt_region_fw);
 }
 
 int
 qla81xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
 {
        int rval;
+       struct qla_hw_data *ha = vha->hw;
+
+       if (ql2xfwloadbin == 2)
+               goto try_blob_fw;
 
        /*
         * FW Load priority:
         * 1) Firmware residing in flash.
         * 2) Firmware via request-firmware interface (.bin file).
+        * 3) Golden-Firmware residing in flash -- limited operation.
         */
-       rval = qla24xx_load_risc_flash(vha, srisc_addr);
+       rval = qla24xx_load_risc_flash(vha, srisc_addr, ha->flt_region_fw);
        if (rval == QLA_SUCCESS)
                return rval;
 
-       return qla24xx_load_risc_blob(vha, srisc_addr);
+try_blob_fw:
+       rval = qla24xx_load_risc_blob(vha, srisc_addr);
+       if (rval == QLA_SUCCESS || !ha->flt_region_gold_fw)
+               return rval;
+
+       qla_printk(KERN_ERR, ha,
+           "FW: Attempting to fallback to golden firmware...\n");
+       rval = qla24xx_load_risc_flash(vha, srisc_addr, ha->flt_region_gold_fw);
+       if (rval != QLA_SUCCESS)
+               return rval;
+
+       qla_printk(KERN_ERR, ha,
+           "FW: Please update operational firmware...\n");
+       ha->flags.running_gold_fw = 1;
+
+       return rval;
 }
 
 void
@@ -4146,7 +4238,7 @@ qla2x00_try_to_stop_firmware(scsi_qla_host_t *vha)
 
        ret = qla2x00_stop_firmware(vha);
        for (retries = 5; ret != QLA_SUCCESS && ret != QLA_FUNCTION_TIMEOUT &&
-           retries ; retries--) {
+           ret != QLA_INVALID_COMMAND && retries ; retries--) {
                ha->isp_ops->reset_chip(vha);
                if (ha->isp_ops->chip_diag(vha) != QLA_SUCCESS)
                        continue;
@@ -4165,13 +4257,19 @@ qla24xx_configure_vhba(scsi_qla_host_t *vha)
        uint16_t mb[MAILBOX_REGISTER_COUNT];
        struct qla_hw_data *ha = vha->hw;
        struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
-       struct req_que *req = ha->req_q_map[vha->req_ques[0]];
-       struct rsp_que *rsp = req->rsp;
+       struct req_que *req;
+       struct rsp_que *rsp;
 
        if (!vha->vp_idx)
                return -EINVAL;
 
        rval = qla2x00_fw_ready(base_vha);
+       if (ql2xmultique_tag)
+               req = ha->req_q_map[0];
+       else
+               req = vha->req;
+       rsp = req->rsp;
+
        if (rval == QLA_SUCCESS) {
                clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
                qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL);
@@ -4305,7 +4403,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
        for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++)
                chksum += le32_to_cpu(*dptr++);
 
-       DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", ha->host_no));
+       DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", vha->host_no));
        DEBUG5(qla2x00_dump_buffer((uint8_t *)nv, ha->nvram_size));
 
        /* Bad NVRAM data, set defaults parameters. */
@@ -4329,7 +4427,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
                nv->execution_throttle = __constant_cpu_to_le16(0xFFFF);
                nv->exchange_count = __constant_cpu_to_le16(0);
                nv->port_name[0] = 0x21;
-               nv->port_name[1] = 0x00 + PCI_FUNC(ha->pdev->devfn);
+               nv->port_name[1] = 0x00 + ha->port_no;
                nv->port_name[2] = 0x00;
                nv->port_name[3] = 0xe0;
                nv->port_name[4] = 0x8b;
@@ -4358,12 +4456,12 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
                nv->max_luns_per_target = __constant_cpu_to_le16(128);
                nv->port_down_retry_count = __constant_cpu_to_le16(30);
                nv->link_down_timeout = __constant_cpu_to_le16(30);
-               nv->enode_mac[0] = 0x01;
+               nv->enode_mac[0] = 0x00;
                nv->enode_mac[1] = 0x02;
                nv->enode_mac[2] = 0x03;
                nv->enode_mac[3] = 0x04;
                nv->enode_mac[4] = 0x05;
-               nv->enode_mac[5] = 0x06 + PCI_FUNC(ha->pdev->devfn);
+               nv->enode_mac[5] = 0x06 + ha->port_no;
 
                rval = 1;
        }
@@ -4396,7 +4494,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
                icb->enode_mac[2] = 0x03;
                icb->enode_mac[3] = 0x04;
                icb->enode_mac[4] = 0x05;
-               icb->enode_mac[5] = 0x06 + PCI_FUNC(ha->pdev->devfn);
+               icb->enode_mac[5] = 0x06 + ha->port_no;
        }
 
        /* Use extended-initialization control block. */
index a8abbb95730df7d39eda9721471f04f4af1dd8db..13396beae2cedcde63b00c6156b44696168e6cc0 100644 (file)
@@ -15,6 +15,7 @@ static request_t *qla2x00_req_pkt(struct scsi_qla_host *, struct req_que *,
                                                        struct rsp_que *rsp);
 static void qla2x00_isp_cmd(struct scsi_qla_host *, struct req_que *);
 
+static void qla25xx_set_que(srb_t *, struct rsp_que **);
 /**
  * qla2x00_get_cmd_direction() - Determine control_flag data direction.
  * @cmd: SCSI command
@@ -92,9 +93,10 @@ qla2x00_calc_iocbs_64(uint16_t dsds)
  * Returns a pointer to the Continuation Type 0 IOCB packet.
  */
 static inline cont_entry_t *
-qla2x00_prep_cont_type0_iocb(struct req_que *req, struct scsi_qla_host *vha)
+qla2x00_prep_cont_type0_iocb(struct scsi_qla_host *vha)
 {
        cont_entry_t *cont_pkt;
+       struct req_que *req = vha->req;
        /* Adjust ring index. */
        req->ring_index++;
        if (req->ring_index == req->length) {
@@ -120,10 +122,11 @@ qla2x00_prep_cont_type0_iocb(struct req_que *req, struct scsi_qla_host *vha)
  * Returns a pointer to the continuation type 1 IOCB packet.
  */
 static inline cont_a64_entry_t *
-qla2x00_prep_cont_type1_iocb(struct req_que *req, scsi_qla_host_t *vha)
+qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha)
 {
        cont_a64_entry_t *cont_pkt;
 
+       struct req_que *req = vha->req;
        /* Adjust ring index. */
        req->ring_index++;
        if (req->ring_index == req->length) {
@@ -159,7 +162,6 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt,
        struct scsi_cmnd *cmd;
        struct scatterlist *sg;
        int i;
-       struct req_que *req;
 
        cmd = sp->cmd;
 
@@ -174,8 +176,6 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt,
        }
 
        vha = sp->fcport->vha;
-       req = sp->que;
-
        cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(sp));
 
        /* Three DSDs are available in the Command Type 2 IOCB */
@@ -192,7 +192,7 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt,
                         * Seven DSDs are available in the Continuation
                         * Type 0 IOCB.
                         */
-                       cont_pkt = qla2x00_prep_cont_type0_iocb(req, vha);
+                       cont_pkt = qla2x00_prep_cont_type0_iocb(vha);
                        cur_dsd = (uint32_t *)&cont_pkt->dseg_0_address;
                        avail_dsds = 7;
                }
@@ -220,7 +220,6 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
        struct scsi_cmnd *cmd;
        struct scatterlist *sg;
        int i;
-       struct req_que *req;
 
        cmd = sp->cmd;
 
@@ -235,8 +234,6 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
        }
 
        vha = sp->fcport->vha;
-       req = sp->que;
-
        cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(sp));
 
        /* Two DSDs are available in the Command Type 3 IOCB */
@@ -254,7 +251,7 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
                         * Five DSDs are available in the Continuation
                         * Type 1 IOCB.
                         */
-                       cont_pkt = qla2x00_prep_cont_type1_iocb(req, vha);
+                       cont_pkt = qla2x00_prep_cont_type1_iocb(vha);
                        cur_dsd = (uint32_t *)cont_pkt->dseg_0_address;
                        avail_dsds = 5;
                }
@@ -353,7 +350,6 @@ qla2x00_start_scsi(srb_t *sp)
        /* Build command packet */
        req->current_outstanding_cmd = handle;
        req->outstanding_cmds[handle] = sp;
-       sp->que = req;
        sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle;
        req->cnt -= req_cnt;
 
@@ -453,6 +449,7 @@ __qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req,
                        mrk24->lun[2] = MSB(lun);
                        host_to_fcp_swap(mrk24->lun, sizeof(mrk24->lun));
                        mrk24->vp_index = vha->vp_idx;
+                       mrk24->handle = MAKE_HANDLE(req->id, mrk24->handle);
                } else {
                        SET_TARGET_ID(ha, mrk->target, loop_id);
                        mrk->lun = cpu_to_le16(lun);
@@ -531,9 +528,6 @@ qla2x00_req_pkt(struct scsi_qla_host *vha, struct req_que *req,
                        for (cnt = 0; cnt < REQUEST_ENTRY_SIZE / 4; cnt++)
                                *dword_ptr++ = 0;
 
-                       /* Set system defined field. */
-                       pkt->sys_define = (uint8_t)req->ring_index;
-
                        /* Set entry count. */
                        pkt->entry_count = 1;
 
@@ -656,7 +650,7 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
        }
 
        vha = sp->fcport->vha;
-       req = sp->que;
+       req = vha->req;
 
        /* Set transfer direction */
        if (cmd->sc_data_direction == DMA_TO_DEVICE) {
@@ -687,7 +681,7 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
                         * Five DSDs are available in the Continuation
                         * Type 1 IOCB.
                         */
-                       cont_pkt = qla2x00_prep_cont_type1_iocb(req, vha);
+                       cont_pkt = qla2x00_prep_cont_type1_iocb(vha);
                        cur_dsd = (uint32_t *)cont_pkt->dseg_0_address;
                        avail_dsds = 5;
                }
@@ -724,19 +718,13 @@ qla24xx_start_scsi(srb_t *sp)
        struct scsi_cmnd *cmd = sp->cmd;
        struct scsi_qla_host *vha = sp->fcport->vha;
        struct qla_hw_data *ha = vha->hw;
-       uint16_t que_id;
 
        /* Setup device pointers. */
        ret = 0;
-       que_id = vha->req_ques[0];
 
-       req = ha->req_q_map[que_id];
-       sp->que = req;
+       qla25xx_set_que(sp, &rsp);
+       req = vha->req;
 
-       if (req->rsp)
-               rsp = req->rsp;
-       else
-               rsp = ha->rsp_q_map[que_id];
        /* So we know we haven't pci_map'ed anything yet */
        tot_dsds = 0;
 
@@ -794,7 +782,7 @@ qla24xx_start_scsi(srb_t *sp)
        req->cnt -= req_cnt;
 
        cmd_pkt = (struct cmd_type_7 *)req->ring_ptr;
-       cmd_pkt->handle = handle;
+       cmd_pkt->handle = MAKE_HANDLE(req->id, handle);
 
        /* Zero out remaining portion of packet. */
        /*    tagged queuing modifier -- default is TSK_SIMPLE (0). */
@@ -823,6 +811,8 @@ qla24xx_start_scsi(srb_t *sp)
 
        /* Set total data segment count. */
        cmd_pkt->entry_count = (uint8_t)req_cnt;
+       /* Specify response queue number where completion should happen */
+       cmd_pkt->entry_status = (uint8_t) rsp->id;
        wmb();
 
        /* Adjust ring index. */
@@ -842,7 +832,7 @@ qla24xx_start_scsi(srb_t *sp)
        /* Manage unprocessed RIO/ZIO commands in response queue. */
        if (vha->flags.process_response_queue &&
                rsp->ring_ptr->signature != RESPONSE_PROCESSED)
-               qla24xx_process_response_queue(rsp);
+               qla24xx_process_response_queue(vha, rsp);
 
        spin_unlock_irqrestore(&ha->hardware_lock, flags);
        return QLA_SUCCESS;
@@ -855,3 +845,16 @@ queuing_error:
 
        return QLA_FUNCTION_FAILED;
 }
+
+static void qla25xx_set_que(srb_t *sp, struct rsp_que **rsp)
+{
+       struct scsi_cmnd *cmd = sp->cmd;
+       struct qla_hw_data *ha = sp->fcport->vha->hw;
+       int affinity = cmd->request->cpu;
+
+       if (ql2xmultique_tag && affinity >= 0 &&
+               affinity < ha->max_rsp_queues - 1)
+               *rsp = ha->rsp_q_map[affinity + 1];
+        else
+               *rsp = ha->rsp_q_map[0];
+}
index d04981848e561aed9e9c7853619f78c532d84174..c8d0a176fea4a1287853716a7dd1d6f20c910060 100644 (file)
@@ -13,10 +13,9 @@ static void qla2x00_mbx_completion(scsi_qla_host_t *, uint16_t);
 static void qla2x00_process_completed_request(struct scsi_qla_host *,
        struct req_que *, uint32_t);
 static void qla2x00_status_entry(scsi_qla_host_t *, struct rsp_que *, void *);
-static void qla2x00_status_cont_entry(scsi_qla_host_t *, sts_cont_entry_t *);
+static void qla2x00_status_cont_entry(struct rsp_que *, sts_cont_entry_t *);
 static void qla2x00_error_entry(scsi_qla_host_t *, struct rsp_que *,
        sts_entry_t *);
-static struct scsi_qla_host *qla2x00_get_rsp_host(struct rsp_que *);
 
 /**
  * qla2100_intr_handler() - Process interrupts for the ISP2100 and ISP2200.
@@ -51,7 +50,7 @@ qla2100_intr_handler(int irq, void *dev_id)
        status = 0;
 
        spin_lock(&ha->hardware_lock);
-       vha = qla2x00_get_rsp_host(rsp);
+       vha = pci_get_drvdata(ha->pdev);
        for (iter = 50; iter--; ) {
                hccr = RD_REG_WORD(&reg->hccr);
                if (hccr & HCCR_RISC_PAUSE) {
@@ -147,7 +146,7 @@ qla2300_intr_handler(int irq, void *dev_id)
        status = 0;
 
        spin_lock(&ha->hardware_lock);
-       vha = qla2x00_get_rsp_host(rsp);
+       vha = pci_get_drvdata(ha->pdev);
        for (iter = 50; iter--; ) {
                stat = RD_REG_DWORD(&reg->u.isp2300.host_status);
                if (stat & HSR_RISC_PAUSED) {
@@ -685,7 +684,7 @@ skip_rio:
                    vha->host_no));
 
                if (IS_FWI2_CAPABLE(ha))
-                       qla24xx_process_response_queue(rsp);
+                       qla24xx_process_response_queue(vha, rsp);
                else
                        qla2x00_process_response_queue(rsp);
                break;
@@ -766,7 +765,10 @@ qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, void *data)
        struct qla_hw_data *ha = vha->hw;
        struct req_que *req = NULL;
 
-       req = ha->req_q_map[vha->req_ques[0]];
+       if (!ql2xqfulltracking)
+               return;
+
+       req = vha->req;
        if (!req)
                return;
        if (req->max_q_depth <= sdev->queue_depth)
@@ -808,6 +810,9 @@ qla2x00_ramp_up_queue_depth(scsi_qla_host_t *vha, struct req_que *req,
        fc_port_t *fcport;
        struct scsi_device *sdev;
 
+       if (!ql2xqfulltracking)
+               return;
+
        sdev = sp->cmd->device;
        if (sdev->queue_depth >= req->max_q_depth)
                return;
@@ -858,8 +863,8 @@ qla2x00_process_completed_request(struct scsi_qla_host *vha,
                qla2x00_ramp_up_queue_depth(vha, req, sp);
                qla2x00_sp_compl(ha, sp);
        } else {
-               DEBUG2(printk("scsi(%ld): Invalid ISP SCSI completion handle\n",
-                   vha->host_no));
+               DEBUG2(printk("scsi(%ld) Req:%d: Invalid ISP SCSI completion"
+                       " handle(%d)\n", vha->host_no, req->id, index));
                qla_printk(KERN_WARNING, ha,
                    "Invalid ISP SCSI completion handle\n");
 
@@ -881,7 +886,7 @@ qla2x00_process_response_queue(struct rsp_que *rsp)
        uint16_t        handle_cnt;
        uint16_t        cnt;
 
-       vha = qla2x00_get_rsp_host(rsp);
+       vha = pci_get_drvdata(ha->pdev);
 
        if (!vha->flags.online)
                return;
@@ -926,7 +931,7 @@ qla2x00_process_response_queue(struct rsp_que *rsp)
                        }
                        break;
                case STATUS_CONT_TYPE:
-                       qla2x00_status_cont_entry(vha, (sts_cont_entry_t *)pkt);
+                       qla2x00_status_cont_entry(rsp, (sts_cont_entry_t *)pkt);
                        break;
                default:
                        /* Type Not Supported. */
@@ -945,7 +950,8 @@ qla2x00_process_response_queue(struct rsp_que *rsp)
 }
 
 static inline void
-qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t sense_len)
+qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t sense_len,
+       struct rsp_que *rsp)
 {
        struct scsi_cmnd *cp = sp->cmd;
 
@@ -962,7 +968,7 @@ qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t sense_len)
        sp->request_sense_ptr += sense_len;
        sp->request_sense_length -= sense_len;
        if (sp->request_sense_length != 0)
-               sp->fcport->vha->status_srb = sp;
+               rsp->status_srb = sp;
 
        DEBUG5(printk("%s(): Check condition Sense data, scsi(%ld:%d:%d:%d) "
            "cmd=%p pid=%ld\n", __func__, sp->fcport->vha->host_no,
@@ -992,7 +998,9 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
        uint32_t        sense_len, rsp_info_len, resid_len, fw_resid_len;
        uint8_t         *rsp_info, *sense_data;
        struct qla_hw_data *ha = vha->hw;
-       struct req_que *req = rsp->req;
+       uint32_t handle;
+       uint16_t que;
+       struct req_que *req;
 
        sts = (sts_entry_t *) pkt;
        sts24 = (struct sts_entry_24xx *) pkt;
@@ -1003,18 +1011,20 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
                comp_status = le16_to_cpu(sts->comp_status);
                scsi_status = le16_to_cpu(sts->scsi_status) & SS_MASK;
        }
-
+       handle = (uint32_t) LSW(sts->handle);
+       que = MSW(sts->handle);
+       req = ha->req_q_map[que];
        /* Fast path completion. */
        if (comp_status == CS_COMPLETE && scsi_status == 0) {
-               qla2x00_process_completed_request(vha, req, sts->handle);
+               qla2x00_process_completed_request(vha, req, handle);
 
                return;
        }
 
        /* Validate handle. */
-       if (sts->handle < MAX_OUTSTANDING_COMMANDS) {
-               sp = req->outstanding_cmds[sts->handle];
-               req->outstanding_cmds[sts->handle] = NULL;
+       if (handle < MAX_OUTSTANDING_COMMANDS) {
+               sp = req->outstanding_cmds[handle];
+               req->outstanding_cmds[handle] = NULL;
        } else
                sp = NULL;
 
@@ -1030,7 +1040,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
        cp = sp->cmd;
        if (cp == NULL) {
                DEBUG2(printk("scsi(%ld): Command already returned back to OS "
-                   "pkt->handle=%d sp=%p.\n", vha->host_no, sts->handle, sp));
+                   "pkt->handle=%d sp=%p.\n", vha->host_no, handle, sp));
                qla_printk(KERN_WARNING, ha,
                    "Command is NULL: already returned to OS (sp=%p)\n", sp);
 
@@ -1121,6 +1131,8 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
                            scsi_status));
 
                        /* Adjust queue depth for all luns on the port. */
+                       if (!ql2xqfulltracking)
+                               break;
                        fcport->last_queue_full = jiffies;
                        starget_for_each_device(cp->device->sdev_target,
                            fcport, qla2x00_adjust_sdev_qdepth_down);
@@ -1133,7 +1145,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
                if (!(scsi_status & SS_SENSE_LEN_VALID))
                        break;
 
-               qla2x00_handle_sense(sp, sense_data, sense_len);
+               qla2x00_handle_sense(sp, sense_data, sense_len, rsp);
                break;
 
        case CS_DATA_UNDERRUN:
@@ -1179,6 +1191,8 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
                                 * Adjust queue depth for all luns on the
                                 * port.
                                 */
+                               if (!ql2xqfulltracking)
+                                       break;
                                fcport->last_queue_full = jiffies;
                                starget_for_each_device(
                                    cp->device->sdev_target, fcport,
@@ -1192,12 +1206,12 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
                        if (!(scsi_status & SS_SENSE_LEN_VALID))
                                break;
 
-                       qla2x00_handle_sense(sp, sense_data, sense_len);
+                       qla2x00_handle_sense(sp, sense_data, sense_len, rsp);
                } else {
                        /*
                         * If RISC reports underrun and target does not report
                         * it then we must have a lost frame, so tell upper
-                        * layer to retry it by reporting a bus busy.
+                        * layer to retry it by reporting an error.
                         */
                        if (!(scsi_status & SS_RESIDUAL_UNDER)) {
                                DEBUG2(printk("scsi(%ld:%d:%d:%d) Dropped "
@@ -1207,7 +1221,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
                                        cp->device->id, cp->device->lun, resid,
                                        scsi_bufflen(cp)));
 
-                               cp->result = DID_BUS_BUSY << 16;
+                               cp->result = DID_ERROR << 16;
                                break;
                        }
 
@@ -1334,7 +1348,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
        }
 
        /* Place command on done queue. */
-       if (vha->status_srb == NULL)
+       if (rsp->status_srb == NULL)
                qla2x00_sp_compl(ha, sp);
 }
 
@@ -1346,11 +1360,11 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
  * Extended sense data.
  */
 static void
-qla2x00_status_cont_entry(scsi_qla_host_t *vha, sts_cont_entry_t *pkt)
+qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt)
 {
        uint8_t         sense_sz = 0;
-       struct qla_hw_data *ha = vha->hw;
-       srb_t           *sp = vha->status_srb;
+       struct qla_hw_data *ha = rsp->hw;
+       srb_t           *sp = rsp->status_srb;
        struct scsi_cmnd *cp;
 
        if (sp != NULL && sp->request_sense_length != 0) {
@@ -1362,7 +1376,7 @@ qla2x00_status_cont_entry(scsi_qla_host_t *vha, sts_cont_entry_t *pkt)
                            "cmd is NULL: already returned to OS (sp=%p)\n",
                            sp);
 
-                       vha->status_srb = NULL;
+                       rsp->status_srb = NULL;
                        return;
                }
 
@@ -1383,7 +1397,7 @@ qla2x00_status_cont_entry(scsi_qla_host_t *vha, sts_cont_entry_t *pkt)
 
                /* Place command on done queue. */
                if (sp->request_sense_length == 0) {
-                       vha->status_srb = NULL;
+                       rsp->status_srb = NULL;
                        qla2x00_sp_compl(ha, sp);
                }
        }
@@ -1399,7 +1413,9 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt)
 {
        srb_t *sp;
        struct qla_hw_data *ha = vha->hw;
-       struct req_que *req = rsp->req;
+       uint32_t handle = LSW(pkt->handle);
+       uint16_t que = MSW(pkt->handle);
+       struct req_que *req = ha->req_q_map[que];
 #if defined(QL_DEBUG_LEVEL_2)
        if (pkt->entry_status & RF_INV_E_ORDER)
                qla_printk(KERN_ERR, ha, "%s: Invalid Entry Order\n", __func__);
@@ -1417,14 +1433,14 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt)
 #endif
 
        /* Validate handle. */
-       if (pkt->handle < MAX_OUTSTANDING_COMMANDS)
-               sp = req->outstanding_cmds[pkt->handle];
+       if (handle < MAX_OUTSTANDING_COMMANDS)
+               sp = req->outstanding_cmds[handle];
        else
                sp = NULL;
 
        if (sp) {
                /* Free outstanding command slot. */
-               req->outstanding_cmds[pkt->handle] = NULL;
+               req->outstanding_cmds[handle] = NULL;
 
                /* Bad payload or header */
                if (pkt->entry_status &
@@ -1486,13 +1502,10 @@ qla24xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0)
  * qla24xx_process_response_queue() - Process response queue entries.
  * @ha: SCSI driver HA context
  */
-void
-qla24xx_process_response_queue(struct rsp_que *rsp)
+void qla24xx_process_response_queue(struct scsi_qla_host *vha,
+       struct rsp_que *rsp)
 {
        struct sts_entry_24xx *pkt;
-       struct scsi_qla_host *vha;
-
-       vha = qla2x00_get_rsp_host(rsp);
 
        if (!vha->flags.online)
                return;
@@ -1523,7 +1536,7 @@ qla24xx_process_response_queue(struct rsp_que *rsp)
                        qla2x00_status_entry(vha, rsp, pkt);
                        break;
                case STATUS_CONT_TYPE:
-                       qla2x00_status_cont_entry(vha, (sts_cont_entry_t *)pkt);
+                       qla2x00_status_cont_entry(rsp, (sts_cont_entry_t *)pkt);
                        break;
                case VP_RPT_ID_IOCB_TYPE:
                        qla24xx_report_id_acquisition(vha,
@@ -1626,7 +1639,7 @@ qla24xx_intr_handler(int irq, void *dev_id)
        status = 0;
 
        spin_lock(&ha->hardware_lock);
-       vha = qla2x00_get_rsp_host(rsp);
+       vha = pci_get_drvdata(ha->pdev);
        for (iter = 50; iter--; ) {
                stat = RD_REG_DWORD(&reg->host_status);
                if (stat & HSRX_RISC_PAUSED) {
@@ -1664,7 +1677,7 @@ qla24xx_intr_handler(int irq, void *dev_id)
                        break;
                case 0x13:
                case 0x14:
-                       qla24xx_process_response_queue(rsp);
+                       qla24xx_process_response_queue(vha, rsp);
                        break;
                default:
                        DEBUG2(printk("scsi(%ld): Unrecognized interrupt type "
@@ -1692,6 +1705,7 @@ qla24xx_msix_rsp_q(int irq, void *dev_id)
        struct qla_hw_data *ha;
        struct rsp_que *rsp;
        struct device_reg_24xx __iomem *reg;
+       struct scsi_qla_host *vha;
 
        rsp = (struct rsp_que *) dev_id;
        if (!rsp) {
@@ -1704,7 +1718,8 @@ qla24xx_msix_rsp_q(int irq, void *dev_id)
 
        spin_lock_irq(&ha->hardware_lock);
 
-       qla24xx_process_response_queue(rsp);
+       vha = qla25xx_get_host(rsp);
+       qla24xx_process_response_queue(vha, rsp);
        WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
 
        spin_unlock_irq(&ha->hardware_lock);
@@ -1717,7 +1732,6 @@ qla25xx_msix_rsp_q(int irq, void *dev_id)
 {
        struct qla_hw_data *ha;
        struct rsp_que *rsp;
-       struct device_reg_24xx __iomem *reg;
 
        rsp = (struct rsp_que *) dev_id;
        if (!rsp) {
@@ -1726,13 +1740,8 @@ qla25xx_msix_rsp_q(int irq, void *dev_id)
                return IRQ_NONE;
        }
        ha = rsp->hw;
-       reg = &ha->iobase->isp24;
 
-       spin_lock_irq(&ha->hardware_lock);
-
-       qla24xx_process_response_queue(rsp);
-
-       spin_unlock_irq(&ha->hardware_lock);
+       queue_work_on((int) (rsp->id - 1), ha->wq, &rsp->q_work);
 
        return IRQ_HANDLED;
 }
@@ -1760,7 +1769,7 @@ qla24xx_msix_default(int irq, void *dev_id)
        status = 0;
 
        spin_lock_irq(&ha->hardware_lock);
-       vha = qla2x00_get_rsp_host(rsp);
+       vha = pci_get_drvdata(ha->pdev);
        do {
                stat = RD_REG_DWORD(&reg->host_status);
                if (stat & HSRX_RISC_PAUSED) {
@@ -1798,7 +1807,7 @@ qla24xx_msix_default(int irq, void *dev_id)
                        break;
                case 0x13:
                case 0x14:
-                       qla24xx_process_response_queue(rsp);
+                       qla24xx_process_response_queue(vha, rsp);
                        break;
                default:
                        DEBUG2(printk("scsi(%ld): Unrecognized interrupt type "
@@ -1822,31 +1831,14 @@ qla24xx_msix_default(int irq, void *dev_id)
 /* Interrupt handling helpers. */
 
 struct qla_init_msix_entry {
-       uint16_t entry;
-       uint16_t index;
        const char *name;
        irq_handler_t handler;
 };
 
-static struct qla_init_msix_entry base_queue = {
-       .entry = 0,
-       .index = 0,
-       .name = "qla2xxx (default)",
-       .handler = qla24xx_msix_default,
-};
-
-static struct qla_init_msix_entry base_rsp_queue = {
-       .entry = 1,
-       .index = 1,
-       .name = "qla2xxx (rsp_q)",
-       .handler = qla24xx_msix_rsp_q,
-};
-
-static struct qla_init_msix_entry multi_rsp_queue = {
-       .entry = 1,
-       .index = 1,
-       .name = "qla2xxx (multi_q)",
-       .handler = qla25xx_msix_rsp_q,
+static struct qla_init_msix_entry msix_entries[3] = {
+       { "qla2xxx (default)", qla24xx_msix_default },
+       { "qla2xxx (rsp_q)", qla24xx_msix_rsp_q },
+       { "qla2xxx (multiq)", qla25xx_msix_rsp_q },
 };
 
 static void
@@ -1873,7 +1865,6 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp)
        int i, ret;
        struct msix_entry *entries;
        struct qla_msix_entry *qentry;
-       struct qla_init_msix_entry *msix_queue;
 
        entries = kzalloc(sizeof(struct msix_entry) * ha->msix_count,
                                        GFP_KERNEL);
@@ -1900,7 +1891,7 @@ msix_failed:
                                ha->msix_count, ret);
                        goto msix_out;
                }
-               ha->max_queues = ha->msix_count - 1;
+               ha->max_rsp_queues = ha->msix_count - 1;
        }
        ha->msix_entries = kzalloc(sizeof(struct qla_msix_entry) *
                                ha->msix_count, GFP_KERNEL);
@@ -1918,45 +1909,27 @@ msix_failed:
                qentry->rsp = NULL;
        }
 
-       /* Enable MSI-X for AENs for queue 0 */
-       qentry = &ha->msix_entries[0];
-       ret = request_irq(qentry->vector, base_queue.handler, 0,
-                                       base_queue.name, rsp);
-       if (ret) {
-               qla_printk(KERN_WARNING, ha,
+       /* Enable MSI-X vectors for the base queue */
+       for (i = 0; i < 2; i++) {
+               qentry = &ha->msix_entries[i];
+               ret = request_irq(qentry->vector, msix_entries[i].handler,
+                                       0, msix_entries[i].name, rsp);
+               if (ret) {
+                       qla_printk(KERN_WARNING, ha,
                        "MSI-X: Unable to register handler -- %x/%d.\n",
                        qentry->vector, ret);
-               qla24xx_disable_msix(ha);
-               goto msix_out;
+                       qla24xx_disable_msix(ha);
+                       ha->mqenable = 0;
+                       goto msix_out;
+               }
+               qentry->have_irq = 1;
+               qentry->rsp = rsp;
+               rsp->msix = qentry;
        }
-       qentry->have_irq = 1;
-       qentry->rsp = rsp;
 
        /* Enable MSI-X vector for response queue update for queue 0 */
-       if (ha->max_queues > 1 && ha->mqiobase) {
+       if (ha->mqiobase &&  (ha->max_rsp_queues > 1 || ha->max_req_queues > 1))
                ha->mqenable = 1;
-               msix_queue = &multi_rsp_queue;
-               qla_printk(KERN_INFO, ha,
-                               "MQ enabled, Number of Queue Resources: %d \n",
-                               ha->max_queues);
-       } else {
-               ha->mqenable = 0;
-               msix_queue = &base_rsp_queue;
-       }
-
-       qentry = &ha->msix_entries[1];
-       ret = request_irq(qentry->vector, msix_queue->handler, 0,
-                                               msix_queue->name, rsp);
-       if (ret) {
-               qla_printk(KERN_WARNING, ha,
-                       "MSI-X: Unable to register handler -- %x/%d.\n",
-                       qentry->vector, ret);
-               qla24xx_disable_msix(ha);
-               ha->mqenable = 0;
-               goto msix_out;
-       }
-       qentry->have_irq = 1;
-       qentry->rsp = rsp;
 
 msix_out:
        kfree(entries);
@@ -2063,35 +2036,11 @@ qla2x00_free_irqs(scsi_qla_host_t *vha)
        }
 }
 
-static struct scsi_qla_host *
-qla2x00_get_rsp_host(struct rsp_que *rsp)
-{
-       srb_t *sp;
-       struct qla_hw_data *ha = rsp->hw;
-       struct scsi_qla_host *vha = NULL;
-       struct sts_entry_24xx *pkt;
-       struct req_que *req;
-
-       if (rsp->id) {
-               pkt = (struct sts_entry_24xx *) rsp->ring_ptr;
-               req = rsp->req;
-               if (pkt && pkt->handle < MAX_OUTSTANDING_COMMANDS) {
-                       sp = req->outstanding_cmds[pkt->handle];
-                       if (sp)
-                               vha = sp->fcport->vha;
-               }
-       }
-       if (!vha)
-       /* handle it in base queue */
-               vha = pci_get_drvdata(ha->pdev);
-
-       return vha;
-}
 
 int qla25xx_request_irq(struct rsp_que *rsp)
 {
        struct qla_hw_data *ha = rsp->hw;
-       struct qla_init_msix_entry *intr = &multi_rsp_queue;
+       struct qla_init_msix_entry *intr = &msix_entries[2];
        struct qla_msix_entry *msix = rsp->msix;
        int ret;
 
@@ -2106,3 +2055,30 @@ int qla25xx_request_irq(struct rsp_que *rsp)
        msix->rsp = rsp;
        return ret;
 }
+
+struct scsi_qla_host *
+qla25xx_get_host(struct rsp_que *rsp)
+{
+       srb_t *sp;
+       struct qla_hw_data *ha = rsp->hw;
+       struct scsi_qla_host *vha = NULL;
+       struct sts_entry_24xx *pkt;
+       struct req_que *req;
+       uint16_t que;
+       uint32_t handle;
+
+       pkt = (struct sts_entry_24xx *) rsp->ring_ptr;
+       que = MSW(pkt->handle);
+       handle = (uint32_t) LSW(pkt->handle);
+       req = ha->req_q_map[que];
+       if (handle < MAX_OUTSTANDING_COMMANDS) {
+               sp = req->outstanding_cmds[handle];
+               if (sp)
+                       return  sp->fcport->vha;
+               else
+                       goto base_que;
+       }
+base_que:
+       vha = pci_get_drvdata(ha->pdev);
+       return vha;
+}
index e67c1660bf4671aacf54c43e35dca5182b81fc33..451ece0760b0f6ea49bb7884f5f95b2ac5528783 100644 (file)
@@ -408,7 +408,7 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
  * Context:
  *     Kernel context.
  */
-void
+int
 qla2x00_get_fw_version(scsi_qla_host_t *vha, uint16_t *major, uint16_t *minor,
     uint16_t *subminor, uint16_t *attributes, uint32_t *memory, uint8_t *mpi,
     uint32_t *mpi_caps, uint8_t *phy)
@@ -427,6 +427,8 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha, uint16_t *major, uint16_t *minor,
        mcp->flags = 0;
        mcp->tov = MBX_TOV_SECONDS;
        rval = qla2x00_mailbox_command(vha, mcp);
+       if (rval != QLA_SUCCESS)
+               goto failed;
 
        /* Return mailbox data. */
        *major = mcp->mb[1];
@@ -446,7 +448,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha, uint16_t *major, uint16_t *minor,
                phy[1] = mcp->mb[9] >> 8;
                phy[2] = mcp->mb[9] & 0xff;
        }
-
+failed:
        if (rval != QLA_SUCCESS) {
                /*EMPTY*/
                DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
@@ -455,6 +457,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha, uint16_t *major, uint16_t *minor,
                /*EMPTY*/
                DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
        }
+       return rval;
 }
 
 /*
@@ -748,20 +751,20 @@ qla2x00_issue_iocb(scsi_qla_host_t *vha, void *buffer, dma_addr_t phys_addr,
  *     Kernel context.
  */
 int
-qla2x00_abort_command(scsi_qla_host_t *vha, srb_t *sp, struct req_que *req)
+qla2x00_abort_command(srb_t *sp)
 {
        unsigned long   flags = 0;
-       fc_port_t       *fcport;
        int             rval;
        uint32_t        handle = 0;
        mbx_cmd_t       mc;
        mbx_cmd_t       *mcp = &mc;
+       fc_port_t       *fcport = sp->fcport;
+       scsi_qla_host_t *vha = fcport->vha;
        struct qla_hw_data *ha = vha->hw;
+       struct req_que *req = vha->req;
 
        DEBUG11(printk("qla2x00_abort_command(%ld): entered.\n", vha->host_no));
 
-       fcport = sp->fcport;
-
        spin_lock_irqsave(&ha->hardware_lock, flags);
        for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) {
                if (req->outstanding_cmds[handle] == sp)
@@ -800,7 +803,7 @@ qla2x00_abort_command(scsi_qla_host_t *vha, srb_t *sp, struct req_que *req)
 }
 
 int
-qla2x00_abort_target(struct fc_port *fcport, unsigned int l)
+qla2x00_abort_target(struct fc_port *fcport, unsigned int l, int tag)
 {
        int rval, rval2;
        mbx_cmd_t  mc;
@@ -813,8 +816,8 @@ qla2x00_abort_target(struct fc_port *fcport, unsigned int l)
 
        l = l;
        vha = fcport->vha;
-       req = vha->hw->req_q_map[0];
-       rsp = vha->hw->rsp_q_map[0];
+       req = vha->hw->req_q_map[tag];
+       rsp = vha->hw->rsp_q_map[tag];
        mcp->mb[0] = MBC_ABORT_TARGET;
        mcp->out_mb = MBX_9|MBX_2|MBX_1|MBX_0;
        if (HAS_EXTENDED_IDS(vha->hw)) {
@@ -850,7 +853,7 @@ qla2x00_abort_target(struct fc_port *fcport, unsigned int l)
 }
 
 int
-qla2x00_lun_reset(struct fc_port *fcport, unsigned int l)
+qla2x00_lun_reset(struct fc_port *fcport, unsigned int l, int tag)
 {
        int rval, rval2;
        mbx_cmd_t  mc;
@@ -862,8 +865,8 @@ qla2x00_lun_reset(struct fc_port *fcport, unsigned int l)
        DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->vha->host_no));
 
        vha = fcport->vha;
-       req = vha->hw->req_q_map[0];
-       rsp = vha->hw->rsp_q_map[0];
+       req = vha->hw->req_q_map[tag];
+       rsp = vha->hw->rsp_q_map[tag];
        mcp->mb[0] = MBC_LUN_RESET;
        mcp->out_mb = MBX_9|MBX_3|MBX_2|MBX_1|MBX_0;
        if (HAS_EXTENDED_IDS(vha->hw))
@@ -931,6 +934,8 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
        mcp->mb[9] = vha->vp_idx;
        mcp->out_mb = MBX_9|MBX_0;
        mcp->in_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+       if (IS_QLA81XX(vha->hw))
+               mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10;
        mcp->tov = MBX_TOV_SECONDS;
        mcp->flags = 0;
        rval = qla2x00_mailbox_command(vha, mcp);
@@ -952,9 +957,19 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
                DEBUG2_3_11(printk("qla2x00_get_adapter_id(%ld): failed=%x.\n",
                    vha->host_no, rval));
        } else {
-               /*EMPTY*/
                DEBUG11(printk("qla2x00_get_adapter_id(%ld): done.\n",
                    vha->host_no));
+
+               if (IS_QLA81XX(vha->hw)) {
+                       vha->fcoe_vlan_id = mcp->mb[9] & 0xfff;
+                       vha->fcoe_fcf_idx = mcp->mb[10];
+                       vha->fcoe_vn_port_mac[5] = mcp->mb[11] >> 8;
+                       vha->fcoe_vn_port_mac[4] = mcp->mb[11] & 0xff;
+                       vha->fcoe_vn_port_mac[3] = mcp->mb[12] >> 8;
+                       vha->fcoe_vn_port_mac[2] = mcp->mb[12] & 0xff;
+                       vha->fcoe_vn_port_mac[1] = mcp->mb[13] >> 8;
+                       vha->fcoe_vn_port_mac[0] = mcp->mb[13] & 0xff;
+               }
        }
 
        return rval;
@@ -1252,7 +1267,7 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
 
        mcp->mb[0] = MBC_GET_FIRMWARE_STATE;
        mcp->out_mb = MBX_0;
-       mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+       mcp->in_mb = MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
        mcp->tov = MBX_TOV_SECONDS;
        mcp->flags = 0;
        rval = qla2x00_mailbox_command(vha, mcp);
@@ -1261,6 +1276,8 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
        states[0] = mcp->mb[1];
        states[1] = mcp->mb[2];
        states[2] = mcp->mb[3];
+       states[3] = mcp->mb[4];
+       states[4] = mcp->mb[5];
 
        if (rval != QLA_SUCCESS) {
                /*EMPTY*/
@@ -1480,9 +1497,17 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
        dma_addr_t      lg_dma;
        uint32_t        iop[2];
        struct qla_hw_data *ha = vha->hw;
+       struct req_que *req;
+       struct rsp_que *rsp;
 
        DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
 
+       if (ql2xmultique_tag)
+               req = ha->req_q_map[0];
+       else
+               req = vha->req;
+       rsp = req->rsp;
+
        lg = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
        if (lg == NULL) {
                DEBUG2_3(printk("%s(%ld): failed to allocate Login IOCB.\n",
@@ -1493,6 +1518,7 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
 
        lg->entry_type = LOGINOUT_PORT_IOCB_TYPE;
        lg->entry_count = 1;
+       lg->handle = MAKE_HANDLE(req->id, lg->handle);
        lg->nport_handle = cpu_to_le16(loop_id);
        lg->control_flags = __constant_cpu_to_le16(LCF_COMMAND_PLOGI);
        if (opt & BIT_0)
@@ -1741,6 +1767,8 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
        struct logio_entry_24xx *lg;
        dma_addr_t      lg_dma;
        struct qla_hw_data *ha = vha->hw;
+       struct req_que *req;
+       struct rsp_que *rsp;
 
        DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
 
@@ -1752,8 +1780,14 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
        }
        memset(lg, 0, sizeof(struct logio_entry_24xx));
 
+       if (ql2xmaxqueues > 1)
+               req = ha->req_q_map[0];
+       else
+               req = vha->req;
+       rsp = req->rsp;
        lg->entry_type = LOGINOUT_PORT_IOCB_TYPE;
        lg->entry_count = 1;
+       lg->handle = MAKE_HANDLE(req->id, lg->handle);
        lg->nport_handle = cpu_to_le16(loop_id);
        lg->control_flags =
            __constant_cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO);
@@ -1864,9 +1898,6 @@ qla2x00_full_login_lip(scsi_qla_host_t *vha)
        mbx_cmd_t mc;
        mbx_cmd_t *mcp = &mc;
 
-       if (IS_QLA81XX(vha->hw))
-           return QLA_SUCCESS;
-
        DEBUG11(printk("qla2x00_full_login_lip(%ld): entered.\n",
            vha->host_no));
 
@@ -2195,21 +2226,21 @@ qla24xx_get_isp_stats(scsi_qla_host_t *vha, struct link_statistics *stats,
 }
 
 int
-qla24xx_abort_command(scsi_qla_host_t *vha, srb_t *sp, struct req_que *req)
+qla24xx_abort_command(srb_t *sp)
 {
        int             rval;
-       fc_port_t       *fcport;
        unsigned long   flags = 0;
 
        struct abort_entry_24xx *abt;
        dma_addr_t      abt_dma;
        uint32_t        handle;
+       fc_port_t       *fcport = sp->fcport;
+       struct scsi_qla_host *vha = fcport->vha;
        struct qla_hw_data *ha = vha->hw;
+       struct req_que *req = vha->req;
 
        DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
 
-       fcport = sp->fcport;
-
        spin_lock_irqsave(&ha->hardware_lock, flags);
        for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) {
                if (req->outstanding_cmds[handle] == sp)
@@ -2231,6 +2262,7 @@ qla24xx_abort_command(scsi_qla_host_t *vha, srb_t *sp, struct req_que *req)
 
        abt->entry_type = ABORT_IOCB_TYPE;
        abt->entry_count = 1;
+       abt->handle = MAKE_HANDLE(req->id, abt->handle);
        abt->nport_handle = cpu_to_le16(fcport->loop_id);
        abt->handle_to_abort = handle;
        abt->port_id[0] = fcport->d_id.b.al_pa;
@@ -2272,7 +2304,7 @@ struct tsk_mgmt_cmd {
 
 static int
 __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
-    unsigned int l)
+    unsigned int l, int tag)
 {
        int             rval, rval2;
        struct tsk_mgmt_cmd *tsk;
@@ -2286,8 +2318,11 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
 
        vha = fcport->vha;
        ha = vha->hw;
-       req = ha->req_q_map[0];
-       rsp = ha->rsp_q_map[0];
+       req = vha->req;
+       if (ql2xmultique_tag)
+               rsp = ha->rsp_q_map[tag + 1];
+       else
+               rsp = req->rsp;
        tsk = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &tsk_dma);
        if (tsk == NULL) {
                DEBUG2_3(printk("%s(%ld): failed to allocate Task Management "
@@ -2298,6 +2333,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
 
        tsk->p.tsk.entry_type = TSK_MGMT_IOCB_TYPE;
        tsk->p.tsk.entry_count = 1;
+       tsk->p.tsk.handle = MAKE_HANDLE(req->id, tsk->p.tsk.handle);
        tsk->p.tsk.nport_handle = cpu_to_le16(fcport->loop_id);
        tsk->p.tsk.timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
        tsk->p.tsk.control_flags = cpu_to_le32(type);
@@ -2344,15 +2380,15 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
 }
 
 int
-qla24xx_abort_target(struct fc_port *fcport, unsigned int l)
+qla24xx_abort_target(struct fc_port *fcport, unsigned int l, int tag)
 {
-       return __qla24xx_issue_tmf("Target", TCF_TARGET_RESET, fcport, l);
+       return __qla24xx_issue_tmf("Target", TCF_TARGET_RESET, fcport, l, tag);
 }
 
 int
-qla24xx_lun_reset(struct fc_port *fcport, unsigned int l)
+qla24xx_lun_reset(struct fc_port *fcport, unsigned int l, int tag)
 {
-       return __qla24xx_issue_tmf("Lun", TCF_LUN_RESET, fcport, l);
+       return __qla24xx_issue_tmf("Lun", TCF_LUN_RESET, fcport, l, tag);
 }
 
 int
@@ -2446,6 +2482,8 @@ qla2x00_stop_firmware(scsi_qla_host_t *vha)
        if (rval != QLA_SUCCESS) {
                DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
                    vha->host_no, rval));
+               if (mcp->mb[0] == MBS_INVALID_COMMAND)
+                       rval = QLA_INVALID_COMMAND;
        } else {
                DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
        }
@@ -2717,8 +2755,11 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
                if (vp_idx == 0)
                        return;
 
-               if (MSB(stat) == 1)
+               if (MSB(stat) == 1) {
+                       DEBUG2(printk("scsi(%ld): Could not acquire ID for "
+                           "VP[%d].\n", vha->host_no, vp_idx));
                        return;
+               }
 
                list_for_each_entry_safe(vp, tvp, &ha->vp_list, list)
                        if (vp_idx == vp->vp_idx)
@@ -3141,6 +3182,8 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req)
                WRT_REG_DWORD(&reg->req_q_in, 0);
                WRT_REG_DWORD(&reg->req_q_out, 0);
        }
+       req->req_q_in = &reg->req_q_in;
+       req->req_q_out = &reg->req_q_out;
        spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
        rval = qla2x00_mailbox_command(vha, mcp);
@@ -3167,7 +3210,6 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
        mcp->mb[6] = MSW(MSD(rsp->dma));
        mcp->mb[7] = LSW(MSD(rsp->dma));
        mcp->mb[5] = rsp->length;
-       mcp->mb[11] = rsp->vp_idx;
        mcp->mb[14] = rsp->msix->entry;
        mcp->mb[13] = rsp->rid;
 
@@ -3179,7 +3221,7 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
        mcp->mb[8] = 0;
        /* que out ptr index */
        mcp->mb[9] = 0;
-       mcp->out_mb = MBX_14|MBX_13|MBX_12|MBX_11|MBX_10|MBX_9|MBX_8|MBX_7
+       mcp->out_mb = MBX_14|MBX_13|MBX_9|MBX_8|MBX_7
                        |MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
        mcp->in_mb = MBX_0;
        mcp->flags = MBX_DMA_OUT;
@@ -3384,7 +3426,7 @@ qla2x00_read_edc(scsi_qla_host_t *vha, uint16_t dev, uint16_t adr,
                DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__,
                    vha->host_no, rval, mcp->mb[0]));
        } else {
-               DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+               DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
        }
 
        return rval;
@@ -3428,3 +3470,141 @@ qla2x00_write_edc(scsi_qla_host_t *vha, uint16_t dev, uint16_t adr,
 
        return rval;
 }
+
+int
+qla2x00_get_xgmac_stats(scsi_qla_host_t *vha, dma_addr_t stats_dma,
+    uint16_t size_in_bytes, uint16_t *actual_size)
+{
+       int rval;
+       mbx_cmd_t mc;
+       mbx_cmd_t *mcp = &mc;
+
+       if (!IS_QLA81XX(vha->hw))
+               return QLA_FUNCTION_FAILED;
+
+       DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+
+       mcp->mb[0] = MBC_GET_XGMAC_STATS;
+       mcp->mb[2] = MSW(stats_dma);
+       mcp->mb[3] = LSW(stats_dma);
+       mcp->mb[6] = MSW(MSD(stats_dma));
+       mcp->mb[7] = LSW(MSD(stats_dma));
+       mcp->mb[8] = size_in_bytes >> 2;
+       mcp->out_mb = MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0;
+       mcp->in_mb = MBX_2|MBX_1|MBX_0;
+       mcp->tov = MBX_TOV_SECONDS;
+       mcp->flags = 0;
+       rval = qla2x00_mailbox_command(vha, mcp);
+
+       if (rval != QLA_SUCCESS) {
+               DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=0x%x "
+                   "mb[1]=0x%x mb[2]=0x%x.\n", __func__, vha->host_no, rval,
+                   mcp->mb[0], mcp->mb[1], mcp->mb[2]));
+       } else {
+               DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+
+               *actual_size = mcp->mb[2] << 2;
+       }
+
+       return rval;
+}
+
+int
+qla2x00_get_dcbx_params(scsi_qla_host_t *vha, dma_addr_t tlv_dma,
+    uint16_t size)
+{
+       int rval;
+       mbx_cmd_t mc;
+       mbx_cmd_t *mcp = &mc;
+
+       if (!IS_QLA81XX(vha->hw))
+               return QLA_FUNCTION_FAILED;
+
+       DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+
+       mcp->mb[0] = MBC_GET_DCBX_PARAMS;
+       mcp->mb[1] = 0;
+       mcp->mb[2] = MSW(tlv_dma);
+       mcp->mb[3] = LSW(tlv_dma);
+       mcp->mb[6] = MSW(MSD(tlv_dma));
+       mcp->mb[7] = LSW(MSD(tlv_dma));
+       mcp->mb[8] = size;
+       mcp->out_mb = MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+       mcp->in_mb = MBX_2|MBX_1|MBX_0;
+       mcp->tov = MBX_TOV_SECONDS;
+       mcp->flags = 0;
+       rval = qla2x00_mailbox_command(vha, mcp);
+
+       if (rval != QLA_SUCCESS) {
+               DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=0x%x "
+                   "mb[1]=0x%x mb[2]=0x%x.\n", __func__, vha->host_no, rval,
+                   mcp->mb[0], mcp->mb[1], mcp->mb[2]));
+       } else {
+               DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+       }
+
+       return rval;
+}
+
+int
+qla2x00_read_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t *data)
+{
+       int rval;
+       mbx_cmd_t mc;
+       mbx_cmd_t *mcp = &mc;
+
+       if (!IS_FWI2_CAPABLE(vha->hw))
+               return QLA_FUNCTION_FAILED;
+
+       DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+
+       mcp->mb[0] = MBC_READ_RAM_EXTENDED;
+       mcp->mb[1] = LSW(risc_addr);
+       mcp->mb[8] = MSW(risc_addr);
+       mcp->out_mb = MBX_8|MBX_1|MBX_0;
+       mcp->in_mb = MBX_3|MBX_2|MBX_0;
+       mcp->tov = 30;
+       mcp->flags = 0;
+       rval = qla2x00_mailbox_command(vha, mcp);
+       if (rval != QLA_SUCCESS) {
+               DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__,
+                   vha->host_no, rval, mcp->mb[0]));
+       } else {
+               DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+               *data = mcp->mb[3] << 16 | mcp->mb[2];
+       }
+
+       return rval;
+}
+
+int
+qla2x00_write_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t data)
+{
+       int rval;
+       mbx_cmd_t mc;
+       mbx_cmd_t *mcp = &mc;
+
+       if (!IS_FWI2_CAPABLE(vha->hw))
+                return QLA_FUNCTION_FAILED;
+
+       DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+
+       mcp->mb[0] = MBC_WRITE_RAM_WORD_EXTENDED;
+       mcp->mb[1] = LSW(risc_addr);
+       mcp->mb[2] = LSW(data);
+       mcp->mb[3] = MSW(data);
+       mcp->mb[8] = MSW(risc_addr);
+       mcp->out_mb = MBX_8|MBX_3|MBX_2|MBX_1|MBX_0;
+       mcp->in_mb = MBX_0;
+       mcp->tov = 30;
+       mcp->flags = 0;
+       rval = qla2x00_mailbox_command(vha, mcp);
+       if (rval != QLA_SUCCESS) {
+               DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__,
+                   vha->host_no, rval, mcp->mb[0]));
+       } else {
+               DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
+       }
+
+       return rval;
+}
index 51716c7e30083495ebcc59ed387fd498472ac181..650bcef08f2a4d080f5e325a7fd82766179aeb7b 100644 (file)
@@ -398,9 +398,8 @@ qla24xx_create_vhost(struct fc_vport *fc_vport)
 
        qla2x00_start_timer(vha, qla2x00_timer, WATCH_INTERVAL);
 
-       memset(vha->req_ques, 0, sizeof(vha->req_ques));
-       vha->req_ques[0] = ha->req_q_map[0]->id;
-       host->can_queue = ha->req_q_map[0]->length + 128;
+       vha->req = base_vha->req;
+       host->can_queue = base_vha->req->length + 128;
        host->this_id = 255;
        host->cmd_per_lun = 3;
        host->max_cmd_len = MAX_CMDSZ;
@@ -515,76 +514,53 @@ int qla25xx_update_req_que(struct scsi_qla_host *vha, uint8_t que, uint8_t qos)
 
 /* Delete all queues for a given vhost */
 int
-qla25xx_delete_queues(struct scsi_qla_host *vha, uint8_t que_no)
+qla25xx_delete_queues(struct scsi_qla_host *vha)
 {
        int cnt, ret = 0;
        struct req_que *req = NULL;
        struct rsp_que *rsp = NULL;
        struct qla_hw_data *ha = vha->hw;
 
-       if (que_no) {
-       /* Delete request queue */
-               req = ha->req_q_map[que_no];
+       /* Delete request queues */
+       for (cnt = 1; cnt < ha->max_req_queues; cnt++) {
+               req = ha->req_q_map[cnt];
                if (req) {
-                       rsp = req->rsp;
                        ret = qla25xx_delete_req_que(vha, req);
                        if (ret != QLA_SUCCESS) {
                                qla_printk(KERN_WARNING, ha,
-                               "Couldn't delete req que %d\n", req->id);
+                               "Couldn't delete req que %d\n",
+                               req->id);
                                return ret;
                        }
-                       /* Delete associated response queue */
-                       if (rsp) {
-                               ret = qla25xx_delete_rsp_que(vha, rsp);
-                               if (ret != QLA_SUCCESS) {
-                                       qla_printk(KERN_WARNING, ha,
-                                               "Couldn't delete rsp que %d\n",
-                                               rsp->id);
-                                       return ret;
-                               }
-                       }
                }
-       } else {  /* delete all queues of this host */
-               for (cnt = 0; cnt < QLA_MAX_HOST_QUES; cnt++) {
-                       /* Delete request queues */
-                       req = ha->req_q_map[vha->req_ques[cnt]];
-                       if (req && req->id) {
-                               rsp = req->rsp;
-                               ret = qla25xx_delete_req_que(vha, req);
-                               if (ret != QLA_SUCCESS) {
-                                       qla_printk(KERN_WARNING, ha,
-                                               "Couldn't delete req que %d\n",
-                                               vha->req_ques[cnt]);
-                                       return ret;
-                               }
-                               vha->req_ques[cnt] = ha->req_q_map[0]->id;
-                       /* Delete associated response queue */
-                               if (rsp && rsp->id) {
-                                       ret = qla25xx_delete_rsp_que(vha, rsp);
-                                       if (ret != QLA_SUCCESS) {
-                                               qla_printk(KERN_WARNING, ha,
-                                               "Couldn't delete rsp que %d\n",
-                                               rsp->id);
-                                               return ret;
-                                       }
-                               }
+       }
+
+       /* Delete response queues */
+       for (cnt = 1; cnt < ha->max_rsp_queues; cnt++) {
+               rsp = ha->rsp_q_map[cnt];
+               if (rsp) {
+                       ret = qla25xx_delete_rsp_que(vha, rsp);
+                       if (ret != QLA_SUCCESS) {
+                               qla_printk(KERN_WARNING, ha,
+                               "Couldn't delete rsp que %d\n",
+                               rsp->id);
+                               return ret;
                        }
                }
        }
-       qla_printk(KERN_INFO, ha, "Queues deleted for vport:%d\n",
-               vha->vp_idx);
        return ret;
 }
 
 int
 qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
-       uint8_t vp_idx, uint16_t rid, uint8_t rsp_que, uint8_t qos)
+       uint8_t vp_idx, uint16_t rid, int rsp_que, uint8_t qos)
 {
        int ret = 0;
        struct req_que *req = NULL;
        struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
        uint16_t que_id = 0;
        device_reg_t __iomem *reg;
+       uint32_t cnt;
 
        req = kzalloc(sizeof(struct req_que), GFP_KERNEL);
        if (req == NULL) {
@@ -604,8 +580,8 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
        }
 
        mutex_lock(&ha->vport_lock);
-       que_id = find_first_zero_bit(ha->req_qid_map, ha->max_queues);
-       if (que_id >= ha->max_queues) {
+       que_id = find_first_zero_bit(ha->req_qid_map, ha->max_req_queues);
+       if (que_id >= ha->max_req_queues) {
                mutex_unlock(&ha->vport_lock);
                qla_printk(KERN_INFO, ha, "No resources to create "
                         "additional request queue\n");
@@ -617,10 +593,10 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
        req->vp_idx = vp_idx;
        req->qos = qos;
 
-       if (ha->rsp_q_map[rsp_que]) {
+       if (rsp_que < 0)
+               req->rsp = NULL;
+       else
                req->rsp = ha->rsp_q_map[rsp_que];
-               req->rsp->req = req;
-       }
        /* Use alternate PCI bus number */
        if (MSB(req->rid))
                options |= BIT_4;
@@ -628,13 +604,16 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
        if (LSB(req->rid))
                options |= BIT_5;
        req->options = options;
+
+       for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++)
+               req->outstanding_cmds[cnt] = NULL;
+       req->current_outstanding_cmd = 1;
+
        req->ring_ptr = req->ring;
        req->ring_index = 0;
        req->cnt = req->length;
        req->id = que_id;
        reg = ISP_QUE_REG(ha, que_id);
-       req->req_q_in = &reg->isp25mq.req_q_in;
-       req->req_q_out = &reg->isp25mq.req_q_out;
        req->max_q_depth = ha->req_q_map[0]->max_q_depth;
        mutex_unlock(&ha->vport_lock);
 
@@ -654,10 +633,19 @@ que_failed:
        return 0;
 }
 
+static void qla_do_work(struct work_struct *work)
+{
+       struct rsp_que *rsp = container_of(work, struct rsp_que, q_work);
+       struct scsi_qla_host *vha;
+
+       vha = qla25xx_get_host(rsp);
+       qla24xx_process_response_queue(vha, rsp);
+}
+
 /* create response queue */
 int
 qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
-       uint8_t vp_idx, uint16_t rid)
+       uint8_t vp_idx, uint16_t rid, int req)
 {
        int ret = 0;
        struct rsp_que *rsp = NULL;
@@ -672,7 +660,7 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
                goto que_failed;
        }
 
-       rsp->length = RESPONSE_ENTRY_CNT_2300;
+       rsp->length = RESPONSE_ENTRY_CNT_MQ;
        rsp->ring = dma_alloc_coherent(&ha->pdev->dev,
                        (rsp->length + 1) * sizeof(response_t),
                        &rsp->dma, GFP_KERNEL);
@@ -683,8 +671,8 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
        }
 
        mutex_lock(&ha->vport_lock);
-       que_id = find_first_zero_bit(ha->rsp_qid_map, ha->max_queues);
-       if (que_id >= ha->max_queues) {
+       que_id = find_first_zero_bit(ha->rsp_qid_map, ha->max_rsp_queues);
+       if (que_id >= ha->max_rsp_queues) {
                mutex_unlock(&ha->vport_lock);
                qla_printk(KERN_INFO, ha, "No resources to create "
                         "additional response queue\n");
@@ -708,8 +696,6 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
        if (LSB(rsp->rid))
                options |= BIT_5;
        rsp->options = options;
-       rsp->ring_ptr = rsp->ring;
-       rsp->ring_index = 0;
        rsp->id = que_id;
        reg = ISP_QUE_REG(ha, que_id);
        rsp->rsp_q_in = &reg->isp25mq.rsp_q_in;
@@ -728,9 +714,14 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
                mutex_unlock(&ha->vport_lock);
                goto que_failed;
        }
+       if (req >= 0)
+               rsp->req = ha->req_q_map[req];
+       else
+               rsp->req = NULL;
 
        qla2x00_init_response_q_entries(rsp);
-
+       if (rsp->hw->wq)
+               INIT_WORK(&rsp->q_work, qla_do_work);
        return rsp->id;
 
 que_failed:
@@ -744,14 +735,16 @@ qla25xx_create_queues(struct scsi_qla_host *vha, uint8_t qos)
        uint16_t options = 0;
        uint8_t ret = 0;
        struct qla_hw_data *ha = vha->hw;
+       struct rsp_que *rsp;
 
        options |= BIT_1;
-       ret = qla25xx_create_rsp_que(ha, options, vha->vp_idx, 0);
+       ret = qla25xx_create_rsp_que(ha, options, vha->vp_idx, 0, -1);
        if (!ret) {
                qla_printk(KERN_WARNING, ha, "Response Que create failed\n");
                return ret;
        } else
                qla_printk(KERN_INFO, ha, "Response Que:%d created.\n", ret);
+       rsp = ha->rsp_q_map[ret];
 
        options = 0;
        if (qos & BIT_7)
@@ -759,10 +752,11 @@ qla25xx_create_queues(struct scsi_qla_host *vha, uint8_t qos)
        ret = qla25xx_create_req_que(ha, options, vha->vp_idx, 0, ret,
                                        qos & ~BIT_7);
        if (ret) {
-               vha->req_ques[0] = ret;
+               vha->req = ha->req_q_map[ret];
                qla_printk(KERN_INFO, ha, "Request Que:%d created.\n", ret);
        } else
                qla_printk(KERN_WARNING, ha, "Request Que create failed\n");
+       rsp->req = ha->req_q_map[ret];
 
        return ret;
 }
index e4fdcdad80d0b7c12c1be44b0eb75a834ef37d49..dcf011679c8bbab5de6ab3d0b0dc58af4527ec80 100644 (file)
@@ -77,6 +77,14 @@ module_param(ql2xmaxqdepth, int, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(ql2xmaxqdepth,
                "Maximum queue depth to report for target devices.");
 
+int ql2xqfulltracking = 1;
+module_param(ql2xqfulltracking, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ql2xqfulltracking,
+               "Controls whether the driver tracks queue full status "
+               "returns and dynamically adjusts a scsi device's queue "
+               "depth.  Default is 1, perform tracking.  Set to 0 to "
+               "disable dynamic tracking and adjustment of queue depth.");
+
 int ql2xqfullrampup = 120;
 module_param(ql2xqfullrampup, int, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(ql2xqfullrampup,
@@ -96,6 +104,23 @@ MODULE_PARM_DESC(ql2xmaxqueues,
                "Enables MQ settings "
                "Default is 1 for single queue. Set it to number \
                        of queues in MQ mode.");
+
+int ql2xmultique_tag;
+module_param(ql2xmultique_tag, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(ql2xmultique_tag,
+               "Enables CPU affinity settings for the driver "
+               "Default is 0 for no affinity of request and response IO. "
+               "Set it to 1 to turn on the cpu affinity.");
+
+int ql2xfwloadbin;
+module_param(ql2xfwloadbin, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(ql2xfwloadbin,
+               "Option to specify location from which to load ISP firmware:\n"
+               " 2 -- load firmware via the request_firmware() (hotplug)\n"
+               "      interface.\n"
+               " 1 -- load firmware from flash.\n"
+               " 0 -- use default semantics.\n");
+
 /*
  * SCSI host template entry points
  */
@@ -187,7 +212,7 @@ static void qla2x00_sp_free_dma(srb_t *);
 /* -------------------------------------------------------------------------- */
 static int qla2x00_alloc_queues(struct qla_hw_data *ha)
 {
-       ha->req_q_map = kzalloc(sizeof(struct req_que *) * ha->max_queues,
+       ha->req_q_map = kzalloc(sizeof(struct req_que *) * ha->max_req_queues,
                                GFP_KERNEL);
        if (!ha->req_q_map) {
                qla_printk(KERN_WARNING, ha,
@@ -195,7 +220,7 @@ static int qla2x00_alloc_queues(struct qla_hw_data *ha)
                goto fail_req_map;
        }
 
-       ha->rsp_q_map = kzalloc(sizeof(struct rsp_que *) * ha->max_queues,
+       ha->rsp_q_map = kzalloc(sizeof(struct rsp_que *) * ha->max_rsp_queues,
                                GFP_KERNEL);
        if (!ha->rsp_q_map) {
                qla_printk(KERN_WARNING, ha,
@@ -213,16 +238,8 @@ fail_req_map:
        return -ENOMEM;
 }
 
-static void qla2x00_free_que(struct qla_hw_data *ha, struct req_que *req,
-       struct rsp_que *rsp)
+static void qla2x00_free_req_que(struct qla_hw_data *ha, struct req_que *req)
 {
-       if (rsp && rsp->ring)
-               dma_free_coherent(&ha->pdev->dev,
-               (rsp->length + 1) * sizeof(response_t),
-               rsp->ring, rsp->dma);
-
-       kfree(rsp);
-       rsp = NULL;
        if (req && req->ring)
                dma_free_coherent(&ha->pdev->dev,
                (req->length + 1) * sizeof(request_t),
@@ -232,22 +249,77 @@ static void qla2x00_free_que(struct qla_hw_data *ha, struct req_que *req,
        req = NULL;
 }
 
+static void qla2x00_free_rsp_que(struct qla_hw_data *ha, struct rsp_que *rsp)
+{
+       if (rsp && rsp->ring)
+               dma_free_coherent(&ha->pdev->dev,
+               (rsp->length + 1) * sizeof(response_t),
+               rsp->ring, rsp->dma);
+
+       kfree(rsp);
+       rsp = NULL;
+}
+
 static void qla2x00_free_queues(struct qla_hw_data *ha)
 {
        struct req_que *req;
        struct rsp_que *rsp;
        int cnt;
 
-       for (cnt = 0; cnt < ha->max_queues; cnt++) {
-               rsp = ha->rsp_q_map[cnt];
+       for (cnt = 0; cnt < ha->max_req_queues; cnt++) {
                req = ha->req_q_map[cnt];
-               qla2x00_free_que(ha, req, rsp);
+               qla2x00_free_req_que(ha, req);
+       }
+       kfree(ha->req_q_map);
+       ha->req_q_map = NULL;
+
+       for (cnt = 0; cnt < ha->max_rsp_queues; cnt++) {
+               rsp = ha->rsp_q_map[cnt];
+               qla2x00_free_rsp_que(ha, rsp);
        }
        kfree(ha->rsp_q_map);
        ha->rsp_q_map = NULL;
+}
 
-       kfree(ha->req_q_map);
-       ha->req_q_map = NULL;
+static int qla25xx_setup_mode(struct scsi_qla_host *vha)
+{
+       uint16_t options = 0;
+       int ques, req, ret;
+       struct qla_hw_data *ha = vha->hw;
+
+       if (ql2xmultique_tag) {
+               /* CPU affinity mode */
+               ha->wq = create_workqueue("qla2xxx_wq");
+               /* create a request queue for IO */
+               options |= BIT_7;
+               req = qla25xx_create_req_que(ha, options, 0, 0, -1,
+                       QLA_DEFAULT_QUE_QOS);
+               if (!req) {
+                       qla_printk(KERN_WARNING, ha,
+                               "Can't create request queue\n");
+                       goto fail;
+               }
+               vha->req = ha->req_q_map[req];
+               options |= BIT_1;
+               for (ques = 1; ques < ha->max_rsp_queues; ques++) {
+                       ret = qla25xx_create_rsp_que(ha, options, 0, 0, req);
+                       if (!ret) {
+                               qla_printk(KERN_WARNING, ha,
+                                       "Response Queue create failed\n");
+                               goto fail2;
+                       }
+               }
+               DEBUG2(qla_printk(KERN_INFO, ha,
+                       "CPU affinity mode enabled, no. of response"
+                       " queues:%d, no. of request queues:%d\n",
+                       ha->max_rsp_queues, ha->max_req_queues));
+       }
+       return 0;
+fail2:
+       qla25xx_delete_queues(vha);
+fail:
+       ha->mqenable = 0;
+       return 1;
 }
 
 static char *
@@ -387,7 +459,6 @@ qla2x00_get_new_sp(scsi_qla_host_t *vha, fc_port_t *fcport,
 
        sp->fcport = fcport;
        sp->cmd = cmd;
-       sp->que = ha->req_q_map[0];
        sp->flags = 0;
        CMD_SP(cmd) = (void *)sp;
        cmd->scsi_done = done;
@@ -612,7 +683,7 @@ qla2x00_wait_for_loop_ready(scsi_qla_host_t *vha)
 void
 qla2x00_abort_fcport_cmds(fc_port_t *fcport)
 {
-       int cnt, que, id;
+       int cnt;
        unsigned long flags;
        srb_t *sp;
        scsi_qla_host_t *vha = fcport->vha;
@@ -620,32 +691,27 @@ qla2x00_abort_fcport_cmds(fc_port_t *fcport)
        struct req_que *req;
 
        spin_lock_irqsave(&ha->hardware_lock, flags);
-       for (que = 0; que < QLA_MAX_HOST_QUES; que++) {
-               id = vha->req_ques[que];
-               req = ha->req_q_map[id];
-               if (!req)
+       req = vha->req;
+       for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+               sp = req->outstanding_cmds[cnt];
+               if (!sp)
+                       continue;
+               if (sp->fcport != fcport)
                        continue;
-               for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
-                       sp = req->outstanding_cmds[cnt];
-                       if (!sp)
-                               continue;
-                       if (sp->fcport != fcport)
-                               continue;
 
-                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
-                       if (ha->isp_ops->abort_command(vha, sp, req)) {
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+               if (ha->isp_ops->abort_command(sp)) {
+                       DEBUG2(qla_printk(KERN_WARNING, ha,
+                       "Abort failed --  %lx\n",
+                       sp->cmd->serial_number));
+               } else {
+                       if (qla2x00_eh_wait_on_command(sp->cmd) !=
+                               QLA_SUCCESS)
                                DEBUG2(qla_printk(KERN_WARNING, ha,
-                               "Abort failed --  %lx\n",
+                               "Abort failed while waiting --  %lx\n",
                                sp->cmd->serial_number));
-                       } else {
-                               if (qla2x00_eh_wait_on_command(sp->cmd) !=
-                                       QLA_SUCCESS)
-                                       DEBUG2(qla_printk(KERN_WARNING, ha,
-                                       "Abort failed while waiting --  %lx\n",
-                                       sp->cmd->serial_number));
-                       }
-                       spin_lock_irqsave(&ha->hardware_lock, flags);
                }
+               spin_lock_irqsave(&ha->hardware_lock, flags);
        }
        spin_unlock_irqrestore(&ha->hardware_lock, flags);
 }
@@ -693,7 +759,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
        unsigned long flags;
        int wait = 0;
        struct qla_hw_data *ha = vha->hw;
-       struct req_que *req;
+       struct req_que *req = vha->req;
        srb_t *spt;
 
        qla2x00_block_error_handler(cmd);
@@ -709,7 +775,6 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
        spt = (srb_t *) CMD_SP(cmd);
        if (!spt)
                return SUCCESS;
-       req = spt->que;
 
        /* Check active list for command command. */
        spin_lock_irqsave(&ha->hardware_lock, flags);
@@ -726,7 +791,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
                " pid=%ld.\n", __func__, vha->host_no, sp, serial));
 
                spin_unlock_irqrestore(&ha->hardware_lock, flags);
-               if (ha->isp_ops->abort_command(vha, sp, req)) {
+               if (ha->isp_ops->abort_command(sp)) {
                        DEBUG2(printk("%s(%ld): abort_command "
                        "mbx failed.\n", __func__, vha->host_no));
                        ret = FAILED;
@@ -777,7 +842,7 @@ qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t,
                return status;
 
        spin_lock_irqsave(&ha->hardware_lock, flags);
-       req = sp->que;
+       req = vha->req;
        for (cnt = 1; status == QLA_SUCCESS &&
                cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
                sp = req->outstanding_cmds[cnt];
@@ -820,7 +885,7 @@ static char *reset_errors[] = {
 
 static int
 __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
-    struct scsi_cmnd *cmd, int (*do_reset)(struct fc_port *, unsigned int))
+    struct scsi_cmnd *cmd, int (*do_reset)(struct fc_port *, unsigned int, int))
 {
        scsi_qla_host_t *vha = shost_priv(cmd->device->host);
        fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
@@ -841,7 +906,8 @@ __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
        if (qla2x00_wait_for_loop_ready(vha) != QLA_SUCCESS)
                goto eh_reset_failed;
        err = 2;
-       if (do_reset(fcport, cmd->device->lun) != QLA_SUCCESS)
+       if (do_reset(fcport, cmd->device->lun, cmd->request->cpu + 1)
+               != QLA_SUCCESS)
                goto eh_reset_failed;
        err = 3;
        if (qla2x00_eh_wait_for_pending_commands(vha, cmd->device->id,
@@ -996,6 +1062,9 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
                if (qla2x00_vp_abort_isp(vha))
                        goto eh_host_reset_lock;
        } else {
+               if (ha->wq)
+                       flush_workqueue(ha->wq);
+
                set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
                if (qla2x00_abort_isp(base_vha)) {
                        clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
@@ -1037,7 +1106,8 @@ qla2x00_loop_reset(scsi_qla_host_t *vha)
        struct fc_port *fcport;
        struct qla_hw_data *ha = vha->hw;
 
-       if (ha->flags.enable_lip_full_login && !vha->vp_idx) {
+       if (ha->flags.enable_lip_full_login && !vha->vp_idx &&
+           !IS_QLA81XX(ha)) {
                ret = qla2x00_full_login_lip(vha);
                if (ret != QLA_SUCCESS) {
                        DEBUG2_3(printk("%s(%ld): failed: "
@@ -1064,7 +1134,7 @@ qla2x00_loop_reset(scsi_qla_host_t *vha)
                        if (fcport->port_type != FCT_TARGET)
                                continue;
 
-                       ret = ha->isp_ops->target_reset(fcport, 0);
+                       ret = ha->isp_ops->target_reset(fcport, 0, 0);
                        if (ret != QLA_SUCCESS) {
                                DEBUG2_3(printk("%s(%ld): bus_reset failed: "
                                    "target_reset=%d d_id=%x.\n", __func__,
@@ -1088,7 +1158,7 @@ qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res)
        struct req_que *req;
 
        spin_lock_irqsave(&ha->hardware_lock, flags);
-       for (que = 0; que < ha->max_queues; que++) {
+       for (que = 0; que < ha->max_req_queues; que++) {
                req = ha->req_q_map[que];
                if (!req)
                        continue;
@@ -1123,7 +1193,7 @@ qla2xxx_slave_configure(struct scsi_device *sdev)
        scsi_qla_host_t *vha = shost_priv(sdev->host);
        struct qla_hw_data *ha = vha->hw;
        struct fc_rport *rport = starget_to_rport(sdev->sdev_target);
-       struct req_que *req = ha->req_q_map[vha->req_ques[0]];
+       struct req_que *req = vha->req;
 
        if (sdev->tagged_supported)
                scsi_activate_tcq(sdev, req->max_q_depth);
@@ -1511,6 +1581,13 @@ qla2x00_set_isp_flags(struct qla_hw_data *ha)
                ha->fw_srisc_address = RISC_START_ADDRESS_2400;
                break;
        }
+
+       /* Get adapter physical port no from interrupt pin register. */
+       pci_read_config_byte(ha->pdev, PCI_INTERRUPT_PIN, &ha->port_no);
+       if (ha->port_no & 1)
+               ha->flags.port0 = 1;
+       else
+               ha->flags.port0 = 0;
 }
 
 static int
@@ -1518,6 +1595,7 @@ qla2x00_iospace_config(struct qla_hw_data *ha)
 {
        resource_size_t pio;
        uint16_t msix;
+       int cpus;
 
        if (pci_request_selected_regions(ha->pdev, ha->bars,
            QLA2XXX_DRIVER_NAME)) {
@@ -1571,8 +1649,9 @@ skip_pio:
        }
 
        /* Determine queue resources */
-       ha->max_queues = 1;
-       if (ql2xmaxqueues <= 1 || (!IS_QLA25XX(ha) && !IS_QLA81XX(ha)))
+       ha->max_req_queues = ha->max_rsp_queues = 1;
+       if ((ql2xmaxqueues <= 1 || ql2xmultique_tag < 1) &&
+               (!IS_QLA25XX(ha) && !IS_QLA81XX(ha)))
                goto mqiobase_exit;
        ha->mqiobase = ioremap(pci_resource_start(ha->pdev, 3),
                        pci_resource_len(ha->pdev, 3));
@@ -1582,18 +1661,24 @@ skip_pio:
                ha->msix_count = msix;
                /* Max queues are bounded by available msix vectors */
                /* queue 0 uses two msix vectors */
-               if (ha->msix_count - 1 < ql2xmaxqueues)
-                       ha->max_queues = ha->msix_count - 1;
-               else if (ql2xmaxqueues > QLA_MQ_SIZE)
-                       ha->max_queues = QLA_MQ_SIZE;
-               else
-                       ha->max_queues = ql2xmaxqueues;
+               if (ql2xmultique_tag) {
+                       cpus = num_online_cpus();
+                       ha->max_rsp_queues = (ha->msix_count - 1 - cpus) ?
+                               (cpus + 1) : (ha->msix_count - 1);
+                       ha->max_req_queues = 2;
+               } else if (ql2xmaxqueues > 1) {
+                       ha->max_req_queues = ql2xmaxqueues > QLA_MQ_SIZE ?
+                                               QLA_MQ_SIZE : ql2xmaxqueues;
+                       DEBUG2(qla_printk(KERN_INFO, ha, "QoS mode set, max no"
+                       " of request queues:%d\n", ha->max_req_queues));
+               }
                qla_printk(KERN_INFO, ha,
                        "MSI-X vector count: %d\n", msix);
-       }
+       } else
+               qla_printk(KERN_INFO, ha, "BAR 3 not enabled\n");
 
 mqiobase_exit:
-       ha->msix_count = ha->max_queues + 1;
+       ha->msix_count = ha->max_rsp_queues + 1;
        return (0);
 
 iospace_error_exit:
@@ -1605,6 +1690,9 @@ qla2xxx_scan_start(struct Scsi_Host *shost)
 {
        scsi_qla_host_t *vha = shost_priv(shost);
 
+       if (vha->hw->flags.running_gold_fw)
+               return;
+
        set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
        set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
        set_bit(RSCN_UPDATE, &vha->dpc_flags);
@@ -1768,6 +1856,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
                ha->init_cb_size = sizeof(struct mid_init_cb_81xx);
                ha->gid_list_info_size = 8;
                ha->optrom_size = OPTROM_SIZE_81XX;
+               ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX;
                ha->isp_ops = &qla81xx_isp_ops;
                ha->flash_conf_off = FARX_ACCESS_FLASH_CONF_81XX;
                ha->flash_data_off = FARX_ACCESS_FLASH_DATA_81XX;
@@ -1803,14 +1892,15 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 
                ret = -ENOMEM;
                qla2x00_mem_free(ha);
-               qla2x00_free_que(ha, req, rsp);
+               qla2x00_free_req_que(ha, req);
+               qla2x00_free_rsp_que(ha, rsp);
                goto probe_hw_failed;
        }
 
        pci_set_drvdata(pdev, base_vha);
 
        host = base_vha->host;
-       base_vha->req_ques[0] = req->id;
+       base_vha->req = req;
        host->can_queue = req->length + 128;
        if (IS_QLA2XXX_MIDTYPE(ha))
                base_vha->mgmt_svr_loop_id = 10 + base_vha->vp_idx;
@@ -1841,7 +1931,10 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        }
        ha->rsp_q_map[0] = rsp;
        ha->req_q_map[0] = req;
-
+       rsp->req = req;
+       req->rsp = rsp;
+       set_bit(0, ha->req_qid_map);
+       set_bit(0, ha->rsp_qid_map);
        /* FWI2-capable only. */
        req->req_q_in = &ha->iobase->isp24.req_q_in;
        req->req_q_out = &ha->iobase->isp24.req_q_out;
@@ -1866,6 +1959,15 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
                goto probe_failed;
        }
 
+       if (ha->mqenable)
+               if (qla25xx_setup_mode(base_vha))
+                       qla_printk(KERN_WARNING, ha,
+                               "Can't create queues, falling back to single"
+                               " queue mode\n");
+
+       if (ha->flags.running_gold_fw)
+               goto skip_dpc;
+
        /*
         * Startup the kernel thread for this host adapter
         */
@@ -1878,6 +1980,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
                goto probe_failed;
        }
 
+skip_dpc:
        list_add_tail(&base_vha->list, &ha->vp_list);
        base_vha->host->irq = ha->pdev->irq;
 
@@ -1917,8 +2020,9 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        return 0;
 
 probe_init_failed:
-       qla2x00_free_que(ha, req, rsp);
-       ha->max_queues = 0;
+       qla2x00_free_req_que(ha, req);
+       qla2x00_free_rsp_que(ha, rsp);
+       ha->max_req_queues = ha->max_rsp_queues = 0;
 
 probe_failed:
        if (base_vha->timer_active)
@@ -1976,6 +2080,13 @@ qla2x00_remove_one(struct pci_dev *pdev)
 
        base_vha->flags.online = 0;
 
+       /* Flush the work queue and remove it */
+       if (ha->wq) {
+               flush_workqueue(ha->wq);
+               destroy_workqueue(ha->wq);
+               ha->wq = NULL;
+       }
+
        /* Kill the kernel thread for this host */
        if (ha->dpc_thread) {
                struct task_struct *t = ha->dpc_thread;
@@ -2017,6 +2128,8 @@ qla2x00_free_device(scsi_qla_host_t *vha)
 {
        struct qla_hw_data *ha = vha->hw;
 
+       qla25xx_delete_queues(vha);
+
        if (ha->flags.fce_enabled)
                qla2x00_disable_fce_trace(vha, NULL, NULL);
 
@@ -2329,6 +2442,14 @@ qla2x00_mem_free(struct qla_hw_data *ha)
                vfree(ha->fw_dump);
        }
 
+       if (ha->dcbx_tlv)
+               dma_free_coherent(&ha->pdev->dev, DCBX_TLV_DATA_SIZE,
+                   ha->dcbx_tlv, ha->dcbx_tlv_dma);
+
+       if (ha->xgmac_data)
+               dma_free_coherent(&ha->pdev->dev, XGMAC_DATA_SIZE,
+                   ha->xgmac_data, ha->xgmac_data_dma);
+
        if (ha->sns_cmd)
                dma_free_coherent(&ha->pdev->dev, sizeof(struct sns_cmd_pkt),
                ha->sns_cmd, ha->sns_cmd_dma);
@@ -2412,6 +2533,8 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht,
        INIT_LIST_HEAD(&vha->work_list);
        INIT_LIST_HEAD(&vha->list);
 
+       spin_lock_init(&vha->work_lock);
+
        sprintf(vha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, vha->host_no);
        return vha;
 
@@ -2420,13 +2543,11 @@ fail:
 }
 
 static struct qla_work_evt *
-qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type,
-    int locked)
+qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type)
 {
        struct qla_work_evt *e;
 
-       e = kzalloc(sizeof(struct qla_work_evt), locked ? GFP_ATOMIC:
-           GFP_KERNEL);
+       e = kzalloc(sizeof(struct qla_work_evt), GFP_ATOMIC);
        if (!e)
                return NULL;
 
@@ -2437,17 +2558,15 @@ qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type,
 }
 
 static int
-qla2x00_post_work(struct scsi_qla_host *vha, struct qla_work_evt *e, int locked)
+qla2x00_post_work(struct scsi_qla_host *vha, struct qla_work_evt *e)
 {
-       unsigned long uninitialized_var(flags);
-       struct qla_hw_data *ha = vha->hw;
+       unsigned long flags;
 
-       if (!locked)
-               spin_lock_irqsave(&ha->hardware_lock, flags);
+       spin_lock_irqsave(&vha->work_lock, flags);
        list_add_tail(&e->list, &vha->work_list);
+       spin_unlock_irqrestore(&vha->work_lock, flags);
        qla2xxx_wake_dpc(vha);
-       if (!locked)
-               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
        return QLA_SUCCESS;
 }
 
@@ -2457,13 +2576,13 @@ qla2x00_post_aen_work(struct scsi_qla_host *vha, enum fc_host_event_code code,
 {
        struct qla_work_evt *e;
 
-       e = qla2x00_alloc_work(vha, QLA_EVT_AEN, 1);
+       e = qla2x00_alloc_work(vha, QLA_EVT_AEN);
        if (!e)
                return QLA_FUNCTION_FAILED;
 
        e->u.aen.code = code;
        e->u.aen.data = data;
-       return qla2x00_post_work(vha, e, 1);
+       return qla2x00_post_work(vha, e);
 }
 
 int
@@ -2471,25 +2590,27 @@ qla2x00_post_idc_ack_work(struct scsi_qla_host *vha, uint16_t *mb)
 {
        struct qla_work_evt *e;
 
-       e = qla2x00_alloc_work(vha, QLA_EVT_IDC_ACK, 1);
+       e = qla2x00_alloc_work(vha, QLA_EVT_IDC_ACK);
        if (!e)
                return QLA_FUNCTION_FAILED;
 
        memcpy(e->u.idc_ack.mb, mb, QLA_IDC_ACK_REGS * sizeof(uint16_t));
-       return qla2x00_post_work(vha, e, 1);
+       return qla2x00_post_work(vha, e);
 }
 
 static void
 qla2x00_do_work(struct scsi_qla_host *vha)
 {
-       struct qla_work_evt *e;
-       struct qla_hw_data *ha = vha->hw;
+       struct qla_work_evt *e, *tmp;
+       unsigned long flags;
+       LIST_HEAD(work);
 
-       spin_lock_irq(&ha->hardware_lock);
-       while (!list_empty(&vha->work_list)) {
-               e = list_entry(vha->work_list.next, struct qla_work_evt, list);
+       spin_lock_irqsave(&vha->work_lock, flags);
+       list_splice_init(&vha->work_list, &work);
+       spin_unlock_irqrestore(&vha->work_lock, flags);
+
+       list_for_each_entry_safe(e, tmp, &work, list) {
                list_del_init(&e->list);
-               spin_unlock_irq(&ha->hardware_lock);
 
                switch (e->type) {
                case QLA_EVT_AEN:
@@ -2502,10 +2623,9 @@ qla2x00_do_work(struct scsi_qla_host *vha)
                }
                if (e->flags & QLA_EVT_FLAG_FREE)
                        kfree(e);
-               spin_lock_irq(&ha->hardware_lock);
        }
-       spin_unlock_irq(&ha->hardware_lock);
 }
+
 /* Relogins all the fcports of a vport
  * Context: dpc thread
  */
index 152ecfc26cd201e4ad0b304708e2962a7619afa2..6260505dceb5ff20fc00e8305734453c08872a38 100644 (file)
@@ -219,8 +219,8 @@ qla2x00_write_nvram_word(struct qla_hw_data *ha, uint32_t addr, uint16_t data)
        wait_cnt = NVR_WAIT_CNT;
        do {
                if (!--wait_cnt) {
-                       DEBUG9_10(printk("%s(%ld): NVRAM didn't go ready...\n",
-                           __func__, vha->host_no));
+                       DEBUG9_10(qla_printk(KERN_WARNING, ha,
+                           "NVRAM didn't go ready...\n"));
                        break;
                }
                NVRAM_DELAY();
@@ -349,7 +349,7 @@ qla2x00_clear_nvram_protection(struct qla_hw_data *ha)
                wait_cnt = NVR_WAIT_CNT;
                do {
                        if (!--wait_cnt) {
-                               DEBUG9_10(qla_printk(
+                               DEBUG9_10(qla_printk(KERN_WARNING, ha,
                                    "NVRAM didn't go ready...\n"));
                                break;
                        }
@@ -408,7 +408,8 @@ qla2x00_set_nvram_protection(struct qla_hw_data *ha, int stat)
        wait_cnt = NVR_WAIT_CNT;
        do {
                if (!--wait_cnt) {
-                       DEBUG9_10(qla_printk("NVRAM didn't go ready...\n"));
+                       DEBUG9_10(qla_printk(KERN_WARNING, ha,
+                           "NVRAM didn't go ready...\n"));
                        break;
                }
                NVRAM_DELAY();
@@ -701,32 +702,35 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr)
                        break;
                case FLT_REG_VPD_0:
                        ha->flt_region_vpd_nvram = start;
-                       if (!(PCI_FUNC(ha->pdev->devfn) & 1))
+                       if (ha->flags.port0)
                                ha->flt_region_vpd = start;
                        break;
                case FLT_REG_VPD_1:
-                       if (PCI_FUNC(ha->pdev->devfn) & 1)
+                       if (!ha->flags.port0)
                                ha->flt_region_vpd = start;
                        break;
                case FLT_REG_NVRAM_0:
-                       if (!(PCI_FUNC(ha->pdev->devfn) & 1))
+                       if (ha->flags.port0)
                                ha->flt_region_nvram = start;
                        break;
                case FLT_REG_NVRAM_1:
-                       if (PCI_FUNC(ha->pdev->devfn) & 1)
+                       if (!ha->flags.port0)
                                ha->flt_region_nvram = start;
                        break;
                case FLT_REG_FDT:
                        ha->flt_region_fdt = start;
                        break;
                case FLT_REG_NPIV_CONF_0:
-                       if (!(PCI_FUNC(ha->pdev->devfn) & 1))
+                       if (ha->flags.port0)
                                ha->flt_region_npiv_conf = start;
                        break;
                case FLT_REG_NPIV_CONF_1:
-                       if (PCI_FUNC(ha->pdev->devfn) & 1)
+                       if (!ha->flags.port0)
                                ha->flt_region_npiv_conf = start;
                        break;
+               case FLT_REG_GOLD_FW:
+                       ha->flt_region_gold_fw = start;
+                       break;
                }
        }
        goto done;
@@ -744,12 +748,12 @@ no_flash_data:
        ha->flt_region_fw = def_fw[def];
        ha->flt_region_boot = def_boot[def];
        ha->flt_region_vpd_nvram = def_vpd_nvram[def];
-       ha->flt_region_vpd = !(PCI_FUNC(ha->pdev->devfn) & 1) ?
+       ha->flt_region_vpd = ha->flags.port0 ?
            def_vpd0[def]: def_vpd1[def];
-       ha->flt_region_nvram = !(PCI_FUNC(ha->pdev->devfn) & 1) ?
+       ha->flt_region_nvram = ha->flags.port0 ?
            def_nvram0[def]: def_nvram1[def];
        ha->flt_region_fdt = def_fdt[def];
-       ha->flt_region_npiv_conf = !(PCI_FUNC(ha->pdev->devfn) & 1) ?
+       ha->flt_region_npiv_conf = ha->flags.port0 ?
            def_npiv_conf0[def]: def_npiv_conf1[def];
 done:
        DEBUG2(qla_printk(KERN_DEBUG, ha, "FLT[%s]: boot=0x%x fw=0x%x "
@@ -924,6 +928,8 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha)
                struct fc_vport_identifiers vid;
                struct fc_vport *vport;
 
+               memcpy(&ha->npiv_info[i], entry, sizeof(struct qla_npiv_entry));
+
                flags = le16_to_cpu(entry->flags);
                if (flags == 0xffff)
                        continue;
@@ -937,9 +943,7 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha)
                vid.port_name = wwn_to_u64(entry->port_name);
                vid.node_name = wwn_to_u64(entry->node_name);
 
-               memcpy(&ha->npiv_info[i], entry, sizeof(struct qla_npiv_entry));
-
-               DEBUG2(qla_printk(KERN_DEBUG, ha, "NPIV[%02x]: wwpn=%llx "
+               DEBUG2(qla_printk(KERN_INFO, ha, "NPIV[%02x]: wwpn=%llx "
                        "wwnn=%llx vf_id=0x%x Q_qos=0x%x F_qos=0x%x.\n", cnt,
                        vid.port_name, vid.node_name, le16_to_cpu(entry->vf_id),
                        entry->q_qos, entry->f_qos));
@@ -955,7 +959,6 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha)
        }
 done:
        kfree(data);
-       ha->npiv_info = NULL;
 }
 
 static int
@@ -1079,8 +1082,9 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr,
                                    0xff0000) | ((fdata >> 16) & 0xff));
                        ret = qla24xx_erase_sector(vha, fdata);
                        if (ret != QLA_SUCCESS) {
-                               DEBUG9(qla_printk("Unable to erase sector: "
-                                   "address=%x.\n", faddr));
+                               DEBUG9(qla_printk(KERN_WARNING, ha,
+                                   "Unable to erase sector: address=%x.\n",
+                                   faddr));
                                break;
                        }
                }
@@ -1240,8 +1244,9 @@ qla24xx_write_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr,
                ret = qla24xx_write_flash_dword(ha,
                    nvram_data_addr(ha, naddr), cpu_to_le32(*dwptr));
                if (ret != QLA_SUCCESS) {
-                       DEBUG9(qla_printk("Unable to program nvram address=%x "
-                           "data=%x.\n", naddr, *dwptr));
+                       DEBUG9(qla_printk(KERN_WARNING, ha,
+                           "Unable to program nvram address=%x data=%x.\n",
+                           naddr, *dwptr));
                        break;
                }
        }
index 19d1afc3a34345fba4daa04f5c3f08959db5b891..b63feaf43126bd27f35c951453265c9e27c896bf 100644 (file)
@@ -7,7 +7,7 @@
 /*
  * Driver version
  */
-#define QLA2XXX_VERSION      "8.03.01-k1"
+#define QLA2XXX_VERSION      "8.03.01-k3"
 
 #define QLA_DRIVER_MAJOR_VER   8
 #define QLA_DRIVER_MINOR_VER   3
index 166417a6afbab0ceb2a30dec46a1cfddeb02a5a1..2de5f3ad640b2da78eba5401454c8415f44841e4 100644 (file)
@@ -1225,8 +1225,8 @@ EXPORT_SYMBOL(__scsi_device_lookup_by_target);
  * @starget:   SCSI target pointer
  * @lun:       SCSI Logical Unit Number
  *
- * Description: Looks up the scsi_device with the specified @channel, @id, @lun
- * for a given host.  The returned scsi_device has an additional reference that
+ * Description: Looks up the scsi_device with the specified @lun for a given
+ * @starget.  The returned scsi_device has an additional reference that
  * needs to be released with scsi_device_put once you're done with it.
  **/
 struct scsi_device *scsi_device_lookup_by_target(struct scsi_target *starget,
index 213123b0486b74a985492715b1c79dde0321682f..41a21772df1289525ad8859608d1d54c73461e76 100644 (file)
@@ -887,7 +887,7 @@ static int resp_start_stop(struct scsi_cmnd * scp,
 static sector_t get_sdebug_capacity(void)
 {
        if (scsi_debug_virtual_gb > 0)
-               return 2048 * 1024 * scsi_debug_virtual_gb;
+               return 2048 * 1024 * (sector_t)scsi_debug_virtual_gb;
        else
                return sdebug_store_sectors;
 }
index 0c2c73be197469289e17defb2bebb28febc4814e..a1689353d7fd715cf91776e9b290381795876477 100644 (file)
@@ -641,9 +641,9 @@ EXPORT_SYMBOL(scsi_eh_prep_cmnd);
 /**
  * scsi_eh_restore_cmnd  - Restore a scsi command info as part of error recory
  * @scmd:       SCSI command structure to restore
- * @ses:        saved information from a coresponding call to scsi_prep_eh_cmnd
+ * @ses:        saved information from a coresponding call to scsi_eh_prep_cmnd
  *
- * Undo any damage done by above scsi_prep_eh_cmnd().
+ * Undo any damage done by above scsi_eh_prep_cmnd().
  */
 void scsi_eh_restore_cmnd(struct scsi_cmnd* scmd, struct scsi_eh_save *ses)
 {
@@ -1451,28 +1451,21 @@ static void eh_lock_door_done(struct request *req, int uptodate)
  * @sdev:      SCSI device to prevent medium removal
  *
  * Locking:
- *     We must be called from process context; scsi_allocate_request()
- *     may sleep.
+ *     We must be called from process context.
  *
  * Notes:
  *     We queue up an asynchronous "ALLOW MEDIUM REMOVAL" request on the
  *     head of the devices request queue, and continue.
- *
- * Bugs:
- *     scsi_allocate_request() may sleep waiting for existing requests to
- *     be processed.  However, since we haven't kicked off any request
- *     processing for this host, this may deadlock.
- *
- *     If scsi_allocate_request() fails for what ever reason, we
- *     completely forget to lock the door.
  */
 static void scsi_eh_lock_door(struct scsi_device *sdev)
 {
        struct request *req;
 
+       /*
+        * blk_get_request with GFP_KERNEL (__GFP_WAIT) sleeps until a
+        * request becomes available
+        */
        req = blk_get_request(sdev->request_queue, READ, GFP_KERNEL);
-       if (!req)
-               return;
 
        req->cmd[0] = ALLOW_MEDIUM_REMOVAL;
        req->cmd[1] = 0;
index dd3f9d2b99fd05b7834e0abbb7e2cbe23e12d462..30f3275e119ed57473f1fd4e91270d38538eb025 100644 (file)
@@ -2412,20 +2412,18 @@ int
 scsi_internal_device_unblock(struct scsi_device *sdev)
 {
        struct request_queue *q = sdev->request_queue; 
-       int err;
        unsigned long flags;
        
        /* 
         * Try to transition the scsi device to SDEV_RUNNING
         * and goose the device queue if successful.  
         */
-       err = scsi_device_set_state(sdev, SDEV_RUNNING);
-       if (err) {
-               err = scsi_device_set_state(sdev, SDEV_CREATED);
-
-               if (err)
-                       return err;
-       }
+       if (sdev->sdev_state == SDEV_BLOCK)
+               sdev->sdev_state = SDEV_RUNNING;
+       else if (sdev->sdev_state == SDEV_CREATED_BLOCK)
+               sdev->sdev_state = SDEV_CREATED;
+       else
+               return -EINVAL;
 
        spin_lock_irqsave(q->queue_lock, flags);
        blk_start_queue(q);
index e2b50d8f57a86c686b932e2e6ae7db35001ff279..c44783801402083b7dbd610df35f8990eb090ef9 100644 (file)
@@ -115,12 +115,12 @@ MODULE_PARM_DESC(max_report_luns,
                 "REPORT LUNS maximum number of LUNS received (should be"
                 " between 1 and 16384)");
 
-static unsigned int scsi_inq_timeout = SCSI_TIMEOUT/HZ+3;
+static unsigned int scsi_inq_timeout = SCSI_TIMEOUT/HZ + 18;
 
 module_param_named(inq_timeout, scsi_inq_timeout, uint, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(inq_timeout, 
                 "Timeout (in seconds) waiting for devices to answer INQUIRY."
-                " Default is 5. Some non-compliant devices need more.");
+                " Default is 20. Some devices may need more; most need less.");
 
 /* This lock protects only this list */
 static DEFINE_SPINLOCK(async_scan_lock);
index 0a2ce7b6325cdbf13d5aceda17add03898567f24..f3e664628d7ae273d6e97c2f794d990b7ea9dcfd 100644 (file)
@@ -37,7 +37,6 @@
 #define ISCSI_TRANSPORT_VERSION "2.0-870"
 
 struct iscsi_internal {
-       int daemon_pid;
        struct scsi_transport_template t;
        struct iscsi_transport *iscsi_transport;
        struct list_head list;
@@ -938,23 +937,9 @@ iscsi_if_transport_lookup(struct iscsi_transport *tt)
 }
 
 static int
-iscsi_broadcast_skb(struct sk_buff *skb, gfp_t gfp)
+iscsi_multicast_skb(struct sk_buff *skb, uint32_t group, gfp_t gfp)
 {
-       return netlink_broadcast(nls, skb, 0, 1, gfp);
-}
-
-static int
-iscsi_unicast_skb(struct sk_buff *skb, int pid)
-{
-       int rc;
-
-       rc = netlink_unicast(nls, skb, pid, MSG_DONTWAIT);
-       if (rc < 0) {
-               printk(KERN_ERR "iscsi: can not unicast skb (%d)\n", rc);
-               return rc;
-       }
-
-       return 0;
+       return nlmsg_multicast(nls, skb, 0, group, gfp);
 }
 
 int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
@@ -980,7 +965,7 @@ int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
                return -ENOMEM;
        }
 
-       nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0);
+       nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
        ev = NLMSG_DATA(nlh);
        memset(ev, 0, sizeof(*ev));
        ev->transport_handle = iscsi_handle(conn->transport);
@@ -991,10 +976,45 @@ int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
        memcpy(pdu, hdr, sizeof(struct iscsi_hdr));
        memcpy(pdu + sizeof(struct iscsi_hdr), data, data_size);
 
-       return iscsi_unicast_skb(skb, priv->daemon_pid);
+       return iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC);
 }
 EXPORT_SYMBOL_GPL(iscsi_recv_pdu);
 
+int iscsi_offload_mesg(struct Scsi_Host *shost,
+                      struct iscsi_transport *transport, uint32_t type,
+                      char *data, uint16_t data_size)
+{
+       struct nlmsghdr *nlh;
+       struct sk_buff *skb;
+       struct iscsi_uevent *ev;
+       int len = NLMSG_SPACE(sizeof(*ev) + data_size);
+
+       skb = alloc_skb(len, GFP_NOIO);
+       if (!skb) {
+               printk(KERN_ERR "can not deliver iscsi offload message:OOM\n");
+               return -ENOMEM;
+       }
+
+       nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
+       ev = NLMSG_DATA(nlh);
+       memset(ev, 0, sizeof(*ev));
+       ev->type = type;
+       ev->transport_handle = iscsi_handle(transport);
+       switch (type) {
+       case ISCSI_KEVENT_PATH_REQ:
+               ev->r.req_path.host_no = shost->host_no;
+               break;
+       case ISCSI_KEVENT_IF_DOWN:
+               ev->r.notify_if_down.host_no = shost->host_no;
+               break;
+       }
+
+       memcpy((char *)ev + sizeof(*ev), data, data_size);
+
+       return iscsi_multicast_skb(skb, ISCSI_NL_GRP_UIP, GFP_NOIO);
+}
+EXPORT_SYMBOL_GPL(iscsi_offload_mesg);
+
 void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
 {
        struct nlmsghdr *nlh;
@@ -1014,7 +1034,7 @@ void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
                return;
        }
 
-       nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0);
+       nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
        ev = NLMSG_DATA(nlh);
        ev->transport_handle = iscsi_handle(conn->transport);
        ev->type = ISCSI_KEVENT_CONN_ERROR;
@@ -1022,7 +1042,7 @@ void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
        ev->r.connerror.cid = conn->cid;
        ev->r.connerror.sid = iscsi_conn_get_sid(conn);
 
-       iscsi_broadcast_skb(skb, GFP_ATOMIC);
+       iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC);
 
        iscsi_cls_conn_printk(KERN_INFO, conn, "detected conn error (%d)\n",
                              error);
@@ -1030,8 +1050,8 @@ void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
 EXPORT_SYMBOL_GPL(iscsi_conn_error_event);
 
 static int
-iscsi_if_send_reply(int pid, int seq, int type, int done, int multi,
-                     void *payload, int size)
+iscsi_if_send_reply(uint32_t group, int seq, int type, int done, int multi,
+                   void *payload, int size)
 {
        struct sk_buff  *skb;
        struct nlmsghdr *nlh;
@@ -1045,10 +1065,10 @@ iscsi_if_send_reply(int pid, int seq, int type, int done, int multi,
                return -ENOMEM;
        }
 
-       nlh = __nlmsg_put(skb, pid, seq, t, (len - sizeof(*nlh)), 0);
+       nlh = __nlmsg_put(skb, 0, 0, t, (len - sizeof(*nlh)), 0);
        nlh->nlmsg_flags = flags;
        memcpy(NLMSG_DATA(nlh), payload, size);
-       return iscsi_unicast_skb(skb, pid);
+       return iscsi_multicast_skb(skb, group, GFP_ATOMIC);
 }
 
 static int
@@ -1085,7 +1105,7 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
                        return -ENOMEM;
                }
 
-               nlhstat = __nlmsg_put(skbstat, priv->daemon_pid, 0, 0,
+               nlhstat = __nlmsg_put(skbstat, 0, 0, 0,
                                      (len - sizeof(*nlhstat)), 0);
                evstat = NLMSG_DATA(nlhstat);
                memset(evstat, 0, sizeof(*evstat));
@@ -1109,7 +1129,8 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
                skb_trim(skbstat, NLMSG_ALIGN(actual_size));
                nlhstat->nlmsg_len = actual_size;
 
-               err = iscsi_unicast_skb(skbstat, priv->daemon_pid);
+               err = iscsi_multicast_skb(skbstat, ISCSI_NL_GRP_ISCSID,
+                                         GFP_ATOMIC);
        } while (err < 0 && err != -ECONNREFUSED);
 
        return err;
@@ -1143,7 +1164,7 @@ int iscsi_session_event(struct iscsi_cls_session *session,
                return -ENOMEM;
        }
 
-       nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0);
+       nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
        ev = NLMSG_DATA(nlh);
        ev->transport_handle = iscsi_handle(session->transport);
 
@@ -1172,7 +1193,7 @@ int iscsi_session_event(struct iscsi_cls_session *session,
         * this will occur if the daemon is not up, so we just warn
         * the user and when the daemon is restarted it will handle it
         */
-       rc = iscsi_broadcast_skb(skb, GFP_KERNEL);
+       rc = iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL);
        if (rc == -ESRCH)
                iscsi_cls_session_printk(KERN_ERR, session,
                                         "Cannot notify userspace of session "
@@ -1268,26 +1289,54 @@ iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev)
        return err;
 }
 
+static int iscsi_if_ep_connect(struct iscsi_transport *transport,
+                              struct iscsi_uevent *ev, int msg_type)
+{
+       struct iscsi_endpoint *ep;
+       struct sockaddr *dst_addr;
+       struct Scsi_Host *shost = NULL;
+       int non_blocking, err = 0;
+
+       if (!transport->ep_connect)
+               return -EINVAL;
+
+       if (msg_type == ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST) {
+               shost = scsi_host_lookup(ev->u.ep_connect_through_host.host_no);
+               if (!shost) {
+                       printk(KERN_ERR "ep connect failed. Could not find "
+                              "host no %u\n",
+                              ev->u.ep_connect_through_host.host_no);
+                       return -ENODEV;
+               }
+               non_blocking = ev->u.ep_connect_through_host.non_blocking;
+       } else
+               non_blocking = ev->u.ep_connect.non_blocking;
+
+       dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
+       ep = transport->ep_connect(shost, dst_addr, non_blocking);
+       if (IS_ERR(ep)) {
+               err = PTR_ERR(ep);
+               goto release_host;
+       }
+
+       ev->r.ep_connect_ret.handle = ep->id;
+release_host:
+       if (shost)
+               scsi_host_put(shost);
+       return err;
+}
+
 static int
 iscsi_if_transport_ep(struct iscsi_transport *transport,
                      struct iscsi_uevent *ev, int msg_type)
 {
        struct iscsi_endpoint *ep;
-       struct sockaddr *dst_addr;
        int rc = 0;
 
        switch (msg_type) {
+       case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST:
        case ISCSI_UEVENT_TRANSPORT_EP_CONNECT:
-               if (!transport->ep_connect)
-                       return -EINVAL;
-
-               dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
-               ep = transport->ep_connect(dst_addr,
-                                          ev->u.ep_connect.non_blocking);
-               if (IS_ERR(ep))
-                       return PTR_ERR(ep);
-
-               ev->r.ep_connect_ret.handle = ep->id;
+               rc = iscsi_if_ep_connect(transport, ev, msg_type);
                break;
        case ISCSI_UEVENT_TRANSPORT_EP_POLL:
                if (!transport->ep_poll)
@@ -1365,7 +1414,31 @@ iscsi_set_host_param(struct iscsi_transport *transport,
 }
 
 static int
-iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+iscsi_set_path(struct iscsi_transport *transport, struct iscsi_uevent *ev)
+{
+       struct Scsi_Host *shost;
+       struct iscsi_path *params;
+       int err;
+
+       if (!transport->set_path)
+               return -ENOSYS;
+
+       shost = scsi_host_lookup(ev->u.set_path.host_no);
+       if (!shost) {
+               printk(KERN_ERR "set path could not find host no %u\n",
+                      ev->u.set_path.host_no);
+               return -ENODEV;
+       }
+
+       params = (struct iscsi_path *)((char *)ev + sizeof(*ev));
+       err = transport->set_path(shost, params);
+
+       scsi_host_put(shost);
+       return err;
+}
+
+static int
+iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
 {
        int err = 0;
        struct iscsi_uevent *ev = NLMSG_DATA(nlh);
@@ -1375,6 +1448,11 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        struct iscsi_cls_conn *conn;
        struct iscsi_endpoint *ep = NULL;
 
+       if (nlh->nlmsg_type == ISCSI_UEVENT_PATH_UPDATE)
+               *group = ISCSI_NL_GRP_UIP;
+       else
+               *group = ISCSI_NL_GRP_ISCSID;
+
        priv = iscsi_if_transport_lookup(iscsi_ptr(ev->transport_handle));
        if (!priv)
                return -EINVAL;
@@ -1383,8 +1461,6 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        if (!try_module_get(transport->owner))
                return -EINVAL;
 
-       priv->daemon_pid = NETLINK_CREDS(skb)->pid;
-
        switch (nlh->nlmsg_type) {
        case ISCSI_UEVENT_CREATE_SESSION:
                err = iscsi_if_create_session(priv, ep, ev,
@@ -1469,6 +1545,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        case ISCSI_UEVENT_TRANSPORT_EP_CONNECT:
        case ISCSI_UEVENT_TRANSPORT_EP_POLL:
        case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
+       case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST:
                err = iscsi_if_transport_ep(transport, ev, nlh->nlmsg_type);
                break;
        case ISCSI_UEVENT_TGT_DSCVR:
@@ -1477,6 +1554,9 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        case ISCSI_UEVENT_SET_HOST_PARAM:
                err = iscsi_set_host_param(transport, ev);
                break;
+       case ISCSI_UEVENT_PATH_UPDATE:
+               err = iscsi_set_path(transport, ev);
+               break;
        default:
                err = -ENOSYS;
                break;
@@ -1499,6 +1579,7 @@ iscsi_if_rx(struct sk_buff *skb)
                uint32_t rlen;
                struct nlmsghdr *nlh;
                struct iscsi_uevent *ev;
+               uint32_t group;
 
                nlh = nlmsg_hdr(skb);
                if (nlh->nlmsg_len < sizeof(*nlh) ||
@@ -1511,7 +1592,7 @@ iscsi_if_rx(struct sk_buff *skb)
                if (rlen > skb->len)
                        rlen = skb->len;
 
-               err = iscsi_if_recv_msg(skb, nlh);
+               err = iscsi_if_recv_msg(skb, nlh, &group);
                if (err) {
                        ev->type = ISCSI_KEVENT_IF_ERROR;
                        ev->iferror = err;
@@ -1525,8 +1606,7 @@ iscsi_if_rx(struct sk_buff *skb)
                         */
                        if (ev->type == ISCSI_UEVENT_GET_STATS && !err)
                                break;
-                       err = iscsi_if_send_reply(
-                               NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq,
+                       err = iscsi_if_send_reply(group, nlh->nlmsg_seq,
                                nlh->nlmsg_type, 0, 0, ev, sizeof(*ev));
                } while (err < 0 && err != -ECONNREFUSED);
                skb_pull(skb, rlen);
@@ -1774,7 +1854,6 @@ iscsi_register_transport(struct iscsi_transport *tt)
        if (!priv)
                return NULL;
        INIT_LIST_HEAD(&priv->list);
-       priv->daemon_pid = -1;
        priv->iscsi_transport = tt;
        priv->t.user_scan = iscsi_user_scan;
        priv->t.create_work_queue = 1;
index bcf3bd40bbd5fc3bfbecb29f0d7b21b5e393c8b3..878b17a9af3008ab5fe29afc7a8faa4a9c99d58d 100644 (file)
@@ -1902,24 +1902,6 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
        index = sdkp->index;
        dev = &sdp->sdev_gendev;
 
-       if (!sdp->request_queue->rq_timeout) {
-               if (sdp->type != TYPE_MOD)
-                       blk_queue_rq_timeout(sdp->request_queue, SD_TIMEOUT);
-               else
-                       blk_queue_rq_timeout(sdp->request_queue,
-                                            SD_MOD_TIMEOUT);
-       }
-
-       device_initialize(&sdkp->dev);
-       sdkp->dev.parent = &sdp->sdev_gendev;
-       sdkp->dev.class = &sd_disk_class;
-       dev_set_name(&sdkp->dev, dev_name(&sdp->sdev_gendev));
-
-       if (device_add(&sdkp->dev))
-               goto out_free_index;
-
-       get_device(&sdp->sdev_gendev);
-
        if (index < SD_MAX_DISKS) {
                gd->major = sd_major((index & 0xf0) >> 4);
                gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00);
@@ -1954,11 +1936,6 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
 
        sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n",
                  sdp->removable ? "removable " : "");
-
-       return;
-
- out_free_index:
-       ida_remove(&sd_index_ida, index);
 }
 
 /**
@@ -2026,6 +2003,24 @@ static int sd_probe(struct device *dev)
        sdkp->openers = 0;
        sdkp->previous_state = 1;
 
+       if (!sdp->request_queue->rq_timeout) {
+               if (sdp->type != TYPE_MOD)
+                       blk_queue_rq_timeout(sdp->request_queue, SD_TIMEOUT);
+               else
+                       blk_queue_rq_timeout(sdp->request_queue,
+                                            SD_MOD_TIMEOUT);
+       }
+
+       device_initialize(&sdkp->dev);
+       sdkp->dev.parent = &sdp->sdev_gendev;
+       sdkp->dev.class = &sd_disk_class;
+       dev_set_name(&sdkp->dev, dev_name(&sdp->sdev_gendev));
+
+       if (device_add(&sdkp->dev))
+               goto out_free_index;
+
+       get_device(&sdp->sdev_gendev);
+
        async_schedule(sd_probe_async, sdkp);
 
        return 0;
@@ -2055,8 +2050,10 @@ static int sd_probe(struct device *dev)
  **/
 static int sd_remove(struct device *dev)
 {
-       struct scsi_disk *sdkp = dev_get_drvdata(dev);
+       struct scsi_disk *sdkp;
 
+       async_synchronize_full();
+       sdkp = dev_get_drvdata(dev);
        device_del(&sdkp->dev);
        del_gendisk(sdkp->disk);
        sd_shutdown(dev);
index 89bd438e1fe30692e006a0bac18c18fc4c21cd86..b33d04250bbc3badeeb3f00644f137402af07bf1 100644 (file)
@@ -2964,7 +2964,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
                            !(STp->use_pf & PF_TESTED)) {
                                /* Try the other possible state of Page Format if not
                                   already tried */
-                               STp->use_pf = !STp->use_pf | PF_TESTED;
+                               STp->use_pf = (STp->use_pf ^ USE_PF) | PF_TESTED;
                                st_release_request(SRpnt);
                                SRpnt = NULL;
                                return st_int_ioctl(STp, cmd_in, arg);
index 583966ec82661f9959fe2434c853d50e2139d771..45374d66d26a4268ae9a765d45687c8a0c02c29a 100644 (file)
@@ -737,11 +737,14 @@ static int sym53c8xx_slave_alloc(struct scsi_device *sdev)
        struct sym_hcb *np = sym_get_hcb(sdev->host);
        struct sym_tcb *tp = &np->target[sdev->id];
        struct sym_lcb *lp;
+       unsigned long flags;
+       int error;
 
        if (sdev->id >= SYM_CONF_MAX_TARGET || sdev->lun >= SYM_CONF_MAX_LUN)
                return -ENXIO;
 
-       tp->starget = sdev->sdev_target;
+       spin_lock_irqsave(np->s.host->host_lock, flags);
+
        /*
         * Fail the device init if the device is flagged NOSCAN at BOOT in
         * the NVRAM.  This may speed up boot and maintain coherency with
@@ -753,26 +756,37 @@ static int sym53c8xx_slave_alloc(struct scsi_device *sdev)
 
        if (tp->usrflags & SYM_SCAN_BOOT_DISABLED) {
                tp->usrflags &= ~SYM_SCAN_BOOT_DISABLED;
-               starget_printk(KERN_INFO, tp->starget,
+               starget_printk(KERN_INFO, sdev->sdev_target,
                                "Scan at boot disabled in NVRAM\n");
-               return -ENXIO;
+               error = -ENXIO;
+               goto out;
        }
 
        if (tp->usrflags & SYM_SCAN_LUNS_DISABLED) {
-               if (sdev->lun != 0)
-                       return -ENXIO;
-               starget_printk(KERN_INFO, tp->starget,
+               if (sdev->lun != 0) {
+                       error = -ENXIO;
+                       goto out;
+               }
+               starget_printk(KERN_INFO, sdev->sdev_target,
                                "Multiple LUNs disabled in NVRAM\n");
        }
 
        lp = sym_alloc_lcb(np, sdev->id, sdev->lun);
-       if (!lp)
-               return -ENOMEM;
+       if (!lp) {
+               error = -ENOMEM;
+               goto out;
+       }
+       if (tp->nlcb == 1)
+               tp->starget = sdev->sdev_target;
 
        spi_min_period(tp->starget) = tp->usr_period;
        spi_max_width(tp->starget) = tp->usr_width;
 
-       return 0;
+       error = 0;
+out:
+       spin_unlock_irqrestore(np->s.host->host_lock, flags);
+
+       return error;
 }
 
 /*
@@ -819,12 +833,34 @@ static int sym53c8xx_slave_configure(struct scsi_device *sdev)
 static void sym53c8xx_slave_destroy(struct scsi_device *sdev)
 {
        struct sym_hcb *np = sym_get_hcb(sdev->host);
-       struct sym_lcb *lp = sym_lp(&np->target[sdev->id], sdev->lun);
+       struct sym_tcb *tp = &np->target[sdev->id];
+       struct sym_lcb *lp = sym_lp(tp, sdev->lun);
+       unsigned long flags;
+
+       spin_lock_irqsave(np->s.host->host_lock, flags);
+
+       if (lp->busy_itlq || lp->busy_itl) {
+               /*
+                * This really shouldn't happen, but we can't return an error
+                * so let's try to stop all on-going I/O.
+                */
+               starget_printk(KERN_WARNING, tp->starget,
+                              "Removing busy LCB (%d)\n", sdev->lun);
+               sym_reset_scsi_bus(np, 1);
+       }
 
-       if (lp->itlq_tbl)
-               sym_mfree_dma(lp->itlq_tbl, SYM_CONF_MAX_TASK * 4, "ITLQ_TBL");
-       kfree(lp->cb_tags);
-       sym_mfree_dma(lp, sizeof(*lp), "LCB");
+       if (sym_free_lcb(np, sdev->id, sdev->lun) == 0) {
+               /*
+                * It was the last unit for this target.
+                */
+               tp->head.sval        = 0;
+               tp->head.wval        = np->rv_scntl3;
+               tp->head.uval        = 0;
+               tp->tgoal.check_nego = 1;
+               tp->starget          = NULL;
+       }
+
+       spin_unlock_irqrestore(np->s.host->host_lock, flags);
 }
 
 /*
@@ -890,6 +926,8 @@ static void sym_exec_user_command (struct sym_hcb *np, struct sym_usrcmd *uc)
                        if (!((uc->target >> t) & 1))
                                continue;
                        tp = &np->target[t];
+                       if (!tp->nlcb)
+                               continue;
 
                        switch (uc->cmd) {
 
index ffa70d1ed182dcfee4e1a2fc4593db436c16ced7..69ad4945c9369467f0d2fa5095741845d8156ac8 100644 (file)
@@ -1896,6 +1896,15 @@ void sym_start_up(struct Scsi_Host *shost, int reason)
                tp->head.sval = 0;
                tp->head.wval = np->rv_scntl3;
                tp->head.uval = 0;
+               if (tp->lun0p)
+                       tp->lun0p->to_clear = 0;
+               if (tp->lunmp) {
+                       int ln;
+
+                       for (ln = 1; ln < SYM_CONF_MAX_LUN; ln++)
+                               if (tp->lunmp[ln])
+                                       tp->lunmp[ln]->to_clear = 0;
+               }
        }
 
        /*
@@ -4988,7 +4997,7 @@ struct sym_lcb *sym_alloc_lcb (struct sym_hcb *np, u_char tn, u_char ln)
         */
        if (ln && !tp->lunmp) {
                tp->lunmp = kcalloc(SYM_CONF_MAX_LUN, sizeof(struct sym_lcb *),
-                               GFP_KERNEL);
+                               GFP_ATOMIC);
                if (!tp->lunmp)
                        goto fail;
        }
@@ -5008,6 +5017,7 @@ struct sym_lcb *sym_alloc_lcb (struct sym_hcb *np, u_char tn, u_char ln)
                tp->lun0p = lp;
                tp->head.lun0_sa = cpu_to_scr(vtobus(lp));
        }
+       tp->nlcb++;
 
        /*
         *  Let the itl task point to error handling.
@@ -5084,6 +5094,43 @@ fail:
        return;
 }
 
+/*
+ *  Lun control block deallocation. Returns the number of valid remaing LCBs
+ *  for the target.
+ */
+int sym_free_lcb(struct sym_hcb *np, u_char tn, u_char ln)
+{
+       struct sym_tcb *tp = &np->target[tn];
+       struct sym_lcb *lp = sym_lp(tp, ln);
+
+       tp->nlcb--;
+
+       if (ln) {
+               if (!tp->nlcb) {
+                       kfree(tp->lunmp);
+                       sym_mfree_dma(tp->luntbl, 256, "LUNTBL");
+                       tp->lunmp = NULL;
+                       tp->luntbl = NULL;
+                       tp->head.luntbl_sa = cpu_to_scr(vtobus(np->badluntbl));
+               } else {
+                       tp->luntbl[ln] = cpu_to_scr(vtobus(&np->badlun_sa));
+                       tp->lunmp[ln] = NULL;
+               }
+       } else {
+               tp->lun0p = NULL;
+               tp->head.lun0_sa = cpu_to_scr(vtobus(&np->badlun_sa));
+       }
+
+       if (lp->itlq_tbl) {
+               sym_mfree_dma(lp->itlq_tbl, SYM_CONF_MAX_TASK*4, "ITLQ_TBL");
+               kfree(lp->cb_tags);
+       }
+
+       sym_mfree_dma(lp, sizeof(*lp), "LCB");
+
+       return tp->nlcb;
+}
+
 /*
  *  Queue a SCSI IO to the controller.
  */
index 9ebc8706b6bfc7b70fcdd884a5328130d53e65d4..053e63c86822d7c30a0d25edc0eac28a80eb5eec 100644 (file)
@@ -401,6 +401,7 @@ struct sym_tcb {
         *  An array of bus addresses is used on reselection.
         */
        u32     *luntbl;        /* LCBs bus address table       */
+       int     nlcb;           /* Number of valid LCBs (including LUN #0) */
 
        /*
         *  LUN table used by the C code.
@@ -1065,6 +1066,7 @@ int sym_clear_tasks(struct sym_hcb *np, int cam_status, int target, int lun, int
 struct sym_ccb *sym_get_ccb(struct sym_hcb *np, struct scsi_cmnd *cmd, u_char tag_order);
 void sym_free_ccb(struct sym_hcb *np, struct sym_ccb *cp);
 struct sym_lcb *sym_alloc_lcb(struct sym_hcb *np, u_char tn, u_char ln);
+int sym_free_lcb(struct sym_hcb *np, u_char tn, u_char ln);
 int sym_queue_scsiio(struct sym_hcb *np, struct scsi_cmnd *csio, struct sym_ccb *cp);
 int sym_abort_scsiio(struct sym_hcb *np, struct scsi_cmnd *ccb, int timed_out);
 int sym_reset_scsi_target(struct sym_hcb *np, int target);
index 83a185d52961e5075cd5330de7f770fc4eb28e79..957494775413006567e57de5c952d732beff3689 100644 (file)
@@ -212,7 +212,7 @@ config SPI_TXX9
 
 config SPI_XILINX
        tristate "Xilinx SPI controller"
-       depends on XILINX_VIRTEX && EXPERIMENTAL
+       depends on (XILINX_VIRTEX || MICROBLAZE) && EXPERIMENTAL
        select SPI_BITBANG
        help
          This exposes the SPI controller IP from the Xilinx EDK.
index c6c816b7ecb5d30cacf1dbfe4ca1115df3553788..5eee3f82be5d98d0cb1df531c9d78573c482ddbc 100644 (file)
@@ -22,6 +22,7 @@ config USB_ARCH_HAS_HCD
        default y if PCMCIA && !M32R                    # sl811_cs
        default y if ARM                                # SL-811
        default y if SUPERH                             # r8a66597-hcd
+       default y if MICROBLAZE
        default PCI
 
 # many non-PCI SOC chips embed OHCI
index 0048f1185a60eebb0e393390b50b944665904be8..74712cb8399a6d7c65c93ea38d2a59a9d64d54ce 100644 (file)
@@ -1996,7 +1996,7 @@ config FB_PS3_DEFAULT_SIZE_M
 
 config FB_XILINX
        tristate "Xilinx frame buffer support"
-       depends on FB && XILINX_VIRTEX
+       depends on FB && (XILINX_VIRTEX || MICROBLAZE)
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
        select FB_CFB_IMAGEBLIT
index 35e8eb02b9e90e0b008d557ed2065fcaff98f41b..e4e4d433b00701a16bac2255bd54a1a10b928188 100644 (file)
@@ -354,7 +354,7 @@ static int default_crt_on __devinitdata = 0;
 static int default_lcd_on __devinitdata = 1;
 
 #ifdef CONFIG_MTRR
-static int mtrr = 1;
+static bool mtrr = true;
 #endif
 
 #ifdef CONFIG_PMAC_BACKLIGHT
index 83c5cefc266c6f75562d36537589704a89352185..da7c01b39be248129a1a5f99274695cc510872d2 100644 (file)
@@ -1736,10 +1736,8 @@ static int __init cyber2000fb_init(void)
 
 #ifdef CONFIG_ARCH_SHARK
        err = cyberpro_vl_probe();
-       if (!err) {
+       if (!err)
                ret = 0;
-               __module_get(THIS_MODULE);
-       }
 #endif
 #ifdef CONFIG_PCI
        err = pci_register_driver(&cyberpro_driver);
@@ -1749,14 +1747,15 @@ static int __init cyber2000fb_init(void)
 
        return ret ? err : 0;
 }
+module_init(cyber2000fb_init);
 
+#ifndef CONFIG_ARCH_SHARK
 static void __exit cyberpro_exit(void)
 {
        pci_unregister_driver(&cyberpro_driver);
 }
-
-module_init(cyber2000fb_init);
 module_exit(cyberpro_exit);
+#endif
 
 MODULE_AUTHOR("Russell King");
 MODULE_DESCRIPTION("CyberPro 2000, 2010 and 5000 framebuffer driver");
index 421770b5e6abb7bcb99cf7d86a0fc5786d7ad2ea..ca5b4643a4014b2cd5c08fd68891fda9be177b36 100644 (file)
@@ -45,7 +45,7 @@ static struct fb_fix_screeninfo uvesafb_fix __devinitdata = {
 static int mtrr                __devinitdata = 3; /* enable mtrr by default */
 static int blank       = 1;               /* enable blanking by default */
 static int ypan                = 1;             /* 0: scroll, 1: ypan, 2: ywrap */
-static int pmi_setpal  __devinitdata = 1; /* use PMI for palette changes */
+static bool pmi_setpal __devinitdata = true; /* use PMI for palette changes */
 static int nocrtc      __devinitdata; /* ignore CRTC settings */
 static int noedid      __devinitdata; /* don't try DDC transfers */
 static int vram_remap  __devinitdata; /* set amt. of memory to be used */
@@ -2002,11 +2002,7 @@ static void __devexit uvesafb_exit(void)
 
 module_exit(uvesafb_exit);
 
-static int param_get_scroll(char *buffer, struct kernel_param *kp)
-{
-       return 0;
-}
-
+#define param_get_scroll NULL
 static int param_set_scroll(const char *val, struct kernel_param *kp)
 {
        ypan = 0;
@@ -2017,6 +2013,8 @@ static int param_set_scroll(const char *val, struct kernel_param *kp)
                ypan = 1;
        else if (!strcmp(val, "ywrap"))
                ypan = 2;
+       else
+               return -EINVAL;
 
        return 0;
 }
index 018c070a357f8138852a41fe4ec46d8cc4cf2f98..3a43ebf83a49d6b869339e8f94da1f097141cb07 100644 (file)
@@ -31,21 +31,37 @@ static ssize_t modalias_show(struct device *_d,
        return sprintf(buf, "virtio:d%08Xv%08X\n",
                       dev->id.device, dev->id.vendor);
 }
+static ssize_t features_show(struct device *_d,
+                            struct device_attribute *attr, char *buf)
+{
+       struct virtio_device *dev = container_of(_d, struct virtio_device, dev);
+       unsigned int i;
+       ssize_t len = 0;
+
+       /* We actually represent this as a bitstring, as it could be
+        * arbitrary length in future. */
+       for (i = 0; i < ARRAY_SIZE(dev->features)*BITS_PER_LONG; i++)
+               len += sprintf(buf+len, "%c",
+                              test_bit(i, dev->features) ? '1' : '0');
+       len += sprintf(buf+len, "\n");
+       return len;
+}
 static struct device_attribute virtio_dev_attrs[] = {
        __ATTR_RO(device),
        __ATTR_RO(vendor),
        __ATTR_RO(status),
        __ATTR_RO(modalias),
+       __ATTR_RO(features),
        __ATTR_NULL
 };
 
 static inline int virtio_id_match(const struct virtio_device *dev,
                                  const struct virtio_device_id *id)
 {
-       if (id->device != dev->id.device)
+       if (id->device != dev->id.device && id->device != VIRTIO_DEV_ANY_ID)
                return 0;
 
-       return id->vendor == VIRTIO_DEV_ANY_ID || id->vendor != dev->id.vendor;
+       return id->vendor == VIRTIO_DEV_ANY_ID || id->vendor == dev->id.vendor;
 }
 
 /* This looks through all the IDs a driver claims to support.  If any of them
@@ -118,13 +134,14 @@ static int virtio_dev_probe(struct device *_d)
                if (device_features & (1 << i))
                        set_bit(i, dev->features);
 
+       dev->config->finalize_features(dev);
+
        err = drv->probe(dev);
        if (err)
                add_status(dev, VIRTIO_CONFIG_S_FAILED);
-       else {
-               dev->config->finalize_features(dev);
+       else
                add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK);
-       }
+
        return err;
 }
 
@@ -185,6 +202,8 @@ int register_virtio_device(struct virtio_device *dev)
        /* Acknowledge that we've seen the device. */
        add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE);
 
+       INIT_LIST_HEAD(&dev->vqs);
+
        /* device_register() causes the bus infrastructure to look for a
         * matching driver. */
        err = device_register(&dev->dev);
index 9c76a061a04dc7235ad689cea438fc8d0c6cfedf..26b278264796b31a4b98403d728b1e9ccfd8257b 100644 (file)
@@ -204,6 +204,9 @@ static int balloon(void *_vballoon)
 static int virtballoon_probe(struct virtio_device *vdev)
 {
        struct virtio_balloon *vb;
+       struct virtqueue *vqs[2];
+       vq_callback_t *callbacks[] = { balloon_ack, balloon_ack };
+       const char *names[] = { "inflate", "deflate" };
        int err;
 
        vdev->priv = vb = kmalloc(sizeof(*vb), GFP_KERNEL);
@@ -218,22 +221,17 @@ static int virtballoon_probe(struct virtio_device *vdev)
        vb->vdev = vdev;
 
        /* We expect two virtqueues. */
-       vb->inflate_vq = vdev->config->find_vq(vdev, 0, balloon_ack);
-       if (IS_ERR(vb->inflate_vq)) {
-               err = PTR_ERR(vb->inflate_vq);
+       err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names);
+       if (err)
                goto out_free_vb;
-       }
 
-       vb->deflate_vq = vdev->config->find_vq(vdev, 1, balloon_ack);
-       if (IS_ERR(vb->deflate_vq)) {
-               err = PTR_ERR(vb->deflate_vq);
-               goto out_del_inflate_vq;
-       }
+       vb->inflate_vq = vqs[0];
+       vb->deflate_vq = vqs[1];
 
        vb->thread = kthread_run(balloon, vb, "vballoon");
        if (IS_ERR(vb->thread)) {
                err = PTR_ERR(vb->thread);
-               goto out_del_deflate_vq;
+               goto out_del_vqs;
        }
 
        vb->tell_host_first
@@ -241,10 +239,8 @@ static int virtballoon_probe(struct virtio_device *vdev)
 
        return 0;
 
-out_del_deflate_vq:
-       vdev->config->del_vq(vb->deflate_vq);
-out_del_inflate_vq:
-       vdev->config->del_vq(vb->inflate_vq);
+out_del_vqs:
+       vdev->config->del_vqs(vdev);
 out_free_vb:
        kfree(vb);
 out:
@@ -264,8 +260,7 @@ static void virtballoon_remove(struct virtio_device *vdev)
        /* Now we reset the device so we can clean up the queues. */
        vdev->config->reset(vdev);
 
-       vdev->config->del_vq(vb->deflate_vq);
-       vdev->config->del_vq(vb->inflate_vq);
+       vdev->config->del_vqs(vdev);
        kfree(vb);
 }
 
index 330aacbdec1f7b48fd2774f19e5788461463f07a..193c8f0e5cc5708a8e1077c0a60bc04c3eaafd2d 100644 (file)
@@ -42,6 +42,26 @@ struct virtio_pci_device
        /* a list of queues so we can dispatch IRQs */
        spinlock_t lock;
        struct list_head virtqueues;
+
+       /* MSI-X support */
+       int msix_enabled;
+       int intx_enabled;
+       struct msix_entry *msix_entries;
+       /* Name strings for interrupts. This size should be enough,
+        * and I'm too lazy to allocate each name separately. */
+       char (*msix_names)[256];
+       /* Number of available vectors */
+       unsigned msix_vectors;
+       /* Vectors allocated */
+       unsigned msix_used_vectors;
+};
+
+/* Constants for MSI-X */
+/* Use first vector for configuration changes, second and the rest for
+ * virtqueues Thus, we need at least 2 vectors for MSI. */
+enum {
+       VP_MSIX_CONFIG_VECTOR = 0,
+       VP_MSIX_VQ_VECTOR = 1,
 };
 
 struct virtio_pci_vq_info
@@ -60,6 +80,9 @@ struct virtio_pci_vq_info
 
        /* the list node for the virtqueues list */
        struct list_head node;
+
+       /* MSI-X vector (or none) */
+       unsigned vector;
 };
 
 /* Qumranet donated their vendor ID for devices 0x1000 thru 0x10FF. */
@@ -109,7 +132,8 @@ static void vp_get(struct virtio_device *vdev, unsigned offset,
                   void *buf, unsigned len)
 {
        struct virtio_pci_device *vp_dev = to_vp_device(vdev);
-       void __iomem *ioaddr = vp_dev->ioaddr + VIRTIO_PCI_CONFIG + offset;
+       void __iomem *ioaddr = vp_dev->ioaddr +
+                               VIRTIO_PCI_CONFIG(vp_dev) + offset;
        u8 *ptr = buf;
        int i;
 
@@ -123,7 +147,8 @@ static void vp_set(struct virtio_device *vdev, unsigned offset,
                   const void *buf, unsigned len)
 {
        struct virtio_pci_device *vp_dev = to_vp_device(vdev);
-       void __iomem *ioaddr = vp_dev->ioaddr + VIRTIO_PCI_CONFIG + offset;
+       void __iomem *ioaddr = vp_dev->ioaddr +
+                               VIRTIO_PCI_CONFIG(vp_dev) + offset;
        const u8 *ptr = buf;
        int i;
 
@@ -164,6 +189,37 @@ static void vp_notify(struct virtqueue *vq)
        iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_NOTIFY);
 }
 
+/* Handle a configuration change: Tell driver if it wants to know. */
+static irqreturn_t vp_config_changed(int irq, void *opaque)
+{
+       struct virtio_pci_device *vp_dev = opaque;
+       struct virtio_driver *drv;
+       drv = container_of(vp_dev->vdev.dev.driver,
+                          struct virtio_driver, driver);
+
+       if (drv && drv->config_changed)
+               drv->config_changed(&vp_dev->vdev);
+       return IRQ_HANDLED;
+}
+
+/* Notify all virtqueues on an interrupt. */
+static irqreturn_t vp_vring_interrupt(int irq, void *opaque)
+{
+       struct virtio_pci_device *vp_dev = opaque;
+       struct virtio_pci_vq_info *info;
+       irqreturn_t ret = IRQ_NONE;
+       unsigned long flags;
+
+       spin_lock_irqsave(&vp_dev->lock, flags);
+       list_for_each_entry(info, &vp_dev->virtqueues, node) {
+               if (vring_interrupt(irq, info->vq) == IRQ_HANDLED)
+                       ret = IRQ_HANDLED;
+       }
+       spin_unlock_irqrestore(&vp_dev->lock, flags);
+
+       return ret;
+}
+
 /* A small wrapper to also acknowledge the interrupt when it's handled.
  * I really need an EIO hook for the vring so I can ack the interrupt once we
  * know that we'll be handling the IRQ but before we invoke the callback since
@@ -173,9 +229,6 @@ static void vp_notify(struct virtqueue *vq)
 static irqreturn_t vp_interrupt(int irq, void *opaque)
 {
        struct virtio_pci_device *vp_dev = opaque;
-       struct virtio_pci_vq_info *info;
-       irqreturn_t ret = IRQ_NONE;
-       unsigned long flags;
        u8 isr;
 
        /* reading the ISR has the effect of also clearing it so it's very
@@ -187,34 +240,137 @@ static irqreturn_t vp_interrupt(int irq, void *opaque)
                return IRQ_NONE;
 
        /* Configuration change?  Tell driver if it wants to know. */
-       if (isr & VIRTIO_PCI_ISR_CONFIG) {
-               struct virtio_driver *drv;
-               drv = container_of(vp_dev->vdev.dev.driver,
-                                  struct virtio_driver, driver);
+       if (isr & VIRTIO_PCI_ISR_CONFIG)
+               vp_config_changed(irq, opaque);
 
-               if (drv && drv->config_changed)
-                       drv->config_changed(&vp_dev->vdev);
+       return vp_vring_interrupt(irq, opaque);
+}
+
+static void vp_free_vectors(struct virtio_device *vdev)
+{
+       struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+       int i;
+
+       if (vp_dev->intx_enabled) {
+               free_irq(vp_dev->pci_dev->irq, vp_dev);
+               vp_dev->intx_enabled = 0;
        }
 
-       spin_lock_irqsave(&vp_dev->lock, flags);
-       list_for_each_entry(info, &vp_dev->virtqueues, node) {
-               if (vring_interrupt(irq, info->vq) == IRQ_HANDLED)
-                       ret = IRQ_HANDLED;
+       for (i = 0; i < vp_dev->msix_used_vectors; ++i)
+               free_irq(vp_dev->msix_entries[i].vector, vp_dev);
+       vp_dev->msix_used_vectors = 0;
+
+       if (vp_dev->msix_enabled) {
+               /* Disable the vector used for configuration */
+               iowrite16(VIRTIO_MSI_NO_VECTOR,
+                         vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR);
+               /* Flush the write out to device */
+               ioread16(vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR);
+
+               vp_dev->msix_enabled = 0;
+               pci_disable_msix(vp_dev->pci_dev);
        }
-       spin_unlock_irqrestore(&vp_dev->lock, flags);
+}
 
-       return ret;
+static int vp_enable_msix(struct pci_dev *dev, struct msix_entry *entries,
+                         int *options, int noptions)
+{
+       int i;
+       for (i = 0; i < noptions; ++i)
+               if (!pci_enable_msix(dev, entries, options[i]))
+                       return options[i];
+       return -EBUSY;
+}
+
+static int vp_request_vectors(struct virtio_device *vdev, unsigned max_vqs)
+{
+       struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+       const char *name = dev_name(&vp_dev->vdev.dev);
+       unsigned i, v;
+       int err = -ENOMEM;
+       /* We want at most one vector per queue and one for config changes.
+        * Fallback to separate vectors for config and a shared for queues.
+        * Finally fall back to regular interrupts. */
+       int options[] = { max_vqs + 1, 2 };
+       int nvectors = max(options[0], options[1]);
+
+       vp_dev->msix_entries = kmalloc(nvectors * sizeof *vp_dev->msix_entries,
+                                      GFP_KERNEL);
+       if (!vp_dev->msix_entries)
+               goto error_entries;
+       vp_dev->msix_names = kmalloc(nvectors * sizeof *vp_dev->msix_names,
+                                    GFP_KERNEL);
+       if (!vp_dev->msix_names)
+               goto error_names;
+
+       for (i = 0; i < nvectors; ++i)
+               vp_dev->msix_entries[i].entry = i;
+
+       err = vp_enable_msix(vp_dev->pci_dev, vp_dev->msix_entries,
+                            options, ARRAY_SIZE(options));
+       if (err < 0) {
+               /* Can't allocate enough MSI-X vectors, use regular interrupt */
+               vp_dev->msix_vectors = 0;
+               err = request_irq(vp_dev->pci_dev->irq, vp_interrupt,
+                                 IRQF_SHARED, name, vp_dev);
+               if (err)
+                       goto error_irq;
+               vp_dev->intx_enabled = 1;
+       } else {
+               vp_dev->msix_vectors = err;
+               vp_dev->msix_enabled = 1;
+
+               /* Set the vector used for configuration */
+               v = vp_dev->msix_used_vectors;
+               snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names,
+                        "%s-config", name);
+               err = request_irq(vp_dev->msix_entries[v].vector,
+                                 vp_config_changed, 0, vp_dev->msix_names[v],
+                                 vp_dev);
+               if (err)
+                       goto error_irq;
+               ++vp_dev->msix_used_vectors;
+
+               iowrite16(v, vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR);
+               /* Verify we had enough resources to assign the vector */
+               v = ioread16(vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR);
+               if (v == VIRTIO_MSI_NO_VECTOR) {
+                       err = -EBUSY;
+                       goto error_irq;
+               }
+       }
+
+       if (vp_dev->msix_vectors && vp_dev->msix_vectors != max_vqs + 1) {
+               /* Shared vector for all VQs */
+               v = vp_dev->msix_used_vectors;
+               snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names,
+                        "%s-virtqueues", name);
+               err = request_irq(vp_dev->msix_entries[v].vector,
+                                 vp_vring_interrupt, 0, vp_dev->msix_names[v],
+                                 vp_dev);
+               if (err)
+                       goto error_irq;
+               ++vp_dev->msix_used_vectors;
+       }
+       return 0;
+error_irq:
+       vp_free_vectors(vdev);
+       kfree(vp_dev->msix_names);
+error_names:
+       kfree(vp_dev->msix_entries);
+error_entries:
+       return err;
 }
 
-/* the config->find_vq() implementation */
 static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
-                                   void (*callback)(struct virtqueue *vq))
+                                   void (*callback)(struct virtqueue *vq),
+                                   const char *name)
 {
        struct virtio_pci_device *vp_dev = to_vp_device(vdev);
        struct virtio_pci_vq_info *info;
        struct virtqueue *vq;
        unsigned long flags, size;
-       u16 num;
+       u16 num, vector;
        int err;
 
        /* Select the queue we're interested in */
@@ -233,6 +389,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
 
        info->queue_index = index;
        info->num = num;
+       info->vector = VIRTIO_MSI_NO_VECTOR;
 
        size = PAGE_ALIGN(vring_size(num, VIRTIO_PCI_VRING_ALIGN));
        info->queue = alloc_pages_exact(size, GFP_KERNEL|__GFP_ZERO);
@@ -247,7 +404,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
 
        /* create the vring */
        vq = vring_new_virtqueue(info->num, VIRTIO_PCI_VRING_ALIGN,
-                                vdev, info->queue, vp_notify, callback);
+                                vdev, info->queue, vp_notify, callback, name);
        if (!vq) {
                err = -ENOMEM;
                goto out_activate_queue;
@@ -256,12 +413,43 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
        vq->priv = info;
        info->vq = vq;
 
+       /* allocate per-vq vector if available and necessary */
+       if (callback && vp_dev->msix_used_vectors < vp_dev->msix_vectors) {
+               vector = vp_dev->msix_used_vectors;
+               snprintf(vp_dev->msix_names[vector], sizeof *vp_dev->msix_names,
+                        "%s-%s", dev_name(&vp_dev->vdev.dev), name);
+               err = request_irq(vp_dev->msix_entries[vector].vector,
+                                 vring_interrupt, 0,
+                                 vp_dev->msix_names[vector], vq);
+               if (err)
+                       goto out_request_irq;
+               info->vector = vector;
+               ++vp_dev->msix_used_vectors;
+       } else
+               vector = VP_MSIX_VQ_VECTOR;
+
+        if (callback && vp_dev->msix_enabled) {
+               iowrite16(vector, vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR);
+               vector = ioread16(vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR);
+               if (vector == VIRTIO_MSI_NO_VECTOR) {
+                       err = -EBUSY;
+                       goto out_assign;
+               }
+       }
+
        spin_lock_irqsave(&vp_dev->lock, flags);
        list_add(&info->node, &vp_dev->virtqueues);
        spin_unlock_irqrestore(&vp_dev->lock, flags);
 
        return vq;
 
+out_assign:
+       if (info->vector != VIRTIO_MSI_NO_VECTOR) {
+               free_irq(vp_dev->msix_entries[info->vector].vector, vq);
+               --vp_dev->msix_used_vectors;
+       }
+out_request_irq:
+       vring_del_virtqueue(vq);
 out_activate_queue:
        iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
        free_pages_exact(info->queue, size);
@@ -270,21 +458,27 @@ out_info:
        return ERR_PTR(err);
 }
 
-/* the config->del_vq() implementation */
 static void vp_del_vq(struct virtqueue *vq)
 {
        struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev);
        struct virtio_pci_vq_info *info = vq->priv;
-       unsigned long flags, size;
+       unsigned long size;
 
-       spin_lock_irqsave(&vp_dev->lock, flags);
-       list_del(&info->node);
-       spin_unlock_irqrestore(&vp_dev->lock, flags);
+       iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
+
+       if (info->vector != VIRTIO_MSI_NO_VECTOR)
+               free_irq(vp_dev->msix_entries[info->vector].vector, vq);
+
+       if (vp_dev->msix_enabled) {
+               iowrite16(VIRTIO_MSI_NO_VECTOR,
+                         vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR);
+               /* Flush the write out to device */
+               ioread8(vp_dev->ioaddr + VIRTIO_PCI_ISR);
+       }
 
        vring_del_virtqueue(vq);
 
        /* Select and deactivate the queue */
-       iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
        iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
 
        size = PAGE_ALIGN(vring_size(info->num, VIRTIO_PCI_VRING_ALIGN));
@@ -292,14 +486,57 @@ static void vp_del_vq(struct virtqueue *vq)
        kfree(info);
 }
 
+/* the config->del_vqs() implementation */
+static void vp_del_vqs(struct virtio_device *vdev)
+{
+       struct virtqueue *vq, *n;
+
+       list_for_each_entry_safe(vq, n, &vdev->vqs, list)
+               vp_del_vq(vq);
+
+       vp_free_vectors(vdev);
+}
+
+/* the config->find_vqs() implementation */
+static int vp_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+                      struct virtqueue *vqs[],
+                      vq_callback_t *callbacks[],
+                      const char *names[])
+{
+       int vectors = 0;
+       int i, err;
+
+       /* How many vectors would we like? */
+       for (i = 0; i < nvqs; ++i)
+               if (callbacks[i])
+                       ++vectors;
+
+       err = vp_request_vectors(vdev, vectors);
+       if (err)
+               goto error_request;
+
+       for (i = 0; i < nvqs; ++i) {
+               vqs[i] = vp_find_vq(vdev, i, callbacks[i], names[i]);
+               if (IS_ERR(vqs[i]))
+                       goto error_find;
+       }
+       return 0;
+
+error_find:
+       vp_del_vqs(vdev);
+
+error_request:
+       return PTR_ERR(vqs[i]);
+}
+
 static struct virtio_config_ops virtio_pci_config_ops = {
        .get            = vp_get,
        .set            = vp_set,
        .get_status     = vp_get_status,
        .set_status     = vp_set_status,
        .reset          = vp_reset,
-       .find_vq        = vp_find_vq,
-       .del_vq         = vp_del_vq,
+       .find_vqs       = vp_find_vqs,
+       .del_vqs        = vp_del_vqs,
        .get_features   = vp_get_features,
        .finalize_features = vp_finalize_features,
 };
@@ -310,7 +547,7 @@ static void virtio_pci_release_dev(struct device *_d)
        struct virtio_pci_device *vp_dev = to_vp_device(dev);
        struct pci_dev *pci_dev = vp_dev->pci_dev;
 
-       free_irq(pci_dev->irq, vp_dev);
+       vp_del_vqs(dev);
        pci_set_drvdata(pci_dev, NULL);
        pci_iounmap(pci_dev, vp_dev->ioaddr);
        pci_release_regions(pci_dev);
@@ -369,21 +606,13 @@ static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
        vp_dev->vdev.id.vendor = pci_dev->subsystem_vendor;
        vp_dev->vdev.id.device = pci_dev->subsystem_device;
 
-       /* register a handler for the queue with the PCI device's interrupt */
-       err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, IRQF_SHARED,
-                         dev_name(&vp_dev->vdev.dev), vp_dev);
-       if (err)
-               goto out_set_drvdata;
-
        /* finally register the virtio device */
        err = register_virtio_device(&vp_dev->vdev);
        if (err)
-               goto out_req_irq;
+               goto out_set_drvdata;
 
        return 0;
 
-out_req_irq:
-       free_irq(pci_dev->irq, vp_dev);
 out_set_drvdata:
        pci_set_drvdata(pci_dev, NULL);
        pci_iounmap(pci_dev, vp_dev->ioaddr);
index 5c52369ab9bb81115376ec3e8b959908bdbe8d8f..a882f2606515796334e403baa411ebcbe45c592a 100644 (file)
 
 #ifdef DEBUG
 /* For development, we want to crash whenever the ring is screwed. */
-#define BAD_RING(_vq, fmt...)                  \
-       do { dev_err(&(_vq)->vq.vdev->dev, fmt); BUG(); } while(0)
+#define BAD_RING(_vq, fmt, args...)                            \
+       do {                                                    \
+               dev_err(&(_vq)->vq.vdev->dev,                   \
+                       "%s:"fmt, (_vq)->vq.name, ##args);      \
+               BUG();                                          \
+       } while (0)
 /* Caller is supposed to guarantee no reentry. */
 #define START_USE(_vq)                                         \
        do {                                                    \
                if ((_vq)->in_use)                              \
-                       panic("in_use = %i\n", (_vq)->in_use);  \
+                       panic("%s:in_use = %i\n",               \
+                             (_vq)->vq.name, (_vq)->in_use);   \
                (_vq)->in_use = __LINE__;                       \
                mb();                                           \
-       } while(0)
+       } while (0)
 #define END_USE(_vq) \
        do { BUG_ON(!(_vq)->in_use); (_vq)->in_use = 0; mb(); } while(0)
 #else
-#define BAD_RING(_vq, fmt...)                  \
-       do { dev_err(&_vq->vq.vdev->dev, fmt); (_vq)->broken = true; } while(0)
+#define BAD_RING(_vq, fmt, args...)                            \
+       do {                                                    \
+               dev_err(&_vq->vq.vdev->dev,                     \
+                       "%s:"fmt, (_vq)->vq.name, ##args);      \
+               (_vq)->broken = true;                           \
+       } while (0)
 #define START_USE(vq)
 #define END_USE(vq)
 #endif
@@ -52,6 +61,9 @@ struct vring_virtqueue
        /* Other side has made a mess, don't try any more. */
        bool broken;
 
+       /* Host supports indirect buffers */
+       bool indirect;
+
        /* Number of free buffers */
        unsigned int num_free;
        /* Head of free buffer list. */
@@ -76,6 +88,55 @@ struct vring_virtqueue
 
 #define to_vvq(_vq) container_of(_vq, struct vring_virtqueue, vq)
 
+/* Set up an indirect table of descriptors and add it to the queue. */
+static int vring_add_indirect(struct vring_virtqueue *vq,
+                             struct scatterlist sg[],
+                             unsigned int out,
+                             unsigned int in)
+{
+       struct vring_desc *desc;
+       unsigned head;
+       int i;
+
+       desc = kmalloc((out + in) * sizeof(struct vring_desc), GFP_ATOMIC);
+       if (!desc)
+               return vq->vring.num;
+
+       /* Transfer entries from the sg list into the indirect page */
+       for (i = 0; i < out; i++) {
+               desc[i].flags = VRING_DESC_F_NEXT;
+               desc[i].addr = sg_phys(sg);
+               desc[i].len = sg->length;
+               desc[i].next = i+1;
+               sg++;
+       }
+       for (; i < (out + in); i++) {
+               desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE;
+               desc[i].addr = sg_phys(sg);
+               desc[i].len = sg->length;
+               desc[i].next = i+1;
+               sg++;
+       }
+
+       /* Last one doesn't continue. */
+       desc[i-1].flags &= ~VRING_DESC_F_NEXT;
+       desc[i-1].next = 0;
+
+       /* We're about to use a buffer */
+       vq->num_free--;
+
+       /* Use a single buffer which doesn't continue */
+       head = vq->free_head;
+       vq->vring.desc[head].flags = VRING_DESC_F_INDIRECT;
+       vq->vring.desc[head].addr = virt_to_phys(desc);
+       vq->vring.desc[head].len = i * sizeof(struct vring_desc);
+
+       /* Update free pointer */
+       vq->free_head = vq->vring.desc[head].next;
+
+       return head;
+}
+
 static int vring_add_buf(struct virtqueue *_vq,
                         struct scatterlist sg[],
                         unsigned int out,
@@ -85,12 +146,21 @@ static int vring_add_buf(struct virtqueue *_vq,
        struct vring_virtqueue *vq = to_vvq(_vq);
        unsigned int i, avail, head, uninitialized_var(prev);
 
+       START_USE(vq);
+
        BUG_ON(data == NULL);
+
+       /* If the host supports indirect descriptor tables, and we have multiple
+        * buffers, then go indirect. FIXME: tune this threshold */
+       if (vq->indirect && (out + in) > 1 && vq->num_free) {
+               head = vring_add_indirect(vq, sg, out, in);
+               if (head != vq->vring.num)
+                       goto add_head;
+       }
+
        BUG_ON(out + in > vq->vring.num);
        BUG_ON(out + in == 0);
 
-       START_USE(vq);
-
        if (vq->num_free < out + in) {
                pr_debug("Can't add buf len %i - avail = %i\n",
                         out + in, vq->num_free);
@@ -127,6 +197,7 @@ static int vring_add_buf(struct virtqueue *_vq,
        /* Update free pointer */
        vq->free_head = i;
 
+add_head:
        /* Set token. */
        vq->data[head] = data;
 
@@ -170,6 +241,11 @@ static void detach_buf(struct vring_virtqueue *vq, unsigned int head)
 
        /* Put back on free list: find end */
        i = head;
+
+       /* Free the indirect table */
+       if (vq->vring.desc[i].flags & VRING_DESC_F_INDIRECT)
+               kfree(phys_to_virt(vq->vring.desc[i].addr));
+
        while (vq->vring.desc[i].flags & VRING_DESC_F_NEXT) {
                i = vq->vring.desc[i].next;
                vq->num_free++;
@@ -284,7 +360,8 @@ struct virtqueue *vring_new_virtqueue(unsigned int num,
                                      struct virtio_device *vdev,
                                      void *pages,
                                      void (*notify)(struct virtqueue *),
-                                     void (*callback)(struct virtqueue *))
+                                     void (*callback)(struct virtqueue *),
+                                     const char *name)
 {
        struct vring_virtqueue *vq;
        unsigned int i;
@@ -303,14 +380,18 @@ struct virtqueue *vring_new_virtqueue(unsigned int num,
        vq->vq.callback = callback;
        vq->vq.vdev = vdev;
        vq->vq.vq_ops = &vring_vq_ops;
+       vq->vq.name = name;
        vq->notify = notify;
        vq->broken = false;
        vq->last_used_idx = 0;
        vq->num_added = 0;
+       list_add_tail(&vq->vq.list, &vdev->vqs);
 #ifdef DEBUG
        vq->in_use = false;
 #endif
 
+       vq->indirect = virtio_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC);
+
        /* No callback?  Tell other side not to bother us. */
        if (!callback)
                vq->vring.avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
@@ -327,6 +408,7 @@ EXPORT_SYMBOL_GPL(vring_new_virtqueue);
 
 void vring_del_virtqueue(struct virtqueue *vq)
 {
+       list_del(&vq->list);
        kfree(to_vvq(vq));
 }
 EXPORT_SYMBOL_GPL(vring_del_virtqueue);
@@ -338,6 +420,8 @@ void vring_transport_features(struct virtio_device *vdev)
 
        for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++) {
                switch (i) {
+               case VIRTIO_RING_F_INDIRECT_DESC:
+                       break;
                default:
                        /* We don't understand this bit. */
                        clear_bit(i, vdev->features);
index fddc2025dece777c513a7a7a72f8537751bf1834..10d03d7931c47236a42e2e7ac158ac7564308354 100644 (file)
@@ -43,7 +43,7 @@ static int xen_suspend(void *data)
        if (err) {
                printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
                        err);
-               device_power_up(PMSG_RESUME);
+               dpm_resume_noirq(PMSG_RESUME);
                return err;
        }
 
@@ -69,7 +69,7 @@ static int xen_suspend(void *data)
        }
 
        sysdev_resume();
-       device_power_up(PMSG_RESUME);
+       dpm_resume_noirq(PMSG_RESUME);
 
        return 0;
 }
@@ -92,18 +92,18 @@ static void do_suspend(void)
        }
 #endif
 
-       err = device_suspend(PMSG_SUSPEND);
+       err = dpm_suspend_start(PMSG_SUSPEND);
        if (err) {
-               printk(KERN_ERR "xen suspend: device_suspend %d\n", err);
+               printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
                goto out;
        }
 
        printk(KERN_DEBUG "suspending xenstore...\n");
        xs_suspend();
 
-       err = device_power_down(PMSG_SUSPEND);
+       err = dpm_suspend_noirq(PMSG_SUSPEND);
        if (err) {
-               printk(KERN_ERR "device_power_down failed: %d\n", err);
+               printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
                goto resume_devices;
        }
 
@@ -119,10 +119,10 @@ static void do_suspend(void)
        } else
                xs_suspend_cancel();
 
-       device_power_up(PMSG_RESUME);
+       dpm_resume_noirq(PMSG_RESUME);
 
 resume_devices:
-       device_resume(PMSG_RESUME);
+       dpm_resume_end(PMSG_RESUME);
 
        /* Make sure timer events get retriggered on all CPUs */
        clock_was_set();
index 9f7270f36b2af4e207b26ff52f9b98ad502d8c00..525da2e8f73be1da26f67be2c1630b6f20518ac0 100644 (file)
@@ -62,6 +62,16 @@ source "fs/autofs/Kconfig"
 source "fs/autofs4/Kconfig"
 source "fs/fuse/Kconfig"
 
+config CUSE
+       tristate "Character device in Userpace support"
+       depends on FUSE_FS
+       help
+         This FUSE extension allows character devices to be
+         implemented in userspace.
+
+         If you want to develop or use userspace character device
+         based on CUSE, answer Y or M.
+
 config GENERIC_ACL
        bool
        select FS_POSIX_ACL
index 858fba14aaa60712944aa2ef67102b3acc5cb071..c4dfa1dcc86f32d43469f5122c22ade5516544a5 100644 (file)
@@ -49,7 +49,8 @@ static struct dlm_direntry *get_free_de(struct dlm_ls *ls, int len)
        spin_unlock(&ls->ls_recover_list_lock);
 
        if (!found)
-               de = kzalloc(sizeof(struct dlm_direntry) + len, GFP_KERNEL);
+               de = kzalloc(sizeof(struct dlm_direntry) + len,
+                            ls->ls_allocation);
        return de;
 }
 
@@ -211,7 +212,7 @@ int dlm_recover_directory(struct dlm_ls *ls)
 
        dlm_dir_clear(ls);
 
-       last_name = kmalloc(DLM_RESNAME_MAXLEN, GFP_KERNEL);
+       last_name = kmalloc(DLM_RESNAME_MAXLEN, ls->ls_allocation);
        if (!last_name)
                goto out;
 
@@ -322,7 +323,7 @@ static int get_entry(struct dlm_ls *ls, int nodeid, char *name,
        if (namelen > DLM_RESNAME_MAXLEN)
                return -EINVAL;
 
-       de = kzalloc(sizeof(struct dlm_direntry) + namelen, GFP_KERNEL);
+       de = kzalloc(sizeof(struct dlm_direntry) + namelen, ls->ls_allocation);
        if (!de)
                return -ENOMEM;
 
index cd8e2df3c295f19dbe09141e6222ff4cd9c69a56..d489fcc86713de1d0484902d6e7f911b57377cd0 100644 (file)
@@ -384,7 +384,7 @@ static void threads_stop(void)
        dlm_astd_stop();
 }
 
-static int new_lockspace(char *name, int namelen, void **lockspace,
+static int new_lockspace(const char *name, int namelen, void **lockspace,
                         uint32_t flags, int lvblen)
 {
        struct dlm_ls *ls;
@@ -419,16 +419,14 @@ static int new_lockspace(char *name, int namelen, void **lockspace,
                        break;
                }
                ls->ls_create_count++;
-               module_put(THIS_MODULE);
-               error = 1; /* not an error, return 0 */
+               *lockspace = ls;
+               error = 1;
                break;
        }
        spin_unlock(&lslist_lock);
 
-       if (error < 0)
-               goto out;
        if (error)
-               goto ret_zero;
+               goto out;
 
        error = -ENOMEM;
 
@@ -583,7 +581,6 @@ static int new_lockspace(char *name, int namelen, void **lockspace,
        dlm_create_debug_file(ls);
 
        log_debug(ls, "join complete");
- ret_zero:
        *lockspace = ls;
        return 0;
 
@@ -614,7 +611,7 @@ static int new_lockspace(char *name, int namelen, void **lockspace,
        return error;
 }
 
-int dlm_new_lockspace(char *name, int namelen, void **lockspace,
+int dlm_new_lockspace(const char *name, int namelen, void **lockspace,
                      uint32_t flags, int lvblen)
 {
        int error = 0;
@@ -628,7 +625,9 @@ int dlm_new_lockspace(char *name, int namelen, void **lockspace,
        error = new_lockspace(name, namelen, lockspace, flags, lvblen);
        if (!error)
                ls_count++;
-       else if (!ls_count)
+       if (error > 0)
+               error = 0;
+       if (!ls_count)
                threads_stop();
  out:
        mutex_unlock(&ls_lock);
index 609108a83267f3cfffd9d539f29496c0e5ffc1b1..cdb580a9c7a28352e903fa660c848826966b9dc1 100644 (file)
@@ -309,6 +309,20 @@ static void lowcomms_state_change(struct sock *sk)
                lowcomms_write_space(sk);
 }
 
+int dlm_lowcomms_connect_node(int nodeid)
+{
+       struct connection *con;
+
+       if (nodeid == dlm_our_nodeid())
+               return 0;
+
+       con = nodeid2con(nodeid, GFP_NOFS);
+       if (!con)
+               return -ENOMEM;
+       lowcomms_connect_sock(con);
+       return 0;
+}
+
 /* Make a socket active */
 static int add_sock(struct socket *sock, struct connection *con)
 {
@@ -486,7 +500,7 @@ static void process_sctp_notification(struct connection *con,
                                return;
                        }
 
-                       new_con = nodeid2con(nodeid, GFP_KERNEL);
+                       new_con = nodeid2con(nodeid, GFP_NOFS);
                        if (!new_con)
                                return;
 
@@ -722,7 +736,7 @@ static int tcp_accept_from_sock(struct connection *con)
         *  the same time and the connections cross on the wire.
         *  In this case we store the incoming one in "othercon"
         */
-       newcon = nodeid2con(nodeid, GFP_KERNEL);
+       newcon = nodeid2con(nodeid, GFP_NOFS);
        if (!newcon) {
                result = -ENOMEM;
                goto accept_err;
@@ -732,7 +746,7 @@ static int tcp_accept_from_sock(struct connection *con)
                struct connection *othercon = newcon->othercon;
 
                if (!othercon) {
-                       othercon = kmem_cache_zalloc(con_cache, GFP_KERNEL);
+                       othercon = kmem_cache_zalloc(con_cache, GFP_NOFS);
                        if (!othercon) {
                                log_print("failed to allocate incoming socket");
                                mutex_unlock(&newcon->sock_mutex);
@@ -1421,7 +1435,7 @@ static int work_start(void)
 static void stop_conn(struct connection *con)
 {
        con->flags |= 0x0F;
-       if (con->sock)
+       if (con->sock && con->sock->sk)
                con->sock->sk->sk_user_data = NULL;
 }
 
index a9a9618c0d3f2bcea8063a7217a7e2e036ed76fe..1311e6426287b61eed336c5f17f0cd1a67eab77f 100644 (file)
@@ -2,7 +2,7 @@
 *******************************************************************************
 **
 **  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
-**  Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
+**  Copyright (C) 2004-2009 Red Hat, Inc.  All rights reserved.
 **
 **  This copyrighted material is made available to anyone wishing to use,
 **  modify, copy, or redistribute it subject to the terms and conditions
@@ -19,6 +19,7 @@ void dlm_lowcomms_stop(void);
 int dlm_lowcomms_close(int nodeid);
 void *dlm_lowcomms_get_buffer(int nodeid, int len, gfp_t allocation, char **ppc);
 void dlm_lowcomms_commit_buffer(void *mh);
+int dlm_lowcomms_connect_node(int nodeid);
 
 #endif                         /* __LOWCOMMS_DOT_H__ */
 
index 26133f05ae3a7690911815358ad1f350343564b6..b128775913b29d6b43aeeec34910af6f9583ce9a 100644 (file)
@@ -1,7 +1,7 @@
 /******************************************************************************
 *******************************************************************************
 **
-**  Copyright (C) 2005-2008 Red Hat, Inc.  All rights reserved.
+**  Copyright (C) 2005-2009 Red Hat, Inc.  All rights reserved.
 **
 **  This copyrighted material is made available to anyone wishing to use,
 **  modify, copy, or redistribute it subject to the terms and conditions
@@ -17,6 +17,7 @@
 #include "recover.h"
 #include "rcom.h"
 #include "config.h"
+#include "lowcomms.h"
 
 static void add_ordered_member(struct dlm_ls *ls, struct dlm_member *new)
 {
@@ -45,9 +46,9 @@ static void add_ordered_member(struct dlm_ls *ls, struct dlm_member *new)
 static int dlm_add_member(struct dlm_ls *ls, int nodeid)
 {
        struct dlm_member *memb;
-       int w;
+       int w, error;
 
-       memb = kzalloc(sizeof(struct dlm_member), GFP_KERNEL);
+       memb = kzalloc(sizeof(struct dlm_member), ls->ls_allocation);
        if (!memb)
                return -ENOMEM;
 
@@ -57,6 +58,12 @@ static int dlm_add_member(struct dlm_ls *ls, int nodeid)
                return w;
        }
 
+       error = dlm_lowcomms_connect_node(nodeid);
+       if (error < 0) {
+               kfree(memb);
+               return error;
+       }
+
        memb->nodeid = nodeid;
        memb->weight = w;
        add_ordered_member(ls, memb);
@@ -136,7 +143,7 @@ static void make_member_array(struct dlm_ls *ls)
 
        ls->ls_total_weight = total;
 
-       array = kmalloc(sizeof(int) * total, GFP_KERNEL);
+       array = kmalloc(sizeof(int) * total, ls->ls_allocation);
        if (!array)
                return;
 
@@ -219,7 +226,7 @@ int dlm_recover_members(struct dlm_ls *ls, struct dlm_recover *rv, int *neg_out)
                        continue;
                log_debug(ls, "new nodeid %d is a re-added member", rv->new[i]);
 
-               memb = kzalloc(sizeof(struct dlm_member), GFP_KERNEL);
+               memb = kzalloc(sizeof(struct dlm_member), ls->ls_allocation);
                if (!memb)
                        return -ENOMEM;
                memb->nodeid = rv->new[i];
@@ -334,7 +341,7 @@ int dlm_ls_start(struct dlm_ls *ls)
        int *ids = NULL, *new = NULL;
        int error, ids_count = 0, new_count = 0;
 
-       rv = kzalloc(sizeof(struct dlm_recover), GFP_KERNEL);
+       rv = kzalloc(sizeof(struct dlm_recover), ls->ls_allocation);
        if (!rv)
                return -ENOMEM;
 
index daa4183fbb8489ea759b439aec402092cd7dc0a6..7a2307c08911101e9f82b99e0c6bad22c03a6e24 100644 (file)
@@ -35,7 +35,7 @@ void dlm_add_requestqueue(struct dlm_ls *ls, int nodeid, struct dlm_message *ms)
        struct rq_entry *e;
        int length = ms->m_header.h_length - sizeof(struct dlm_message);
 
-       e = kmalloc(sizeof(struct rq_entry) + length, GFP_KERNEL);
+       e = kmalloc(sizeof(struct rq_entry) + length, ls->ls_allocation);
        if (!e) {
                log_print("dlm_add_requestqueue: out of memory len %d", length);
                return;
index 2a701d593d35f36d2511c25df916a5d61098b019..3f0e1974abdcfbfb8cd8afff3a19346667768672 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/anon_inodes.h>
 #include <linux/eventfd.h>
 #include <linux/syscalls.h>
+#include <linux/module.h>
 
 struct eventfd_ctx {
        wait_queue_head_t wqh;
@@ -56,6 +57,7 @@ int eventfd_signal(struct file *file, int n)
 
        return n;
 }
+EXPORT_SYMBOL_GPL(eventfd_signal);
 
 static int eventfd_release(struct inode *inode, struct file *file)
 {
@@ -197,6 +199,7 @@ struct file *eventfd_fget(int fd)
 
        return file;
 }
+EXPORT_SYMBOL_GPL(eventfd_fget);
 
 SYSCALL_DEFINE2(eventfd2, unsigned int, count, int, flags)
 {
index b1512c4bb8c71d076dd5d27e1b7f08cda00c2e75..24667eedc02329554735f72420cbc04d99faa871 100644 (file)
@@ -175,10 +175,4 @@ int exofs_async_op(struct osd_request *or,
 
 int extract_attr_from_req(struct osd_request *or, struct osd_attr *attr);
 
-int osd_req_read_kern(struct osd_request *or,
-       const struct osd_obj_id *obj, u64 offset, void *buff, u64 len);
-
-int osd_req_write_kern(struct osd_request *or,
-       const struct osd_obj_id *obj, u64 offset, void *buff, u64 len);
-
 #endif /*ifndef __EXOFS_COM_H__*/
index ba8d9fab46932b26394331bb4d93ab60ba2736e5..77d0a295eb1cd3e95443da014fc9801cf64f5aff 100644 (file)
@@ -59,10 +59,9 @@ static void _pcol_init(struct page_collect *pcol, unsigned expected_pages,
                struct inode *inode)
 {
        struct exofs_sb_info *sbi = inode->i_sb->s_fs_info;
-       struct request_queue *req_q = sbi->s_dev->scsi_device->request_queue;
 
        pcol->sbi = sbi;
-       pcol->req_q = req_q;
+       pcol->req_q = osd_request_queue(sbi->s_dev);
        pcol->inode = inode;
        pcol->expected_pages = expected_pages;
 
@@ -266,7 +265,7 @@ static int read_exec(struct page_collect *pcol, bool is_sync)
                goto err;
        }
 
-       osd_req_read(or, &obj, pcol->bio, i_start);
+       osd_req_read(or, &obj, i_start, pcol->bio, pcol->length);
 
        if (is_sync) {
                exofs_sync_op(or, pcol->sbi->s_timeout, oi->i_cred);
@@ -522,7 +521,8 @@ static int write_exec(struct page_collect *pcol)
 
        *pcol_copy = *pcol;
 
-       osd_req_write(or, &obj, pcol_copy->bio, i_start);
+       pcol_copy->bio->bi_rw |= (1 << BIO_RW); /* FIXME: bio_set_dir() */
+       osd_req_write(or, &obj, i_start, pcol_copy->bio, pcol_copy->length);
        ret = exofs_async_op(or, writepages_done, pcol_copy, oi->i_cred);
        if (unlikely(ret)) {
                EXOFS_ERR("write_exec: exofs_async_op() Faild\n");
index 06ca92672eb5d6118ee644074019a650a96125f3..b3d2ccb87aaa8981374e824066bfd2aa608fa86a 100644 (file)
@@ -125,29 +125,3 @@ int extract_attr_from_req(struct osd_request *or, struct osd_attr *attr)
 
        return -EIO;
 }
-
-int osd_req_read_kern(struct osd_request *or,
-       const struct osd_obj_id *obj, u64 offset, void* buff, u64 len)
-{
-       struct request_queue *req_q = or->osd_dev->scsi_device->request_queue;
-       struct bio *bio = bio_map_kern(req_q, buff, len, GFP_KERNEL);
-
-       if (!bio)
-               return -ENOMEM;
-
-       osd_req_read(or, obj, bio, offset);
-       return 0;
-}
-
-int osd_req_write_kern(struct osd_request *or,
-       const struct osd_obj_id *obj, u64 offset, void* buff, u64 len)
-{
-       struct request_queue *req_q = or->osd_dev->scsi_device->request_queue;
-       struct bio *bio = bio_map_kern(req_q, buff, len, GFP_KERNEL);
-
-       if (!bio)
-               return -ENOMEM;
-
-       osd_req_write(or, obj, bio, offset);
-       return 0;
-}
index 72437065f6adee64994145be01efd281c482596a..e95eeb445e587fb09967a877822657dc7fd6d3c5 100644 (file)
@@ -3,5 +3,6 @@
 #
 
 obj-$(CONFIG_FUSE_FS) += fuse.o
+obj-$(CONFIG_CUSE) += cuse.o
 
 fuse-objs := dev.o dir.o file.o inode.o control.o
diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c
new file mode 100644 (file)
index 0000000..de792dc
--- /dev/null
@@ -0,0 +1,610 @@
+/*
+ * CUSE: Character device in Userspace
+ *
+ * Copyright (C) 2008-2009  SUSE Linux Products GmbH
+ * Copyright (C) 2008-2009  Tejun Heo <tj@kernel.org>
+ *
+ * This file is released under the GPLv2.
+ *
+ * CUSE enables character devices to be implemented from userland much
+ * like FUSE allows filesystems.  On initialization /dev/cuse is
+ * created.  By opening the file and replying to the CUSE_INIT request
+ * userland CUSE server can create a character device.  After that the
+ * operation is very similar to FUSE.
+ *
+ * A CUSE instance involves the following objects.
+ *
+ * cuse_conn   : contains fuse_conn and serves as bonding structure
+ * channel     : file handle connected to the userland CUSE server
+ * cdev                : the implemented character device
+ * dev         : generic device for cdev
+ *
+ * Note that 'channel' is what 'dev' is in FUSE.  As CUSE deals with
+ * devices, it's called 'channel' to reduce confusion.
+ *
+ * channel determines when the character device dies.  When channel is
+ * closed, everything begins to destruct.  The cuse_conn is taken off
+ * the lookup table preventing further access from cdev, cdev and
+ * generic device are removed and the base reference of cuse_conn is
+ * put.
+ *
+ * On each open, the matching cuse_conn is looked up and if found an
+ * additional reference is taken which is released when the file is
+ * closed.
+ */
+
+#include <linux/fuse.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/kdev_t.h>
+#include <linux/kthread.h>
+#include <linux/list.h>
+#include <linux/magic.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+
+#include "fuse_i.h"
+
+#define CUSE_CONNTBL_LEN       64
+
+struct cuse_conn {
+       struct list_head        list;   /* linked on cuse_conntbl */
+       struct fuse_conn        fc;     /* fuse connection */
+       struct cdev             *cdev;  /* associated character device */
+       struct device           *dev;   /* device representing @cdev */
+
+       /* init parameters, set once during initialization */
+       bool                    unrestricted_ioctl;
+};
+
+static DEFINE_SPINLOCK(cuse_lock);             /* protects cuse_conntbl */
+static struct list_head cuse_conntbl[CUSE_CONNTBL_LEN];
+static struct class *cuse_class;
+
+static struct cuse_conn *fc_to_cc(struct fuse_conn *fc)
+{
+       return container_of(fc, struct cuse_conn, fc);
+}
+
+static struct list_head *cuse_conntbl_head(dev_t devt)
+{
+       return &cuse_conntbl[(MAJOR(devt) + MINOR(devt)) % CUSE_CONNTBL_LEN];
+}
+
+
+/**************************************************************************
+ * CUSE frontend operations
+ *
+ * These are file operations for the character device.
+ *
+ * On open, CUSE opens a file from the FUSE mnt and stores it to
+ * private_data of the open file.  All other ops call FUSE ops on the
+ * FUSE file.
+ */
+
+static ssize_t cuse_read(struct file *file, char __user *buf, size_t count,
+                        loff_t *ppos)
+{
+       loff_t pos = 0;
+
+       return fuse_direct_io(file, buf, count, &pos, 0);
+}
+
+static ssize_t cuse_write(struct file *file, const char __user *buf,
+                         size_t count, loff_t *ppos)
+{
+       loff_t pos = 0;
+       /*
+        * No locking or generic_write_checks(), the server is
+        * responsible for locking and sanity checks.
+        */
+       return fuse_direct_io(file, buf, count, &pos, 1);
+}
+
+static int cuse_open(struct inode *inode, struct file *file)
+{
+       dev_t devt = inode->i_cdev->dev;
+       struct cuse_conn *cc = NULL, *pos;
+       int rc;
+
+       /* look up and get the connection */
+       spin_lock(&cuse_lock);
+       list_for_each_entry(pos, cuse_conntbl_head(devt), list)
+               if (pos->dev->devt == devt) {
+                       fuse_conn_get(&pos->fc);
+                       cc = pos;
+                       break;
+               }
+       spin_unlock(&cuse_lock);
+
+       /* dead? */
+       if (!cc)
+               return -ENODEV;
+
+       /*
+        * Generic permission check is already done against the chrdev
+        * file, proceed to open.
+        */
+       rc = fuse_do_open(&cc->fc, 0, file, 0);
+       if (rc)
+               fuse_conn_put(&cc->fc);
+       return rc;
+}
+
+static int cuse_release(struct inode *inode, struct file *file)
+{
+       struct fuse_file *ff = file->private_data;
+       struct fuse_conn *fc = ff->fc;
+
+       fuse_sync_release(ff, file->f_flags);
+       fuse_conn_put(fc);
+
+       return 0;
+}
+
+static long cuse_file_ioctl(struct file *file, unsigned int cmd,
+                           unsigned long arg)
+{
+       struct fuse_file *ff = file->private_data;
+       struct cuse_conn *cc = fc_to_cc(ff->fc);
+       unsigned int flags = 0;
+
+       if (cc->unrestricted_ioctl)
+               flags |= FUSE_IOCTL_UNRESTRICTED;
+
+       return fuse_do_ioctl(file, cmd, arg, flags);
+}
+
+static long cuse_file_compat_ioctl(struct file *file, unsigned int cmd,
+                                  unsigned long arg)
+{
+       struct fuse_file *ff = file->private_data;
+       struct cuse_conn *cc = fc_to_cc(ff->fc);
+       unsigned int flags = FUSE_IOCTL_COMPAT;
+
+       if (cc->unrestricted_ioctl)
+               flags |= FUSE_IOCTL_UNRESTRICTED;
+
+       return fuse_do_ioctl(file, cmd, arg, flags);
+}
+
+static const struct file_operations cuse_frontend_fops = {
+       .owner                  = THIS_MODULE,
+       .read                   = cuse_read,
+       .write                  = cuse_write,
+       .open                   = cuse_open,
+       .release                = cuse_release,
+       .unlocked_ioctl         = cuse_file_ioctl,
+       .compat_ioctl           = cuse_file_compat_ioctl,
+       .poll                   = fuse_file_poll,
+};
+
+
+/**************************************************************************
+ * CUSE channel initialization and destruction
+ */
+
+struct cuse_devinfo {
+       const char              *name;
+};
+
+/**
+ * cuse_parse_one - parse one key=value pair
+ * @pp: i/o parameter for the current position
+ * @end: points to one past the end of the packed string
+ * @keyp: out parameter for key
+ * @valp: out parameter for value
+ *
+ * *@pp points to packed strings - "key0=val0\0key1=val1\0" which ends
+ * at @end - 1.  This function parses one pair and set *@keyp to the
+ * start of the key and *@valp to the start of the value.  Note that
+ * the original string is modified such that the key string is
+ * terminated with '\0'.  *@pp is updated to point to the next string.
+ *
+ * RETURNS:
+ * 1 on successful parse, 0 on EOF, -errno on failure.
+ */
+static int cuse_parse_one(char **pp, char *end, char **keyp, char **valp)
+{
+       char *p = *pp;
+       char *key, *val;
+
+       while (p < end && *p == '\0')
+               p++;
+       if (p == end)
+               return 0;
+
+       if (end[-1] != '\0') {
+               printk(KERN_ERR "CUSE: info not properly terminated\n");
+               return -EINVAL;
+       }
+
+       key = val = p;
+       p += strlen(p);
+
+       if (valp) {
+               strsep(&val, "=");
+               if (!val)
+                       val = key + strlen(key);
+               key = strstrip(key);
+               val = strstrip(val);
+       } else
+               key = strstrip(key);
+
+       if (!strlen(key)) {
+               printk(KERN_ERR "CUSE: zero length info key specified\n");
+               return -EINVAL;
+       }
+
+       *pp = p;
+       *keyp = key;
+       if (valp)
+               *valp = val;
+
+       return 1;
+}
+
+/**
+ * cuse_parse_dev_info - parse device info
+ * @p: device info string
+ * @len: length of device info string
+ * @devinfo: out parameter for parsed device info
+ *
+ * Parse @p to extract device info and store it into @devinfo.  String
+ * pointed to by @p is modified by parsing and @devinfo points into
+ * them, so @p shouldn't be freed while @devinfo is in use.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+static int cuse_parse_devinfo(char *p, size_t len, struct cuse_devinfo *devinfo)
+{
+       char *end = p + len;
+       char *key, *val;
+       int rc;
+
+       while (true) {
+               rc = cuse_parse_one(&p, end, &key, &val);
+               if (rc < 0)
+                       return rc;
+               if (!rc)
+                       break;
+               if (strcmp(key, "DEVNAME") == 0)
+                       devinfo->name = val;
+               else
+                       printk(KERN_WARNING "CUSE: unknown device info \"%s\"\n",
+                              key);
+       }
+
+       if (!devinfo->name || !strlen(devinfo->name)) {
+               printk(KERN_ERR "CUSE: DEVNAME unspecified\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void cuse_gendev_release(struct device *dev)
+{
+       kfree(dev);
+}
+
+/**
+ * cuse_process_init_reply - finish initializing CUSE channel
+ *
+ * This function creates the character device and sets up all the
+ * required data structures for it.  Please read the comment at the
+ * top of this file for high level overview.
+ */
+static void cuse_process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
+{
+       struct cuse_conn *cc = fc_to_cc(fc);
+       struct cuse_init_out *arg = &req->misc.cuse_init_out;
+       struct page *page = req->pages[0];
+       struct cuse_devinfo devinfo = { };
+       struct device *dev;
+       struct cdev *cdev;
+       dev_t devt;
+       int rc;
+
+       if (req->out.h.error ||
+           arg->major != FUSE_KERNEL_VERSION || arg->minor < 11) {
+               goto err;
+       }
+
+       fc->minor = arg->minor;
+       fc->max_read = max_t(unsigned, arg->max_read, 4096);
+       fc->max_write = max_t(unsigned, arg->max_write, 4096);
+
+       /* parse init reply */
+       cc->unrestricted_ioctl = arg->flags & CUSE_UNRESTRICTED_IOCTL;
+
+       rc = cuse_parse_devinfo(page_address(page), req->out.args[1].size,
+                               &devinfo);
+       if (rc)
+               goto err;
+
+       /* determine and reserve devt */
+       devt = MKDEV(arg->dev_major, arg->dev_minor);
+       if (!MAJOR(devt))
+               rc = alloc_chrdev_region(&devt, MINOR(devt), 1, devinfo.name);
+       else
+               rc = register_chrdev_region(devt, 1, devinfo.name);
+       if (rc) {
+               printk(KERN_ERR "CUSE: failed to register chrdev region\n");
+               goto err;
+       }
+
+       /* devt determined, create device */
+       rc = -ENOMEM;
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               goto err_region;
+
+       device_initialize(dev);
+       dev_set_uevent_suppress(dev, 1);
+       dev->class = cuse_class;
+       dev->devt = devt;
+       dev->release = cuse_gendev_release;
+       dev_set_drvdata(dev, cc);
+       dev_set_name(dev, "%s", devinfo.name);
+
+       rc = device_add(dev);
+       if (rc)
+               goto err_device;
+
+       /* register cdev */
+       rc = -ENOMEM;
+       cdev = cdev_alloc();
+       if (!cdev)
+               goto err_device;
+
+       cdev->owner = THIS_MODULE;
+       cdev->ops = &cuse_frontend_fops;
+
+       rc = cdev_add(cdev, devt, 1);
+       if (rc)
+               goto err_cdev;
+
+       cc->dev = dev;
+       cc->cdev = cdev;
+
+       /* make the device available */
+       spin_lock(&cuse_lock);
+       list_add(&cc->list, cuse_conntbl_head(devt));
+       spin_unlock(&cuse_lock);
+
+       /* announce device availability */
+       dev_set_uevent_suppress(dev, 0);
+       kobject_uevent(&dev->kobj, KOBJ_ADD);
+out:
+       __free_page(page);
+       return;
+
+err_cdev:
+       cdev_del(cdev);
+err_device:
+       put_device(dev);
+err_region:
+       unregister_chrdev_region(devt, 1);
+err:
+       fc->conn_error = 1;
+       goto out;
+}
+
+static int cuse_send_init(struct cuse_conn *cc)
+{
+       int rc;
+       struct fuse_req *req;
+       struct page *page;
+       struct fuse_conn *fc = &cc->fc;
+       struct cuse_init_in *arg;
+
+       BUILD_BUG_ON(CUSE_INIT_INFO_MAX > PAGE_SIZE);
+
+       req = fuse_get_req(fc);
+       if (IS_ERR(req)) {
+               rc = PTR_ERR(req);
+               goto err;
+       }
+
+       rc = -ENOMEM;
+       page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+       if (!page)
+               goto err_put_req;
+
+       arg = &req->misc.cuse_init_in;
+       arg->major = FUSE_KERNEL_VERSION;
+       arg->minor = FUSE_KERNEL_MINOR_VERSION;
+       arg->flags |= CUSE_UNRESTRICTED_IOCTL;
+       req->in.h.opcode = CUSE_INIT;
+       req->in.numargs = 1;
+       req->in.args[0].size = sizeof(struct cuse_init_in);
+       req->in.args[0].value = arg;
+       req->out.numargs = 2;
+       req->out.args[0].size = sizeof(struct cuse_init_out);
+       req->out.args[0].value = &req->misc.cuse_init_out;
+       req->out.args[1].size = CUSE_INIT_INFO_MAX;
+       req->out.argvar = 1;
+       req->out.argpages = 1;
+       req->pages[0] = page;
+       req->num_pages = 1;
+       req->end = cuse_process_init_reply;
+       fuse_request_send_background(fc, req);
+
+       return 0;
+
+err_put_req:
+       fuse_put_request(fc, req);
+err:
+       return rc;
+}
+
+static void cuse_fc_release(struct fuse_conn *fc)
+{
+       struct cuse_conn *cc = fc_to_cc(fc);
+       kfree(cc);
+}
+
+/**
+ * cuse_channel_open - open method for /dev/cuse
+ * @inode: inode for /dev/cuse
+ * @file: file struct being opened
+ *
+ * Userland CUSE server can create a CUSE device by opening /dev/cuse
+ * and replying to the initilaization request kernel sends.  This
+ * function is responsible for handling CUSE device initialization.
+ * Because the fd opened by this function is used during
+ * initialization, this function only creates cuse_conn and sends
+ * init.  The rest is delegated to a kthread.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+static int cuse_channel_open(struct inode *inode, struct file *file)
+{
+       struct cuse_conn *cc;
+       int rc;
+
+       /* set up cuse_conn */
+       cc = kzalloc(sizeof(*cc), GFP_KERNEL);
+       if (!cc)
+               return -ENOMEM;
+
+       fuse_conn_init(&cc->fc);
+
+       INIT_LIST_HEAD(&cc->list);
+       cc->fc.release = cuse_fc_release;
+
+       cc->fc.connected = 1;
+       cc->fc.blocked = 0;
+       rc = cuse_send_init(cc);
+       if (rc) {
+               fuse_conn_put(&cc->fc);
+               return rc;
+       }
+       file->private_data = &cc->fc;   /* channel owns base reference to cc */
+
+       return 0;
+}
+
+/**
+ * cuse_channel_release - release method for /dev/cuse
+ * @inode: inode for /dev/cuse
+ * @file: file struct being closed
+ *
+ * Disconnect the channel, deregister CUSE device and initiate
+ * destruction by putting the default reference.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+static int cuse_channel_release(struct inode *inode, struct file *file)
+{
+       struct cuse_conn *cc = fc_to_cc(file->private_data);
+       int rc;
+
+       /* remove from the conntbl, no more access from this point on */
+       spin_lock(&cuse_lock);
+       list_del_init(&cc->list);
+       spin_unlock(&cuse_lock);
+
+       /* remove device */
+       if (cc->dev)
+               device_unregister(cc->dev);
+       if (cc->cdev) {
+               unregister_chrdev_region(cc->cdev->dev, 1);
+               cdev_del(cc->cdev);
+       }
+
+       /* kill connection and shutdown channel */
+       fuse_conn_kill(&cc->fc);
+       rc = fuse_dev_release(inode, file);     /* puts the base reference */
+
+       return rc;
+}
+
+static struct file_operations cuse_channel_fops; /* initialized during init */
+
+
+/**************************************************************************
+ * Misc stuff and module initializatiion
+ *
+ * CUSE exports the same set of attributes to sysfs as fusectl.
+ */
+
+static ssize_t cuse_class_waiting_show(struct device *dev,
+                                      struct device_attribute *attr, char *buf)
+{
+       struct cuse_conn *cc = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", atomic_read(&cc->fc.num_waiting));
+}
+
+static ssize_t cuse_class_abort_store(struct device *dev,
+                                     struct device_attribute *attr,
+                                     const char *buf, size_t count)
+{
+       struct cuse_conn *cc = dev_get_drvdata(dev);
+
+       fuse_abort_conn(&cc->fc);
+       return count;
+}
+
+static struct device_attribute cuse_class_dev_attrs[] = {
+       __ATTR(waiting, S_IFREG | 0400, cuse_class_waiting_show, NULL),
+       __ATTR(abort, S_IFREG | 0200, NULL, cuse_class_abort_store),
+       { }
+};
+
+static struct miscdevice cuse_miscdev = {
+       .minor          = MISC_DYNAMIC_MINOR,
+       .name           = "cuse",
+       .fops           = &cuse_channel_fops,
+};
+
+static int __init cuse_init(void)
+{
+       int i, rc;
+
+       /* init conntbl */
+       for (i = 0; i < CUSE_CONNTBL_LEN; i++)
+               INIT_LIST_HEAD(&cuse_conntbl[i]);
+
+       /* inherit and extend fuse_dev_operations */
+       cuse_channel_fops               = fuse_dev_operations;
+       cuse_channel_fops.owner         = THIS_MODULE;
+       cuse_channel_fops.open          = cuse_channel_open;
+       cuse_channel_fops.release       = cuse_channel_release;
+
+       cuse_class = class_create(THIS_MODULE, "cuse");
+       if (IS_ERR(cuse_class))
+               return PTR_ERR(cuse_class);
+
+       cuse_class->dev_attrs = cuse_class_dev_attrs;
+
+       rc = misc_register(&cuse_miscdev);
+       if (rc) {
+               class_destroy(cuse_class);
+               return rc;
+       }
+
+       return 0;
+}
+
+static void __exit cuse_exit(void)
+{
+       misc_deregister(&cuse_miscdev);
+       class_destroy(cuse_class);
+}
+
+module_init(cuse_init);
+module_exit(cuse_exit);
+
+MODULE_AUTHOR("Tejun Heo <tj@kernel.org>");
+MODULE_DESCRIPTION("Character device in Userspace");
+MODULE_LICENSE("GPL");
index ba76b68c52ffe2d127178d71ca5b5ab2ecc6a353..8fed2ed12f38b3ab468a042964ae800479f18d18 100644 (file)
@@ -46,6 +46,7 @@ struct fuse_req *fuse_request_alloc(void)
                fuse_request_init(req);
        return req;
 }
+EXPORT_SYMBOL_GPL(fuse_request_alloc);
 
 struct fuse_req *fuse_request_alloc_nofs(void)
 {
@@ -124,6 +125,7 @@ struct fuse_req *fuse_get_req(struct fuse_conn *fc)
        atomic_dec(&fc->num_waiting);
        return ERR_PTR(err);
 }
+EXPORT_SYMBOL_GPL(fuse_get_req);
 
 /*
  * Return request in fuse_file->reserved_req.  However that may
@@ -208,6 +210,7 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
                        fuse_request_free(req);
        }
 }
+EXPORT_SYMBOL_GPL(fuse_put_request);
 
 static unsigned len_args(unsigned numargs, struct fuse_arg *args)
 {
@@ -282,7 +285,7 @@ __releases(&fc->lock)
                        wake_up_all(&fc->blocked_waitq);
                }
                if (fc->num_background == FUSE_CONGESTION_THRESHOLD &&
-                   fc->connected) {
+                   fc->connected && fc->bdi_initialized) {
                        clear_bdi_congested(&fc->bdi, READ);
                        clear_bdi_congested(&fc->bdi, WRITE);
                }
@@ -400,6 +403,7 @@ void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
        }
        spin_unlock(&fc->lock);
 }
+EXPORT_SYMBOL_GPL(fuse_request_send);
 
 static void fuse_request_send_nowait_locked(struct fuse_conn *fc,
                                            struct fuse_req *req)
@@ -408,7 +412,8 @@ static void fuse_request_send_nowait_locked(struct fuse_conn *fc,
        fc->num_background++;
        if (fc->num_background == FUSE_MAX_BACKGROUND)
                fc->blocked = 1;
-       if (fc->num_background == FUSE_CONGESTION_THRESHOLD) {
+       if (fc->num_background == FUSE_CONGESTION_THRESHOLD &&
+           fc->bdi_initialized) {
                set_bdi_congested(&fc->bdi, READ);
                set_bdi_congested(&fc->bdi, WRITE);
        }
@@ -439,6 +444,7 @@ void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req)
        req->isreply = 1;
        fuse_request_send_nowait(fc, req);
 }
+EXPORT_SYMBOL_GPL(fuse_request_send_background);
 
 /*
  * Called under fc->lock
@@ -1105,8 +1111,9 @@ void fuse_abort_conn(struct fuse_conn *fc)
        }
        spin_unlock(&fc->lock);
 }
+EXPORT_SYMBOL_GPL(fuse_abort_conn);
 
-static int fuse_dev_release(struct inode *inode, struct file *file)
+int fuse_dev_release(struct inode *inode, struct file *file)
 {
        struct fuse_conn *fc = fuse_get_conn(file);
        if (fc) {
@@ -1120,6 +1127,7 @@ static int fuse_dev_release(struct inode *inode, struct file *file)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(fuse_dev_release);
 
 static int fuse_dev_fasync(int fd, struct file *file, int on)
 {
@@ -1142,6 +1150,7 @@ const struct file_operations fuse_dev_operations = {
        .release        = fuse_dev_release,
        .fasync         = fuse_dev_fasync,
 };
+EXPORT_SYMBOL_GPL(fuse_dev_operations);
 
 static struct miscdevice fuse_miscdevice = {
        .minor = FUSE_MINOR,
index 8b8eebc5614bb1e1f6dcba5ecd0ad1966d831f07..b3089a083d30c36a2afa17c03ca1345cc6d38206 100644 (file)
@@ -361,19 +361,6 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
        return ERR_PTR(err);
 }
 
-/*
- * Synchronous release for the case when something goes wrong in CREATE_OPEN
- */
-static void fuse_sync_release(struct fuse_conn *fc, struct fuse_file *ff,
-                             u64 nodeid, int flags)
-{
-       fuse_release_fill(ff, nodeid, flags, FUSE_RELEASE);
-       ff->reserved_req->force = 1;
-       fuse_request_send(fc, ff->reserved_req);
-       fuse_put_request(fc, ff->reserved_req);
-       kfree(ff);
-}
-
 /*
  * Atomic create+open operation
  *
@@ -445,12 +432,14 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
                goto out_free_ff;
 
        fuse_put_request(fc, req);
+       ff->fh = outopen.fh;
+       ff->nodeid = outentry.nodeid;
+       ff->open_flags = outopen.open_flags;
        inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
                          &outentry.attr, entry_attr_timeout(&outentry), 0);
        if (!inode) {
                flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
-               ff->fh = outopen.fh;
-               fuse_sync_release(fc, ff, outentry.nodeid, flags);
+               fuse_sync_release(ff, flags);
                fuse_send_forget(fc, forget_req, outentry.nodeid, 1);
                return -ENOMEM;
        }
@@ -460,11 +449,11 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
        fuse_invalidate_attr(dir);
        file = lookup_instantiate_filp(nd, entry, generic_file_open);
        if (IS_ERR(file)) {
-               ff->fh = outopen.fh;
-               fuse_sync_release(fc, ff, outentry.nodeid, flags);
+               fuse_sync_release(ff, flags);
                return PTR_ERR(file);
        }
-       fuse_finish_open(inode, file, ff, &outopen);
+       file->private_data = fuse_file_get(ff);
+       fuse_finish_open(inode, file);
        return 0;
 
  out_free_ff:
@@ -1035,7 +1024,7 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
        req->out.argpages = 1;
        req->num_pages = 1;
        req->pages[0] = page;
-       fuse_read_fill(req, file, inode, file->f_pos, PAGE_SIZE, FUSE_READDIR);
+       fuse_read_fill(req, file, file->f_pos, PAGE_SIZE, FUSE_READDIR);
        fuse_request_send(fc, req);
        nbytes = req->out.args[0].size;
        err = req->out.h.error;
@@ -1101,12 +1090,14 @@ static void fuse_put_link(struct dentry *dentry, struct nameidata *nd, void *c)
 
 static int fuse_dir_open(struct inode *inode, struct file *file)
 {
-       return fuse_open_common(inode, file, 1);
+       return fuse_open_common(inode, file, true);
 }
 
 static int fuse_dir_release(struct inode *inode, struct file *file)
 {
-       return fuse_release_common(inode, file, 1);
+       fuse_release_common(file, FUSE_RELEASEDIR);
+
+       return 0;
 }
 
 static int fuse_dir_fsync(struct file *file, struct dentry *de, int datasync)
index 06f30e965676e00d9b781eaf9d666686c2a3f214..fce6ce694fdee3c4d6764c1dff661de755de675d 100644 (file)
 #include <linux/slab.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/module.h>
 
 static const struct file_operations fuse_direct_io_file_operations;
 
-static int fuse_send_open(struct inode *inode, struct file *file, int isdir,
-                         struct fuse_open_out *outargp)
+static int fuse_send_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
+                         int opcode, struct fuse_open_out *outargp)
 {
-       struct fuse_conn *fc = get_fuse_conn(inode);
        struct fuse_open_in inarg;
        struct fuse_req *req;
        int err;
@@ -31,8 +31,8 @@ static int fuse_send_open(struct inode *inode, struct file *file, int isdir,
        inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY);
        if (!fc->atomic_o_trunc)
                inarg.flags &= ~O_TRUNC;
-       req->in.h.opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN;
-       req->in.h.nodeid = get_node_id(inode);
+       req->in.h.opcode = opcode;
+       req->in.h.nodeid = nodeid;
        req->in.numargs = 1;
        req->in.args[0].size = sizeof(inarg);
        req->in.args[0].value = &inarg;
@@ -49,22 +49,27 @@ static int fuse_send_open(struct inode *inode, struct file *file, int isdir,
 struct fuse_file *fuse_file_alloc(struct fuse_conn *fc)
 {
        struct fuse_file *ff;
+
        ff = kmalloc(sizeof(struct fuse_file), GFP_KERNEL);
-       if (ff) {
-               ff->reserved_req = fuse_request_alloc();
-               if (!ff->reserved_req) {
-                       kfree(ff);
-                       return NULL;
-               } else {
-                       INIT_LIST_HEAD(&ff->write_entry);
-                       atomic_set(&ff->count, 0);
-                       spin_lock(&fc->lock);
-                       ff->kh = ++fc->khctr;
-                       spin_unlock(&fc->lock);
-               }
-               RB_CLEAR_NODE(&ff->polled_node);
-               init_waitqueue_head(&ff->poll_wait);
+       if (unlikely(!ff))
+               return NULL;
+
+       ff->fc = fc;
+       ff->reserved_req = fuse_request_alloc();
+       if (unlikely(!ff->reserved_req)) {
+               kfree(ff);
+               return NULL;
        }
+
+       INIT_LIST_HEAD(&ff->write_entry);
+       atomic_set(&ff->count, 0);
+       RB_CLEAR_NODE(&ff->polled_node);
+       init_waitqueue_head(&ff->poll_wait);
+
+       spin_lock(&fc->lock);
+       ff->kh = ++fc->khctr;
+       spin_unlock(&fc->lock);
+
        return ff;
 }
 
@@ -74,7 +79,7 @@ void fuse_file_free(struct fuse_file *ff)
        kfree(ff);
 }
 
-static struct fuse_file *fuse_file_get(struct fuse_file *ff)
+struct fuse_file *fuse_file_get(struct fuse_file *ff)
 {
        atomic_inc(&ff->count);
        return ff;
@@ -82,40 +87,65 @@ static struct fuse_file *fuse_file_get(struct fuse_file *ff)
 
 static void fuse_release_end(struct fuse_conn *fc, struct fuse_req *req)
 {
-       dput(req->misc.release.dentry);
-       mntput(req->misc.release.vfsmount);
+       path_put(&req->misc.release.path);
 }
 
 static void fuse_file_put(struct fuse_file *ff)
 {
        if (atomic_dec_and_test(&ff->count)) {
                struct fuse_req *req = ff->reserved_req;
-               struct inode *inode = req->misc.release.dentry->d_inode;
-               struct fuse_conn *fc = get_fuse_conn(inode);
+
                req->end = fuse_release_end;
-               fuse_request_send_background(fc, req);
+               fuse_request_send_background(ff->fc, req);
                kfree(ff);
        }
 }
 
-void fuse_finish_open(struct inode *inode, struct file *file,
-                     struct fuse_file *ff, struct fuse_open_out *outarg)
+int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
+                bool isdir)
 {
-       if (outarg->open_flags & FOPEN_DIRECT_IO)
+       struct fuse_open_out outarg;
+       struct fuse_file *ff;
+       int err;
+       int opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN;
+
+       ff = fuse_file_alloc(fc);
+       if (!ff)
+               return -ENOMEM;
+
+       err = fuse_send_open(fc, nodeid, file, opcode, &outarg);
+       if (err) {
+               fuse_file_free(ff);
+               return err;
+       }
+
+       if (isdir)
+               outarg.open_flags &= ~FOPEN_DIRECT_IO;
+
+       ff->fh = outarg.fh;
+       ff->nodeid = nodeid;
+       ff->open_flags = outarg.open_flags;
+       file->private_data = fuse_file_get(ff);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(fuse_do_open);
+
+void fuse_finish_open(struct inode *inode, struct file *file)
+{
+       struct fuse_file *ff = file->private_data;
+
+       if (ff->open_flags & FOPEN_DIRECT_IO)
                file->f_op = &fuse_direct_io_file_operations;
-       if (!(outarg->open_flags & FOPEN_KEEP_CACHE))
+       if (!(ff->open_flags & FOPEN_KEEP_CACHE))
                invalidate_inode_pages2(inode->i_mapping);
-       if (outarg->open_flags & FOPEN_NONSEEKABLE)
+       if (ff->open_flags & FOPEN_NONSEEKABLE)
                nonseekable_open(inode, file);
-       ff->fh = outarg->fh;
-       file->private_data = fuse_file_get(ff);
 }
 
-int fuse_open_common(struct inode *inode, struct file *file, int isdir)
+int fuse_open_common(struct inode *inode, struct file *file, bool isdir)
 {
        struct fuse_conn *fc = get_fuse_conn(inode);
-       struct fuse_open_out outarg;
-       struct fuse_file *ff;
        int err;
 
        /* VFS checks this, but only _after_ ->open() */
@@ -126,78 +156,85 @@ int fuse_open_common(struct inode *inode, struct file *file, int isdir)
        if (err)
                return err;
 
-       ff = fuse_file_alloc(fc);
-       if (!ff)
-               return -ENOMEM;
-
-       err = fuse_send_open(inode, file, isdir, &outarg);
+       err = fuse_do_open(fc, get_node_id(inode), file, isdir);
        if (err)
-               fuse_file_free(ff);
-       else {
-               if (isdir)
-                       outarg.open_flags &= ~FOPEN_DIRECT_IO;
-               fuse_finish_open(inode, file, ff, &outarg);
-       }
+               return err;
 
-       return err;
+       fuse_finish_open(inode, file);
+
+       return 0;
 }
 
-void fuse_release_fill(struct fuse_file *ff, u64 nodeid, int flags, int opcode)
+static void fuse_prepare_release(struct fuse_file *ff, int flags, int opcode)
 {
+       struct fuse_conn *fc = ff->fc;
        struct fuse_req *req = ff->reserved_req;
        struct fuse_release_in *inarg = &req->misc.release.in;
 
+       spin_lock(&fc->lock);
+       list_del(&ff->write_entry);
+       if (!RB_EMPTY_NODE(&ff->polled_node))
+               rb_erase(&ff->polled_node, &fc->polled_files);
+       spin_unlock(&fc->lock);
+
+       wake_up_interruptible_sync(&ff->poll_wait);
+
        inarg->fh = ff->fh;
        inarg->flags = flags;
        req->in.h.opcode = opcode;
-       req->in.h.nodeid = nodeid;
+       req->in.h.nodeid = ff->nodeid;
        req->in.numargs = 1;
        req->in.args[0].size = sizeof(struct fuse_release_in);
        req->in.args[0].value = inarg;
 }
 
-int fuse_release_common(struct inode *inode, struct file *file, int isdir)
+void fuse_release_common(struct file *file, int opcode)
 {
-       struct fuse_file *ff = file->private_data;
-       if (ff) {
-               struct fuse_conn *fc = get_fuse_conn(inode);
-               struct fuse_req *req = ff->reserved_req;
-
-               fuse_release_fill(ff, get_node_id(inode), file->f_flags,
-                                 isdir ? FUSE_RELEASEDIR : FUSE_RELEASE);
+       struct fuse_file *ff;
+       struct fuse_req *req;
 
-               /* Hold vfsmount and dentry until release is finished */
-               req->misc.release.vfsmount = mntget(file->f_path.mnt);
-               req->misc.release.dentry = dget(file->f_path.dentry);
+       ff = file->private_data;
+       if (unlikely(!ff))
+               return;
 
-               spin_lock(&fc->lock);
-               list_del(&ff->write_entry);
-               if (!RB_EMPTY_NODE(&ff->polled_node))
-                       rb_erase(&ff->polled_node, &fc->polled_files);
-               spin_unlock(&fc->lock);
+       req = ff->reserved_req;
+       fuse_prepare_release(ff, file->f_flags, opcode);
 
-               wake_up_interruptible_sync(&ff->poll_wait);
-               /*
-                * Normally this will send the RELEASE request,
-                * however if some asynchronous READ or WRITE requests
-                * are outstanding, the sending will be delayed
-                */
-               fuse_file_put(ff);
-       }
+       /* Hold vfsmount and dentry until release is finished */
+       path_get(&file->f_path);
+       req->misc.release.path = file->f_path;
 
-       /* Return value is ignored by VFS */
-       return 0;
+       /*
+        * Normally this will send the RELEASE request, however if
+        * some asynchronous READ or WRITE requests are outstanding,
+        * the sending will be delayed.
+        */
+       fuse_file_put(ff);
 }
 
 static int fuse_open(struct inode *inode, struct file *file)
 {
-       return fuse_open_common(inode, file, 0);
+       return fuse_open_common(inode, file, false);
 }
 
 static int fuse_release(struct inode *inode, struct file *file)
 {
-       return fuse_release_common(inode, file, 0);
+       fuse_release_common(file, FUSE_RELEASE);
+
+       /* return value is ignored by VFS */
+       return 0;
+}
+
+void fuse_sync_release(struct fuse_file *ff, int flags)
+{
+       WARN_ON(atomic_read(&ff->count) > 1);
+       fuse_prepare_release(ff, flags, FUSE_RELEASE);
+       ff->reserved_req->force = 1;
+       fuse_request_send(ff->fc, ff->reserved_req);
+       fuse_put_request(ff->fc, ff->reserved_req);
+       kfree(ff);
 }
+EXPORT_SYMBOL_GPL(fuse_sync_release);
 
 /*
  * Scramble the ID space with XTEA, so that the value of the files_struct
@@ -371,8 +408,8 @@ static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
        return fuse_fsync_common(file, de, datasync, 0);
 }
 
-void fuse_read_fill(struct fuse_req *req, struct file *file,
-                   struct inode *inode, loff_t pos, size_t count, int opcode)
+void fuse_read_fill(struct fuse_req *req, struct file *file, loff_t pos,
+                   size_t count, int opcode)
 {
        struct fuse_read_in *inarg = &req->misc.read.in;
        struct fuse_file *ff = file->private_data;
@@ -382,7 +419,7 @@ void fuse_read_fill(struct fuse_req *req, struct file *file,
        inarg->size = count;
        inarg->flags = file->f_flags;
        req->in.h.opcode = opcode;
-       req->in.h.nodeid = get_node_id(inode);
+       req->in.h.nodeid = ff->nodeid;
        req->in.numargs = 1;
        req->in.args[0].size = sizeof(struct fuse_read_in);
        req->in.args[0].value = inarg;
@@ -392,12 +429,12 @@ void fuse_read_fill(struct fuse_req *req, struct file *file,
 }
 
 static size_t fuse_send_read(struct fuse_req *req, struct file *file,
-                            struct inode *inode, loff_t pos, size_t count,
-                            fl_owner_t owner)
+                            loff_t pos, size_t count, fl_owner_t owner)
 {
-       struct fuse_conn *fc = get_fuse_conn(inode);
+       struct fuse_file *ff = file->private_data;
+       struct fuse_conn *fc = ff->fc;
 
-       fuse_read_fill(req, file, inode, pos, count, FUSE_READ);
+       fuse_read_fill(req, file, pos, count, FUSE_READ);
        if (owner != NULL) {
                struct fuse_read_in *inarg = &req->misc.read.in;
 
@@ -455,7 +492,7 @@ static int fuse_readpage(struct file *file, struct page *page)
        req->out.argpages = 1;
        req->num_pages = 1;
        req->pages[0] = page;
-       num_read = fuse_send_read(req, file, inode, pos, count, NULL);
+       num_read = fuse_send_read(req, file, pos, count, NULL);
        err = req->out.h.error;
        fuse_put_request(fc, req);
 
@@ -504,19 +541,18 @@ static void fuse_readpages_end(struct fuse_conn *fc, struct fuse_req *req)
                fuse_file_put(req->ff);
 }
 
-static void fuse_send_readpages(struct fuse_req *req, struct file *file,
-                               struct inode *inode)
+static void fuse_send_readpages(struct fuse_req *req, struct file *file)
 {
-       struct fuse_conn *fc = get_fuse_conn(inode);
+       struct fuse_file *ff = file->private_data;
+       struct fuse_conn *fc = ff->fc;
        loff_t pos = page_offset(req->pages[0]);
        size_t count = req->num_pages << PAGE_CACHE_SHIFT;
 
        req->out.argpages = 1;
        req->out.page_zeroing = 1;
-       fuse_read_fill(req, file, inode, pos, count, FUSE_READ);
+       fuse_read_fill(req, file, pos, count, FUSE_READ);
        req->misc.read.attr_ver = fuse_get_attr_version(fc);
        if (fc->async_read) {
-               struct fuse_file *ff = file->private_data;
                req->ff = fuse_file_get(ff);
                req->end = fuse_readpages_end;
                fuse_request_send_background(fc, req);
@@ -546,7 +582,7 @@ static int fuse_readpages_fill(void *_data, struct page *page)
            (req->num_pages == FUSE_MAX_PAGES_PER_REQ ||
             (req->num_pages + 1) * PAGE_CACHE_SIZE > fc->max_read ||
             req->pages[req->num_pages - 1]->index + 1 != page->index)) {
-               fuse_send_readpages(req, data->file, inode);
+               fuse_send_readpages(req, data->file);
                data->req = req = fuse_get_req(fc);
                if (IS_ERR(req)) {
                        unlock_page(page);
@@ -580,7 +616,7 @@ static int fuse_readpages(struct file *file, struct address_space *mapping,
        err = read_cache_pages(mapping, pages, fuse_readpages_fill, &data);
        if (!err) {
                if (data.req->num_pages)
-                       fuse_send_readpages(data.req, file, inode);
+                       fuse_send_readpages(data.req, file);
                else
                        fuse_put_request(fc, data.req);
        }
@@ -607,24 +643,19 @@ static ssize_t fuse_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
        return generic_file_aio_read(iocb, iov, nr_segs, pos);
 }
 
-static void fuse_write_fill(struct fuse_req *req, struct file *file,
-                           struct fuse_file *ff, struct inode *inode,
-                           loff_t pos, size_t count, int writepage)
+static void fuse_write_fill(struct fuse_req *req, struct fuse_file *ff,
+                           loff_t pos, size_t count)
 {
-       struct fuse_conn *fc = get_fuse_conn(inode);
        struct fuse_write_in *inarg = &req->misc.write.in;
        struct fuse_write_out *outarg = &req->misc.write.out;
 
-       memset(inarg, 0, sizeof(struct fuse_write_in));
        inarg->fh = ff->fh;
        inarg->offset = pos;
        inarg->size = count;
-       inarg->write_flags = writepage ? FUSE_WRITE_CACHE : 0;
-       inarg->flags = file ? file->f_flags : 0;
        req->in.h.opcode = FUSE_WRITE;
-       req->in.h.nodeid = get_node_id(inode);
+       req->in.h.nodeid = ff->nodeid;
        req->in.numargs = 2;
-       if (fc->minor < 9)
+       if (ff->fc->minor < 9)
                req->in.args[0].size = FUSE_COMPAT_WRITE_IN_SIZE;
        else
                req->in.args[0].size = sizeof(struct fuse_write_in);
@@ -636,13 +667,15 @@ static void fuse_write_fill(struct fuse_req *req, struct file *file,
 }
 
 static size_t fuse_send_write(struct fuse_req *req, struct file *file,
-                             struct inode *inode, loff_t pos, size_t count,
-                             fl_owner_t owner)
+                             loff_t pos, size_t count, fl_owner_t owner)
 {
-       struct fuse_conn *fc = get_fuse_conn(inode);
-       fuse_write_fill(req, file, file->private_data, inode, pos, count, 0);
+       struct fuse_file *ff = file->private_data;
+       struct fuse_conn *fc = ff->fc;
+       struct fuse_write_in *inarg = &req->misc.write.in;
+
+       fuse_write_fill(req, ff, pos, count);
+       inarg->flags = file->f_flags;
        if (owner != NULL) {
-               struct fuse_write_in *inarg = &req->misc.write.in;
                inarg->write_flags |= FUSE_WRITE_LOCKOWNER;
                inarg->lock_owner = fuse_lock_owner_id(fc, owner);
        }
@@ -700,7 +733,7 @@ static int fuse_buffered_write(struct file *file, struct inode *inode,
        req->num_pages = 1;
        req->pages[0] = page;
        req->page_offset = offset;
-       nres = fuse_send_write(req, file, inode, pos, count, NULL);
+       nres = fuse_send_write(req, file, pos, count, NULL);
        err = req->out.h.error;
        fuse_put_request(fc, req);
        if (!err && !nres)
@@ -741,7 +774,7 @@ static size_t fuse_send_write_pages(struct fuse_req *req, struct file *file,
        for (i = 0; i < req->num_pages; i++)
                fuse_wait_on_page_writeback(inode, req->pages[i]->index);
 
-       res = fuse_send_write(req, file, inode, pos, count, NULL);
+       res = fuse_send_write(req, file, pos, count, NULL);
 
        offset = req->page_offset;
        count = res;
@@ -979,25 +1012,23 @@ static int fuse_get_user_pages(struct fuse_req *req, const char __user *buf,
        return 0;
 }
 
-static ssize_t fuse_direct_io(struct file *file, const char __user *buf,
-                             size_t count, loff_t *ppos, int write)
+ssize_t fuse_direct_io(struct file *file, const char __user *buf,
+                      size_t count, loff_t *ppos, int write)
 {
-       struct inode *inode = file->f_path.dentry->d_inode;
-       struct fuse_conn *fc = get_fuse_conn(inode);
+       struct fuse_file *ff = file->private_data;
+       struct fuse_conn *fc = ff->fc;
        size_t nmax = write ? fc->max_write : fc->max_read;
        loff_t pos = *ppos;
        ssize_t res = 0;
        struct fuse_req *req;
 
-       if (is_bad_inode(inode))
-               return -EIO;
-
        req = fuse_get_req(fc);
        if (IS_ERR(req))
                return PTR_ERR(req);
 
        while (count) {
                size_t nres;
+               fl_owner_t owner = current->files;
                size_t nbytes = min(count, nmax);
                int err = fuse_get_user_pages(req, buf, &nbytes, write);
                if (err) {
@@ -1006,11 +1037,10 @@ static ssize_t fuse_direct_io(struct file *file, const char __user *buf,
                }
 
                if (write)
-                       nres = fuse_send_write(req, file, inode, pos, nbytes,
-                                              current->files);
+                       nres = fuse_send_write(req, file, pos, nbytes, owner);
                else
-                       nres = fuse_send_read(req, file, inode, pos, nbytes,
-                                             current->files);
+                       nres = fuse_send_read(req, file, pos, nbytes, owner);
+
                fuse_release_user_pages(req, !write);
                if (req->out.h.error) {
                        if (!res)
@@ -1034,20 +1064,27 @@ static ssize_t fuse_direct_io(struct file *file, const char __user *buf,
                }
        }
        fuse_put_request(fc, req);
-       if (res > 0) {
-               if (write)
-                       fuse_write_update_size(inode, pos);
+       if (res > 0)
                *ppos = pos;
-       }
-       fuse_invalidate_attr(inode);
 
        return res;
 }
+EXPORT_SYMBOL_GPL(fuse_direct_io);
 
 static ssize_t fuse_direct_read(struct file *file, char __user *buf,
                                     size_t count, loff_t *ppos)
 {
-       return fuse_direct_io(file, buf, count, ppos, 0);
+       ssize_t res;
+       struct inode *inode = file->f_path.dentry->d_inode;
+
+       if (is_bad_inode(inode))
+               return -EIO;
+
+       res = fuse_direct_io(file, buf, count, ppos, 0);
+
+       fuse_invalidate_attr(inode);
+
+       return res;
 }
 
 static ssize_t fuse_direct_write(struct file *file, const char __user *buf,
@@ -1055,12 +1092,22 @@ static ssize_t fuse_direct_write(struct file *file, const char __user *buf,
 {
        struct inode *inode = file->f_path.dentry->d_inode;
        ssize_t res;
+
+       if (is_bad_inode(inode))
+               return -EIO;
+
        /* Don't allow parallel writes to the same file */
        mutex_lock(&inode->i_mutex);
        res = generic_write_checks(file, ppos, &count, 0);
-       if (!res)
+       if (!res) {
                res = fuse_direct_io(file, buf, count, ppos, 1);
+               if (res > 0)
+                       fuse_write_update_size(inode, *ppos);
+       }
        mutex_unlock(&inode->i_mutex);
+
+       fuse_invalidate_attr(inode);
+
        return res;
 }
 
@@ -1177,9 +1224,10 @@ static int fuse_writepage_locked(struct page *page)
        req->ff = fuse_file_get(ff);
        spin_unlock(&fc->lock);
 
-       fuse_write_fill(req, NULL, ff, inode, page_offset(page), 0, 1);
+       fuse_write_fill(req, ff, page_offset(page), 0);
 
        copy_highpage(tmp_page, page);
+       req->misc.write.in.write_flags |= FUSE_WRITE_CACHE;
        req->in.argpages = 1;
        req->num_pages = 1;
        req->pages[0] = tmp_page;
@@ -1603,12 +1651,11 @@ static int fuse_ioctl_copy_user(struct page **pages, struct iovec *iov,
  * limits ioctl data transfers to well-formed ioctls and is the forced
  * behavior for all FUSE servers.
  */
-static long fuse_file_do_ioctl(struct file *file, unsigned int cmd,
-                              unsigned long arg, unsigned int flags)
+long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
+                  unsigned int flags)
 {
-       struct inode *inode = file->f_dentry->d_inode;
        struct fuse_file *ff = file->private_data;
-       struct fuse_conn *fc = get_fuse_conn(inode);
+       struct fuse_conn *fc = ff->fc;
        struct fuse_ioctl_in inarg = {
                .fh = ff->fh,
                .cmd = cmd,
@@ -1627,13 +1674,6 @@ static long fuse_file_do_ioctl(struct file *file, unsigned int cmd,
        /* assume all the iovs returned by client always fits in a page */
        BUILD_BUG_ON(sizeof(struct iovec) * FUSE_IOCTL_MAX_IOV > PAGE_SIZE);
 
-       if (!fuse_allow_task(fc, current))
-               return -EACCES;
-
-       err = -EIO;
-       if (is_bad_inode(inode))
-               goto out;
-
        err = -ENOMEM;
        pages = kzalloc(sizeof(pages[0]) * FUSE_MAX_PAGES_PER_REQ, GFP_KERNEL);
        iov_page = alloc_page(GFP_KERNEL);
@@ -1694,7 +1734,7 @@ static long fuse_file_do_ioctl(struct file *file, unsigned int cmd,
 
        /* okay, let's send it to the client */
        req->in.h.opcode = FUSE_IOCTL;
-       req->in.h.nodeid = get_node_id(inode);
+       req->in.h.nodeid = ff->nodeid;
        req->in.numargs = 1;
        req->in.args[0].size = sizeof(inarg);
        req->in.args[0].value = &inarg;
@@ -1777,17 +1817,33 @@ static long fuse_file_do_ioctl(struct file *file, unsigned int cmd,
 
        return err ? err : outarg.result;
 }
+EXPORT_SYMBOL_GPL(fuse_do_ioctl);
+
+static long fuse_file_ioctl_common(struct file *file, unsigned int cmd,
+                                  unsigned long arg, unsigned int flags)
+{
+       struct inode *inode = file->f_dentry->d_inode;
+       struct fuse_conn *fc = get_fuse_conn(inode);
+
+       if (!fuse_allow_task(fc, current))
+               return -EACCES;
+
+       if (is_bad_inode(inode))
+               return -EIO;
+
+       return fuse_do_ioctl(file, cmd, arg, flags);
+}
 
 static long fuse_file_ioctl(struct file *file, unsigned int cmd,
                            unsigned long arg)
 {
-       return fuse_file_do_ioctl(file, cmd, arg, 0);
+       return fuse_file_ioctl_common(file, cmd, arg, 0);
 }
 
 static long fuse_file_compat_ioctl(struct file *file, unsigned int cmd,
                                   unsigned long arg)
 {
-       return fuse_file_do_ioctl(file, cmd, arg, FUSE_IOCTL_COMPAT);
+       return fuse_file_ioctl_common(file, cmd, arg, FUSE_IOCTL_COMPAT);
 }
 
 /*
@@ -1841,11 +1897,10 @@ static void fuse_register_polled_file(struct fuse_conn *fc,
        spin_unlock(&fc->lock);
 }
 
-static unsigned fuse_file_poll(struct file *file, poll_table *wait)
+unsigned fuse_file_poll(struct file *file, poll_table *wait)
 {
-       struct inode *inode = file->f_dentry->d_inode;
        struct fuse_file *ff = file->private_data;
-       struct fuse_conn *fc = get_fuse_conn(inode);
+       struct fuse_conn *fc = ff->fc;
        struct fuse_poll_in inarg = { .fh = ff->fh, .kh = ff->kh };
        struct fuse_poll_out outarg;
        struct fuse_req *req;
@@ -1870,7 +1925,7 @@ static unsigned fuse_file_poll(struct file *file, poll_table *wait)
                return PTR_ERR(req);
 
        req->in.h.opcode = FUSE_POLL;
-       req->in.h.nodeid = get_node_id(inode);
+       req->in.h.nodeid = ff->nodeid;
        req->in.numargs = 1;
        req->in.args[0].size = sizeof(inarg);
        req->in.args[0].value = &inarg;
@@ -1889,6 +1944,7 @@ static unsigned fuse_file_poll(struct file *file, poll_table *wait)
        }
        return POLLERR;
 }
+EXPORT_SYMBOL_GPL(fuse_file_poll);
 
 /*
  * This is called from fuse_handle_notify() on FUSE_NOTIFY_POLL and
index 6fc5aedaa0d52b3d86d94bde490aa5711e47cfd1..aaf2f9ff970ec4b824b6bf792264db9dd579f58c 100644 (file)
@@ -97,8 +97,13 @@ struct fuse_inode {
        struct list_head writepages;
 };
 
+struct fuse_conn;
+
 /** FUSE specific file data */
 struct fuse_file {
+       /** Fuse connection for this file */
+       struct fuse_conn *fc;
+
        /** Request reserved for flush and release */
        struct fuse_req *reserved_req;
 
@@ -108,9 +113,15 @@ struct fuse_file {
        /** File handle used by userspace */
        u64 fh;
 
+       /** Node id of this file */
+       u64 nodeid;
+
        /** Refcount */
        atomic_t count;
 
+       /** FOPEN_* flags returned by open */
+       u32 open_flags;
+
        /** Entry on inode's write_files list */
        struct list_head write_entry;
 
@@ -185,8 +196,6 @@ enum fuse_req_state {
        FUSE_REQ_FINISHED
 };
 
-struct fuse_conn;
-
 /**
  * A request to the client
  */
@@ -248,11 +257,12 @@ struct fuse_req {
                struct fuse_forget_in forget_in;
                struct {
                        struct fuse_release_in in;
-                       struct vfsmount *vfsmount;
-                       struct dentry *dentry;
+                       struct path path;
                } release;
                struct fuse_init_in init_in;
                struct fuse_init_out init_out;
+               struct cuse_init_in cuse_init_in;
+               struct cuse_init_out cuse_init_out;
                struct {
                        struct fuse_read_in in;
                        u64 attr_ver;
@@ -386,6 +396,9 @@ struct fuse_conn {
        /** Filesystem supports NFS exporting.  Only set in INIT */
        unsigned export_support:1;
 
+       /** Set if bdi is valid */
+       unsigned bdi_initialized:1;
+
        /*
         * The following bitfields are only for optimization purposes
         * and hence races in setting them will not cause malfunction
@@ -515,25 +528,24 @@ void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
  * Initialize READ or READDIR request
  */
 void fuse_read_fill(struct fuse_req *req, struct file *file,
-                   struct inode *inode, loff_t pos, size_t count, int opcode);
+                   loff_t pos, size_t count, int opcode);
 
 /**
  * Send OPEN or OPENDIR request
  */
-int fuse_open_common(struct inode *inode, struct file *file, int isdir);
+int fuse_open_common(struct inode *inode, struct file *file, bool isdir);
 
 struct fuse_file *fuse_file_alloc(struct fuse_conn *fc);
+struct fuse_file *fuse_file_get(struct fuse_file *ff);
 void fuse_file_free(struct fuse_file *ff);
-void fuse_finish_open(struct inode *inode, struct file *file,
-                     struct fuse_file *ff, struct fuse_open_out *outarg);
+void fuse_finish_open(struct inode *inode, struct file *file);
 
-/** Fill in ff->reserved_req with a RELEASE request */
-void fuse_release_fill(struct fuse_file *ff, u64 nodeid, int flags, int opcode);
+void fuse_sync_release(struct fuse_file *ff, int flags);
 
 /**
  * Send RELEASE or RELEASEDIR request
  */
-int fuse_release_common(struct inode *inode, struct file *file, int isdir);
+void fuse_release_common(struct file *file, int opcode);
 
 /**
  * Send FSYNC or FSYNCDIR request
@@ -652,10 +664,12 @@ void fuse_invalidate_entry_cache(struct dentry *entry);
  */
 struct fuse_conn *fuse_conn_get(struct fuse_conn *fc);
 
+void fuse_conn_kill(struct fuse_conn *fc);
+
 /**
  * Initialize fuse_conn
  */
-int fuse_conn_init(struct fuse_conn *fc, struct super_block *sb);
+void fuse_conn_init(struct fuse_conn *fc);
 
 /**
  * Release reference to fuse_conn
@@ -694,4 +708,13 @@ void fuse_release_nowrite(struct inode *inode);
 
 u64 fuse_get_attr_version(struct fuse_conn *fc);
 
+int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
+                bool isdir);
+ssize_t fuse_direct_io(struct file *file, const char __user *buf,
+                      size_t count, loff_t *ppos, int write);
+long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
+                  unsigned int flags);
+unsigned fuse_file_poll(struct file *file, poll_table *wait);
+int fuse_dev_release(struct inode *inode, struct file *file);
+
 #endif /* _FS_FUSE_I_H */
index 91f7c85f1ffd7e52f8aa9d61c6f309cd8ed5bf42..f0df55a529296e0df1b0f65bcfea7695c3674c09 100644 (file)
@@ -277,11 +277,14 @@ static void fuse_send_destroy(struct fuse_conn *fc)
        }
 }
 
-static void fuse_put_super(struct super_block *sb)
+static void fuse_bdi_destroy(struct fuse_conn *fc)
 {
-       struct fuse_conn *fc = get_fuse_conn_super(sb);
+       if (fc->bdi_initialized)
+               bdi_destroy(&fc->bdi);
+}
 
-       fuse_send_destroy(fc);
+void fuse_conn_kill(struct fuse_conn *fc)
+{
        spin_lock(&fc->lock);
        fc->connected = 0;
        fc->blocked = 0;
@@ -295,7 +298,16 @@ static void fuse_put_super(struct super_block *sb)
        list_del(&fc->entry);
        fuse_ctl_remove_conn(fc);
        mutex_unlock(&fuse_mutex);
-       bdi_destroy(&fc->bdi);
+       fuse_bdi_destroy(fc);
+}
+EXPORT_SYMBOL_GPL(fuse_conn_kill);
+
+static void fuse_put_super(struct super_block *sb)
+{
+       struct fuse_conn *fc = get_fuse_conn_super(sb);
+
+       fuse_send_destroy(fc);
+       fuse_conn_kill(fc);
        fuse_conn_put(fc);
 }
 
@@ -466,10 +478,8 @@ static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt)
        return 0;
 }
 
-int fuse_conn_init(struct fuse_conn *fc, struct super_block *sb)
+void fuse_conn_init(struct fuse_conn *fc)
 {
-       int err;
-
        memset(fc, 0, sizeof(*fc));
        spin_lock_init(&fc->lock);
        mutex_init(&fc->inst_mutex);
@@ -484,49 +494,12 @@ int fuse_conn_init(struct fuse_conn *fc, struct super_block *sb)
        INIT_LIST_HEAD(&fc->bg_queue);
        INIT_LIST_HEAD(&fc->entry);
        atomic_set(&fc->num_waiting, 0);
-       fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
-       fc->bdi.unplug_io_fn = default_unplug_io_fn;
-       /* fuse does it's own writeback accounting */
-       fc->bdi.capabilities = BDI_CAP_NO_ACCT_WB;
        fc->khctr = 0;
        fc->polled_files = RB_ROOT;
-       fc->dev = sb->s_dev;
-       err = bdi_init(&fc->bdi);
-       if (err)
-               goto error_mutex_destroy;
-       if (sb->s_bdev) {
-               err = bdi_register(&fc->bdi, NULL, "%u:%u-fuseblk",
-                                  MAJOR(fc->dev), MINOR(fc->dev));
-       } else {
-               err = bdi_register_dev(&fc->bdi, fc->dev);
-       }
-       if (err)
-               goto error_bdi_destroy;
-       /*
-        * For a single fuse filesystem use max 1% of dirty +
-        * writeback threshold.
-        *
-        * This gives about 1M of write buffer for memory maps on a
-        * machine with 1G and 10% dirty_ratio, which should be more
-        * than enough.
-        *
-        * Privileged users can raise it by writing to
-        *
-        *    /sys/class/bdi/<bdi>/max_ratio
-        */
-       bdi_set_max_ratio(&fc->bdi, 1);
        fc->reqctr = 0;
        fc->blocked = 1;
        fc->attr_version = 1;
        get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key));
-
-       return 0;
-
- error_bdi_destroy:
-       bdi_destroy(&fc->bdi);
- error_mutex_destroy:
-       mutex_destroy(&fc->inst_mutex);
-       return err;
 }
 EXPORT_SYMBOL_GPL(fuse_conn_init);
 
@@ -539,12 +512,14 @@ void fuse_conn_put(struct fuse_conn *fc)
                fc->release(fc);
        }
 }
+EXPORT_SYMBOL_GPL(fuse_conn_put);
 
 struct fuse_conn *fuse_conn_get(struct fuse_conn *fc)
 {
        atomic_inc(&fc->count);
        return fc;
 }
+EXPORT_SYMBOL_GPL(fuse_conn_get);
 
 static struct inode *fuse_get_root_inode(struct super_block *sb, unsigned mode)
 {
@@ -797,6 +772,48 @@ static void fuse_free_conn(struct fuse_conn *fc)
        kfree(fc);
 }
 
+static int fuse_bdi_init(struct fuse_conn *fc, struct super_block *sb)
+{
+       int err;
+
+       fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
+       fc->bdi.unplug_io_fn = default_unplug_io_fn;
+       /* fuse does it's own writeback accounting */
+       fc->bdi.capabilities = BDI_CAP_NO_ACCT_WB;
+
+       err = bdi_init(&fc->bdi);
+       if (err)
+               return err;
+
+       fc->bdi_initialized = 1;
+
+       if (sb->s_bdev) {
+               err =  bdi_register(&fc->bdi, NULL, "%u:%u-fuseblk",
+                                   MAJOR(fc->dev), MINOR(fc->dev));
+       } else {
+               err = bdi_register_dev(&fc->bdi, fc->dev);
+       }
+
+       if (err)
+               return err;
+
+       /*
+        * For a single fuse filesystem use max 1% of dirty +
+        * writeback threshold.
+        *
+        * This gives about 1M of write buffer for memory maps on a
+        * machine with 1G and 10% dirty_ratio, which should be more
+        * than enough.
+        *
+        * Privileged users can raise it by writing to
+        *
+        *    /sys/class/bdi/<bdi>/max_ratio
+        */
+       bdi_set_max_ratio(&fc->bdi, 1);
+
+       return 0;
+}
+
 static int fuse_fill_super(struct super_block *sb, void *data, int silent)
 {
        struct fuse_conn *fc;
@@ -843,11 +860,12 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
        if (!fc)
                goto err_fput;
 
-       err = fuse_conn_init(fc, sb);
-       if (err) {
-               kfree(fc);
-               goto err_fput;
-       }
+       fuse_conn_init(fc);
+
+       fc->dev = sb->s_dev;
+       err = fuse_bdi_init(fc, sb);
+       if (err)
+               goto err_put_conn;
 
        fc->release = fuse_free_conn;
        fc->flags = d.flags;
@@ -911,7 +929,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
  err_put_root:
        dput(root_dentry);
  err_put_conn:
-       bdi_destroy(&fc->bdi);
+       fuse_bdi_destroy(fc);
        fuse_conn_put(fc);
  err_fput:
        fput(file);
index d53a9bea1c2f287f8c68f402a082dece725b3832..3da2f1f4f7386081c025060702aacb535e187600 100644 (file)
@@ -1,3 +1,4 @@
+EXTRA_CFLAGS := -I$(src)
 obj-$(CONFIG_GFS2_FS) += gfs2.o
 gfs2-y := acl.o bmap.o dir.o eaops.o eattr.o glock.o \
        glops.o inode.o log.o lops.o main.o meta_io.o \
index 329763530dc065847057b5be1c81b408763b46dd..6d47379e794bc4a6b8a9f665aa3546da36dde441 100644 (file)
@@ -25,6 +25,7 @@
 #include "trans.h"
 #include "dir.h"
 #include "util.h"
+#include "trace_gfs2.h"
 
 /* This doesn't need to be that large as max 64 bit pointers in a 4k
  * block is 512, so __u16 is fine for that. It saves stack space to
@@ -589,6 +590,7 @@ int gfs2_block_map(struct inode *inode, sector_t lblock,
        clear_buffer_mapped(bh_map);
        clear_buffer_new(bh_map);
        clear_buffer_boundary(bh_map);
+       trace_gfs2_bmap(ip, bh_map, lblock, create, 1);
        if (gfs2_is_dir(ip)) {
                bsize = sdp->sd_jbsize;
                arr = sdp->sd_jheightsize;
@@ -623,6 +625,7 @@ int gfs2_block_map(struct inode *inode, sector_t lblock,
        ret = 0;
 out:
        release_metapath(&mp);
+       trace_gfs2_bmap(ip, bh_map, lblock, create, ret);
        bmap_unlock(ip, create);
        return ret;
 
index 2bf62bcc5181729b44e7247d7ed8947805cd1263..297421c0427a86ef58fa79cc2ce9518d10171381 100644 (file)
@@ -39,6 +39,8 @@
 #include "super.h"
 #include "util.h"
 #include "bmap.h"
+#define CREATE_TRACE_POINTS
+#include "trace_gfs2.h"
 
 struct gfs2_gl_hash_bucket {
         struct hlist_head hb_list;
@@ -155,7 +157,7 @@ static void glock_free(struct gfs2_glock *gl)
 
        if (aspace)
                gfs2_aspace_put(aspace);
-
+       trace_gfs2_glock_put(gl);
        sdp->sd_lockstruct.ls_ops->lm_put_lock(gfs2_glock_cachep, gl);
 }
 
@@ -317,14 +319,17 @@ restart:
                                                return 2;
                                        gh->gh_error = ret;
                                        list_del_init(&gh->gh_list);
+                                       trace_gfs2_glock_queue(gh, 0);
                                        gfs2_holder_wake(gh);
                                        goto restart;
                                }
                                set_bit(HIF_HOLDER, &gh->gh_iflags);
+                               trace_gfs2_promote(gh, 1);
                                gfs2_holder_wake(gh);
                                goto restart;
                        }
                        set_bit(HIF_HOLDER, &gh->gh_iflags);
+                       trace_gfs2_promote(gh, 0);
                        gfs2_holder_wake(gh);
                        continue;
                }
@@ -354,6 +359,7 @@ static inline void do_error(struct gfs2_glock *gl, const int ret)
                else
                        continue;
                list_del_init(&gh->gh_list);
+               trace_gfs2_glock_queue(gh, 0);
                gfs2_holder_wake(gh);
        }
 }
@@ -422,6 +428,7 @@ static void finish_xmote(struct gfs2_glock *gl, unsigned int ret)
        int rv;
 
        spin_lock(&gl->gl_spin);
+       trace_gfs2_glock_state_change(gl, state);
        state_change(gl, state);
        gh = find_first_waiter(gl);
 
@@ -851,6 +858,7 @@ static void handle_callback(struct gfs2_glock *gl, unsigned int state,
                        gl->gl_demote_state != state) {
                gl->gl_demote_state = LM_ST_UNLOCKED;
        }
+       trace_gfs2_demote_rq(gl);
 }
 
 /**
@@ -936,6 +944,7 @@ fail:
                        goto do_cancel;
                return;
        }
+       trace_gfs2_glock_queue(gh, 1);
        list_add_tail(&gh->gh_list, insert_pt);
 do_cancel:
        gh = list_entry(gl->gl_holders.next, struct gfs2_holder, gh_list);
@@ -1032,6 +1041,7 @@ void gfs2_glock_dq(struct gfs2_holder *gh)
                    !test_bit(GLF_DEMOTE, &gl->gl_flags))
                        fast_path = 1;
        }
+       trace_gfs2_glock_queue(gh, 0);
        spin_unlock(&gl->gl_spin);
        if (likely(fast_path))
                return;
index f2e449c595b444e5d94ec674cd15d5dd4a367a9f..13c6237c5f678222a40a4d0e7f67a7707e3c38bc 100644 (file)
@@ -28,6 +28,7 @@
 #include "meta_io.h"
 #include "util.h"
 #include "dir.h"
+#include "trace_gfs2.h"
 
 #define PULL 1
 
@@ -313,6 +314,7 @@ int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks)
                gfs2_log_lock(sdp);
        }
        atomic_sub(blks, &sdp->sd_log_blks_free);
+       trace_gfs2_log_blocks(sdp, -blks);
        gfs2_log_unlock(sdp);
        mutex_unlock(&sdp->sd_log_reserve_mutex);
 
@@ -333,6 +335,7 @@ void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks)
 
        gfs2_log_lock(sdp);
        atomic_add(blks, &sdp->sd_log_blks_free);
+       trace_gfs2_log_blocks(sdp, blks);
        gfs2_assert_withdraw(sdp,
                             atomic_read(&sdp->sd_log_blks_free) <= sdp->sd_jdesc->jd_blocks);
        gfs2_log_unlock(sdp);
@@ -558,6 +561,7 @@ static void log_pull_tail(struct gfs2_sbd *sdp, unsigned int new_tail)
 
        gfs2_log_lock(sdp);
        atomic_add(dist, &sdp->sd_log_blks_free);
+       trace_gfs2_log_blocks(sdp, dist);
        gfs2_assert_withdraw(sdp, atomic_read(&sdp->sd_log_blks_free) <= sdp->sd_jdesc->jd_blocks);
        gfs2_log_unlock(sdp);
 
@@ -715,6 +719,7 @@ void __gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl)
                up_write(&sdp->sd_log_flush_lock);
                return;
        }
+       trace_gfs2_log_flush(sdp, 1);
 
        ai = kzalloc(sizeof(struct gfs2_ail), GFP_NOFS | __GFP_NOFAIL);
        INIT_LIST_HEAD(&ai->ai_ail1_list);
@@ -746,6 +751,7 @@ void __gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl)
        else if (sdp->sd_log_tail != current_tail(sdp) && !sdp->sd_log_idle){
                gfs2_log_lock(sdp);
                atomic_dec(&sdp->sd_log_blks_free); /* Adjust for unreserved buffer */
+               trace_gfs2_log_blocks(sdp, -1);
                gfs2_log_unlock(sdp);
                log_write_header(sdp, 0, PULL);
        }
@@ -763,7 +769,7 @@ void __gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl)
                ai = NULL;
        }
        gfs2_log_unlock(sdp);
-
+       trace_gfs2_log_flush(sdp, 0);
        up_write(&sdp->sd_log_flush_lock);
 
        kfree(ai);
@@ -787,6 +793,7 @@ static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
        gfs2_assert_withdraw(sdp, sdp->sd_log_blks_reserved + tr->tr_reserved >= reserved);
        unused = sdp->sd_log_blks_reserved - reserved + tr->tr_reserved;
        atomic_add(unused, &sdp->sd_log_blks_free);
+       trace_gfs2_log_blocks(sdp, unused);
        gfs2_assert_withdraw(sdp, atomic_read(&sdp->sd_log_blks_free) <=
                             sdp->sd_jdesc->jd_blocks);
        sdp->sd_log_blks_reserved = reserved;
index 00315f50fa467daa94214c8e37e877eb76eaf00c..9969ff062c5b82b9d518b1b841c0aac602f01280 100644 (file)
@@ -27,6 +27,7 @@
 #include "rgrp.h"
 #include "trans.h"
 #include "util.h"
+#include "trace_gfs2.h"
 
 /**
  * gfs2_pin - Pin a buffer in memory
@@ -53,6 +54,7 @@ static void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh)
        if (bd->bd_ail)
                list_move(&bd->bd_ail_st_list, &bd->bd_ail->ai_ail2_list);
        get_bh(bh);
+       trace_gfs2_pin(bd, 1);
 }
 
 /**
@@ -89,6 +91,7 @@ static void gfs2_unpin(struct gfs2_sbd *sdp, struct buffer_head *bh,
        bd->bd_ail = ai;
        list_add(&bd->bd_ail_st_list, &ai->ai_ail1_list);
        clear_bit(GLF_LFLUSH, &bd->bd_gl->gl_flags);
+       trace_gfs2_pin(bd, 0);
        gfs2_log_unlock(sdp);
        unlock_buffer(bh);
 }
index cc34f271b3e7f470230efe58bbeeed3cf6682424..7bc3c45cd676d63bbc621e00a53485ede90c98e4 100644 (file)
@@ -33,6 +33,7 @@
 #include "log.h"
 #include "quota.h"
 #include "dir.h"
+#include "trace_gfs2.h"
 
 #define DO 0
 #define UNDO 1
@@ -775,6 +776,7 @@ static int init_journal(struct gfs2_sbd *sdp, int undo)
                /* Map the extents for this journal's blocks */
                map_journal_extents(sdp);
        }
+       trace_gfs2_log_blocks(sdp, atomic_read(&sdp->sd_log_blks_free));
 
        if (sdp->sd_lockstruct.ls_first) {
                unsigned int x;
index de3239731db8c0bcd0e41a57f6850313edb5774b..daa4ae341a292fe03175c378d441e767706fb9c8 100644 (file)
@@ -29,6 +29,7 @@
 #include "util.h"
 #include "log.h"
 #include "inode.h"
+#include "trace_gfs2.h"
 
 #define BFITNOENT ((u32)~0)
 #define NO_BLOCK ((u64)~0)
@@ -1519,7 +1520,7 @@ int gfs2_alloc_block(struct gfs2_inode *ip, u64 *bn, unsigned int *n)
        spin_lock(&sdp->sd_rindex_spin);
        rgd->rd_free_clone -= *n;
        spin_unlock(&sdp->sd_rindex_spin);
-
+       trace_gfs2_block_alloc(ip, block, *n, GFS2_BLKST_USED);
        *bn = block;
        return 0;
 
@@ -1571,7 +1572,7 @@ u64 gfs2_alloc_di(struct gfs2_inode *dip, u64 *generation)
        spin_lock(&sdp->sd_rindex_spin);
        rgd->rd_free_clone--;
        spin_unlock(&sdp->sd_rindex_spin);
-
+       trace_gfs2_block_alloc(dip, block, 1, GFS2_BLKST_DINODE);
        return block;
 }
 
@@ -1591,7 +1592,7 @@ void gfs2_free_data(struct gfs2_inode *ip, u64 bstart, u32 blen)
        rgd = rgblk_free(sdp, bstart, blen, GFS2_BLKST_FREE);
        if (!rgd)
                return;
-
+       trace_gfs2_block_alloc(ip, bstart, blen, GFS2_BLKST_FREE);
        rgd->rd_free += blen;
 
        gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1);
@@ -1619,7 +1620,7 @@ void gfs2_free_meta(struct gfs2_inode *ip, u64 bstart, u32 blen)
        rgd = rgblk_free(sdp, bstart, blen, GFS2_BLKST_FREE);
        if (!rgd)
                return;
-
+       trace_gfs2_block_alloc(ip, bstart, blen, GFS2_BLKST_FREE);
        rgd->rd_free += blen;
 
        gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1);
@@ -1642,6 +1643,7 @@ void gfs2_unlink_di(struct inode *inode)
        rgd = rgblk_free(sdp, blkno, 1, GFS2_BLKST_UNLINKED);
        if (!rgd)
                return;
+       trace_gfs2_block_alloc(ip, blkno, 1, GFS2_BLKST_UNLINKED);
        gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1);
        gfs2_rgrp_out(rgd, rgd->rd_bits[0].bi_bh->b_data);
        gfs2_trans_add_rg(rgd);
@@ -1673,6 +1675,7 @@ static void gfs2_free_uninit_di(struct gfs2_rgrpd *rgd, u64 blkno)
 void gfs2_free_di(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip)
 {
        gfs2_free_uninit_di(rgd, ip->i_no_addr);
+       trace_gfs2_block_alloc(ip, ip->i_no_addr, 1, GFS2_BLKST_FREE);
        gfs2_quota_change(ip, -1, ip->i_inode.i_uid, ip->i_inode.i_gid);
        gfs2_meta_wipe(ip, ip->i_no_addr, 1);
 }
index c8930b31cdf0b3011f80886987eedaa045c7de0d..0a68013364708dbcafe39398952defaa9f560450 100644 (file)
@@ -719,8 +719,6 @@ static void gfs2_put_super(struct super_block *sb)
        int error;
        struct gfs2_jdesc *jd;
 
-       lock_kernel();
-
        /*  Unfreeze the filesystem, if we need to  */
 
        mutex_lock(&sdp->sd_freeze_lock);
@@ -787,8 +785,6 @@ restart:
 
        /*  At this point, we're through participating in the lockspace  */
        gfs2_sys_fs_del(sdp);
-
-       unlock_kernel();
 }
 
 /**
diff --git a/fs/gfs2/trace_gfs2.h b/fs/gfs2/trace_gfs2.h
new file mode 100644 (file)
index 0000000..98d6ef1
--- /dev/null
@@ -0,0 +1,407 @@
+#if !defined(_TRACE_GFS2_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_GFS2_H
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM gfs2
+#define TRACE_INCLUDE_FILE trace_gfs2
+
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <linux/dlmconstants.h>
+#include <linux/gfs2_ondisk.h>
+#include "incore.h"
+#include "glock.h"
+
+#define dlm_state_name(nn) { DLM_LOCK_##nn, #nn }
+#define glock_trace_name(x) __print_symbolic(x,                \
+                           dlm_state_name(IV),         \
+                           dlm_state_name(NL),         \
+                           dlm_state_name(CR),         \
+                           dlm_state_name(CW),         \
+                           dlm_state_name(PR),         \
+                           dlm_state_name(PW),         \
+                           dlm_state_name(EX))
+
+#define block_state_name(x) __print_symbolic(x,                        \
+                           { GFS2_BLKST_FREE, "free" },        \
+                           { GFS2_BLKST_USED, "used" },        \
+                           { GFS2_BLKST_DINODE, "dinode" },    \
+                           { GFS2_BLKST_UNLINKED, "unlinked" })
+
+#define show_glock_flags(flags) __print_flags(flags, "",       \
+       {(1UL << GLF_LOCK),                     "l" },          \
+       {(1UL << GLF_DEMOTE),                   "D" },          \
+       {(1UL << GLF_PENDING_DEMOTE),           "d" },          \
+       {(1UL << GLF_DEMOTE_IN_PROGRESS),       "p" },          \
+       {(1UL << GLF_DIRTY),                    "y" },          \
+       {(1UL << GLF_LFLUSH),                   "f" },          \
+       {(1UL << GLF_INVALIDATE_IN_PROGRESS),   "i" },          \
+       {(1UL << GLF_REPLY_PENDING),            "r" },          \
+       {(1UL << GLF_INITIAL),                  "I" },          \
+       {(1UL << GLF_FROZEN),                   "F" })
+
+#ifndef NUMPTY
+#define NUMPTY
+static inline u8 glock_trace_state(unsigned int state)
+{
+       switch(state) {
+       case LM_ST_SHARED:
+               return DLM_LOCK_PR;
+       case LM_ST_DEFERRED:
+               return DLM_LOCK_CW;
+       case LM_ST_EXCLUSIVE:
+               return DLM_LOCK_EX;
+       }
+       return DLM_LOCK_NL;
+}
+#endif
+
+/* Section 1 - Locking
+ *
+ * Objectives:
+ * Latency: Remote demote request to state change
+ * Latency: Local lock request to state change
+ * Latency: State change to lock grant
+ * Correctness: Ordering of local lock state vs. I/O requests
+ * Correctness: Responses to remote demote requests
+ */
+
+/* General glock state change (DLM lock request completes) */
+TRACE_EVENT(gfs2_glock_state_change,
+
+       TP_PROTO(const struct gfs2_glock *gl, unsigned int new_state),
+
+       TP_ARGS(gl, new_state),
+
+       TP_STRUCT__entry(
+               __field(        dev_t,  dev                     )
+               __field(        u64,    glnum                   )
+               __field(        u32,    gltype                  )
+               __field(        u8,     cur_state               )
+               __field(        u8,     new_state               )
+               __field(        u8,     dmt_state               )
+               __field(        u8,     tgt_state               )
+               __field(        unsigned long,  flags           )
+       ),
+
+       TP_fast_assign(
+               __entry->dev            = gl->gl_sbd->sd_vfs->s_dev;
+               __entry->glnum          = gl->gl_name.ln_number;
+               __entry->gltype         = gl->gl_name.ln_type;
+               __entry->cur_state      = glock_trace_state(gl->gl_state);
+               __entry->new_state      = glock_trace_state(new_state);
+               __entry->tgt_state      = glock_trace_state(gl->gl_target);
+               __entry->dmt_state      = glock_trace_state(gl->gl_demote_state);
+               __entry->flags          = gl->gl_flags;
+       ),
+
+       TP_printk("%u,%u glock %d:%lld state %s to %s tgt:%s dmt:%s flags:%s",
+                 MAJOR(__entry->dev), MINOR(__entry->dev), __entry->gltype,
+                (unsigned long long)__entry->glnum,
+                 glock_trace_name(__entry->cur_state),
+                 glock_trace_name(__entry->new_state),
+                 glock_trace_name(__entry->tgt_state),
+                 glock_trace_name(__entry->dmt_state),
+                 show_glock_flags(__entry->flags))
+);
+
+/* State change -> unlocked, glock is being deallocated */
+TRACE_EVENT(gfs2_glock_put,
+
+       TP_PROTO(const struct gfs2_glock *gl),
+
+       TP_ARGS(gl),
+
+       TP_STRUCT__entry(
+               __field(        dev_t,  dev                     )
+               __field(        u64,    glnum                   )
+               __field(        u32,    gltype                  )
+               __field(        u8,     cur_state               )
+               __field(        unsigned long,  flags           )
+       ),
+
+       TP_fast_assign(
+               __entry->dev            = gl->gl_sbd->sd_vfs->s_dev;
+               __entry->gltype         = gl->gl_name.ln_type;
+               __entry->glnum          = gl->gl_name.ln_number;
+               __entry->cur_state      = glock_trace_state(gl->gl_state);
+               __entry->flags          = gl->gl_flags;
+       ),
+
+       TP_printk("%u,%u glock %d:%lld state %s => %s flags:%s",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                  __entry->gltype, (unsigned long long)__entry->glnum,
+                  glock_trace_name(__entry->cur_state),
+                 glock_trace_name(DLM_LOCK_IV),
+                 show_glock_flags(__entry->flags))
+
+);
+
+/* Callback (local or remote) requesting lock demotion */
+TRACE_EVENT(gfs2_demote_rq,
+
+       TP_PROTO(const struct gfs2_glock *gl),
+
+       TP_ARGS(gl),
+
+       TP_STRUCT__entry(
+               __field(        dev_t,  dev                     )
+               __field(        u64,    glnum                   )
+               __field(        u32,    gltype                  )
+               __field(        u8,     cur_state               )
+               __field(        u8,     dmt_state               )
+               __field(        unsigned long,  flags           )
+       ),
+
+       TP_fast_assign(
+               __entry->dev            = gl->gl_sbd->sd_vfs->s_dev;
+               __entry->gltype         = gl->gl_name.ln_type;
+               __entry->glnum          = gl->gl_name.ln_number;
+               __entry->cur_state      = glock_trace_state(gl->gl_state);
+               __entry->dmt_state      = glock_trace_state(gl->gl_demote_state);
+               __entry->flags          = gl->gl_flags;
+       ),
+
+       TP_printk("%u,%u glock %d:%lld demote %s to %s flags:%s",
+                 MAJOR(__entry->dev), MINOR(__entry->dev), __entry->gltype,
+                 (unsigned long long)__entry->glnum,
+                  glock_trace_name(__entry->cur_state),
+                  glock_trace_name(__entry->dmt_state),
+                 show_glock_flags(__entry->flags))
+
+);
+
+/* Promotion/grant of a glock */
+TRACE_EVENT(gfs2_promote,
+
+       TP_PROTO(const struct gfs2_holder *gh, int first),
+
+       TP_ARGS(gh, first),
+
+       TP_STRUCT__entry(
+               __field(        dev_t,  dev                     )
+               __field(        u64,    glnum                   )
+               __field(        u32,    gltype                  )
+               __field(        int,    first                   )
+               __field(        u8,     state                   )
+       ),
+
+       TP_fast_assign(
+               __entry->dev    = gh->gh_gl->gl_sbd->sd_vfs->s_dev;
+               __entry->glnum  = gh->gh_gl->gl_name.ln_number;
+               __entry->gltype = gh->gh_gl->gl_name.ln_type;
+               __entry->first  = first;
+               __entry->state  = glock_trace_state(gh->gh_state);
+       ),
+
+       TP_printk("%u,%u glock %u:%llu promote %s %s",
+                 MAJOR(__entry->dev), MINOR(__entry->dev), __entry->gltype,
+                 (unsigned long long)__entry->glnum,
+                 __entry->first ? "first": "other",
+                 glock_trace_name(__entry->state))
+);
+
+/* Queue/dequeue a lock request */
+TRACE_EVENT(gfs2_glock_queue,
+
+       TP_PROTO(const struct gfs2_holder *gh, int queue),
+
+       TP_ARGS(gh, queue),
+
+       TP_STRUCT__entry(
+               __field(        dev_t,  dev                     )
+               __field(        u64,    glnum                   )
+               __field(        u32,    gltype                  )
+               __field(        int,    queue                   )
+               __field(        u8,     state                   )
+       ),
+
+       TP_fast_assign(
+               __entry->dev    = gh->gh_gl->gl_sbd->sd_vfs->s_dev;
+               __entry->glnum  = gh->gh_gl->gl_name.ln_number;
+               __entry->gltype = gh->gh_gl->gl_name.ln_type;
+               __entry->queue  = queue;
+               __entry->state  = glock_trace_state(gh->gh_state);
+       ),
+
+       TP_printk("%u,%u glock %u:%llu %squeue %s",
+                 MAJOR(__entry->dev), MINOR(__entry->dev), __entry->gltype,
+                 (unsigned long long)__entry->glnum,
+                 __entry->queue ? "" : "de",
+                 glock_trace_name(__entry->state))
+);
+
+/* Section 2 - Log/journal
+ *
+ * Objectives:
+ * Latency: Log flush time
+ * Correctness: pin/unpin vs. disk I/O ordering
+ * Performance: Log usage stats
+ */
+
+/* Pin/unpin a block in the log */
+TRACE_EVENT(gfs2_pin,
+
+       TP_PROTO(const struct gfs2_bufdata *bd, int pin),
+
+       TP_ARGS(bd, pin),
+
+       TP_STRUCT__entry(
+               __field(        dev_t,  dev                     )
+               __field(        int,    pin                     )
+               __field(        u32,    len                     )
+               __field(        sector_t,       block           )
+               __field(        u64,    ino                     )
+       ),
+
+       TP_fast_assign(
+               __entry->dev            = bd->bd_gl->gl_sbd->sd_vfs->s_dev;
+               __entry->pin            = pin;
+               __entry->len            = bd->bd_bh->b_size;
+               __entry->block          = bd->bd_bh->b_blocknr;
+               __entry->ino            = bd->bd_gl->gl_name.ln_number;
+       ),
+
+       TP_printk("%u,%u log %s %llu/%lu inode %llu",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->pin ? "pin" : "unpin",
+                 (unsigned long long)__entry->block,
+                 (unsigned long)__entry->len,
+                 (unsigned long long)__entry->ino)
+);
+
+/* Flushing the log */
+TRACE_EVENT(gfs2_log_flush,
+
+       TP_PROTO(const struct gfs2_sbd *sdp, int start),
+
+       TP_ARGS(sdp, start),
+
+       TP_STRUCT__entry(
+               __field(        dev_t,  dev                     )
+               __field(        int,    start                   )
+               __field(        u64,    log_seq                 )
+       ),
+
+       TP_fast_assign(
+               __entry->dev            = sdp->sd_vfs->s_dev;
+               __entry->start          = start;
+               __entry->log_seq        = sdp->sd_log_sequence;
+       ),
+
+       TP_printk("%u,%u log flush %s %llu",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->start ? "start" : "end",
+                 (unsigned long long)__entry->log_seq)
+);
+
+/* Reserving/releasing blocks in the log */
+TRACE_EVENT(gfs2_log_blocks,
+
+       TP_PROTO(const struct gfs2_sbd *sdp, int blocks),
+
+       TP_ARGS(sdp, blocks),
+
+       TP_STRUCT__entry(
+               __field(        dev_t,  dev                     )
+               __field(        int,    blocks                  )
+       ),
+
+       TP_fast_assign(
+               __entry->dev            = sdp->sd_vfs->s_dev;
+               __entry->blocks         = blocks;
+       ),
+
+       TP_printk("%u,%u log reserve %d", MAJOR(__entry->dev),
+                 MINOR(__entry->dev), __entry->blocks)
+);
+
+/* Section 3 - bmap
+ *
+ * Objectives:
+ * Latency: Bmap request time
+ * Performance: Block allocator tracing
+ * Correctness: Test of disard generation vs. blocks allocated
+ */
+
+/* Map an extent of blocks, possibly a new allocation */
+TRACE_EVENT(gfs2_bmap,
+
+       TP_PROTO(const struct gfs2_inode *ip, const struct buffer_head *bh,
+               sector_t lblock, int create, int errno),
+
+       TP_ARGS(ip, bh, lblock, create, errno),
+
+       TP_STRUCT__entry(
+               __field(        dev_t,  dev                     )
+               __field(        sector_t, lblock                )
+               __field(        sector_t, pblock                )
+               __field(        u64,    inum                    )
+               __field(        unsigned long, state            )
+               __field(        u32,    len                     )
+               __field(        int,    create                  )
+               __field(        int,    errno                   )
+       ),
+
+       TP_fast_assign(
+               __entry->dev            = ip->i_gl->gl_sbd->sd_vfs->s_dev;
+               __entry->lblock         = lblock;
+               __entry->pblock         = buffer_mapped(bh) ?  bh->b_blocknr : 0;
+               __entry->inum           = ip->i_no_addr;
+               __entry->state          = bh->b_state;
+               __entry->len            = bh->b_size;
+               __entry->create         = create;
+               __entry->errno          = errno;
+       ),
+
+       TP_printk("%u,%u bmap %llu map %llu/%lu to %llu flags:%08lx %s %d",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 (unsigned long long)__entry->inum,
+                 (unsigned long long)__entry->lblock,
+                 (unsigned long)__entry->len,
+                 (unsigned long long)__entry->pblock,
+                 __entry->state, __entry->create ? "create " : "nocreate",
+                 __entry->errno)
+);
+
+/* Keep track of blocks as they are allocated/freed */
+TRACE_EVENT(gfs2_block_alloc,
+
+       TP_PROTO(const struct gfs2_inode *ip, u64 block, unsigned len,
+               u8 block_state),
+
+       TP_ARGS(ip, block, len, block_state),
+
+       TP_STRUCT__entry(
+               __field(        dev_t,  dev                     )
+               __field(        u64,    start                   )
+               __field(        u64,    inum                    )
+               __field(        u32,    len                     )
+               __field(        u8,     block_state             )
+       ),
+
+       TP_fast_assign(
+               __entry->dev            = ip->i_gl->gl_sbd->sd_vfs->s_dev;
+               __entry->start          = block;
+               __entry->inum           = ip->i_no_addr;
+               __entry->len            = len;
+               __entry->block_state    = block_state;
+       ),
+
+       TP_printk("%u,%u bmap %llu alloc %llu/%lu %s",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 (unsigned long long)__entry->inum,
+                 (unsigned long long)__entry->start,
+                 (unsigned long)__entry->len,
+                 block_state_name(__entry->block_state))
+);
+
+#endif /* _TRACE_GFS2_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#include <trace/define_trace.h>
+
index 0af36085eb28b21498036ce42f5fb682153b7baa..1a9c7878f8649b1df14531f66aa247fd2587e2c8 100644 (file)
@@ -556,27 +556,49 @@ int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
 
        /* add partitions */
        for (p = 1; p < state->limit; p++) {
-               sector_t size = state->parts[p].size;
-               sector_t from = state->parts[p].from;
+               sector_t size, from;
+try_scan:
+               size = state->parts[p].size;
                if (!size)
                        continue;
+
+               from = state->parts[p].from;
                if (from >= get_capacity(disk)) {
                        printk(KERN_WARNING
                               "%s: p%d ignored, start %llu is behind the end of the disk\n",
                               disk->disk_name, p, (unsigned long long) from);
                        continue;
                }
+
                if (from + size > get_capacity(disk)) {
-                       /*
-                        * we can not ignore partitions of broken tables
-                        * created by for example camera firmware, but we
-                        * limit them to the end of the disk to avoid
-                        * creating invalid block devices
-                        */
+                       struct block_device_operations *bdops = disk->fops;
+                       unsigned long long capacity;
+
                        printk(KERN_WARNING
-                              "%s: p%d size %llu limited to end of disk\n",
+                              "%s: p%d size %llu exceeds device capacity, ",
                               disk->disk_name, p, (unsigned long long) size);
-                       size = get_capacity(disk) - from;
+
+                       if (bdops->set_capacity &&
+                           (disk->flags & GENHD_FL_NATIVE_CAPACITY) == 0) {
+                               printk(KERN_CONT "enabling native capacity\n");
+                               capacity = bdops->set_capacity(disk, ~0ULL);
+                               disk->flags |= GENHD_FL_NATIVE_CAPACITY;
+                               if (capacity > get_capacity(disk)) {
+                                       set_capacity(disk, capacity);
+                                       check_disk_size_change(disk, bdev);
+                                       bdev->bd_invalidated = 0;
+                               }
+                               goto try_scan;
+                       } else {
+                               /*
+                                * we can not ignore partitions of broken tables
+                                * created by for example camera firmware, but
+                                * we limit them to the end of the disk to avoid
+                                * creating invalid block devices
+                                */
+                               printk(KERN_CONT "limited to end of disk\n");
+                               size = get_capacity(disk) - from;
+                       }
                }
                part = add_partition(disk, p, from, size,
                                     state->parts[p].flags);
index 4c9932a2503f9d465caa909f5475b9ba5e6cc194..eb62334cda2978489d6bd3fa8bad130ac885d335 100644 (file)
@@ -1,11 +1,33 @@
+header-y += auxvec.h
+header-y += bitsperlong.h
 header-y += errno-base.h
 header-y += errno.h
 header-y += fcntl.h
 header-y += ioctl.h
+header-y += ioctls.h
+header-y += ipcbuf.h
+header-y += mman-common.h
 header-y += mman.h
+header-y += msgbuf.h
+header-y += param.h
 header-y += poll.h
+header-y += posix_types.h
+header-y += sembuf.h
+header-y += setup.h
+header-y += shmbuf.h
+header-y += shmparam.h
+header-y += signal-defs.h
 header-y += signal.h
+header-y += socket.h
+header-y += sockios.h
+header-y += stat.h
 header-y += statfs.h
+header-y += swab.h
+header-y += termbits.h
+header-y += termios.h
+header-y += types.h
+header-y += ucontext.h
+header-y += unistd.h
 
 unifdef-y += int-l64.h
 unifdef-y += int-ll64.h
index 70d185534b9de84dc5340862250a5cbc6b441eb5..290910e4ede4a7412b0d4f21a5a400163f68b9c2 100644 (file)
@@ -9,6 +9,7 @@ unifdef-y += a.out.h
 endif
 unifdef-y += auxvec.h
 unifdef-y += byteorder.h
+unifdef-y += bitsperlong.h
 unifdef-y += errno.h
 unifdef-y += fcntl.h
 unifdef-y += ioctl.h
diff --git a/include/asm-generic/atomic-long.h b/include/asm-generic/atomic-long.h
new file mode 100644 (file)
index 0000000..b7babf0
--- /dev/null
@@ -0,0 +1,258 @@
+#ifndef _ASM_GENERIC_ATOMIC_LONG_H
+#define _ASM_GENERIC_ATOMIC_LONG_H
+/*
+ * Copyright (C) 2005 Silicon Graphics, Inc.
+ *     Christoph Lameter
+ *
+ * Allows to provide arch independent atomic definitions without the need to
+ * edit all arch specific atomic.h files.
+ */
+
+#include <asm/types.h>
+
+/*
+ * Suppport for atomic_long_t
+ *
+ * Casts for parameters are avoided for existing atomic functions in order to
+ * avoid issues with cast-as-lval under gcc 4.x and other limitations that the
+ * macros of a platform may have.
+ */
+
+#if BITS_PER_LONG == 64
+
+typedef atomic64_t atomic_long_t;
+
+#define ATOMIC_LONG_INIT(i)    ATOMIC64_INIT(i)
+
+static inline long atomic_long_read(atomic_long_t *l)
+{
+       atomic64_t *v = (atomic64_t *)l;
+
+       return (long)atomic64_read(v);
+}
+
+static inline void atomic_long_set(atomic_long_t *l, long i)
+{
+       atomic64_t *v = (atomic64_t *)l;
+
+       atomic64_set(v, i);
+}
+
+static inline void atomic_long_inc(atomic_long_t *l)
+{
+       atomic64_t *v = (atomic64_t *)l;
+
+       atomic64_inc(v);
+}
+
+static inline void atomic_long_dec(atomic_long_t *l)
+{
+       atomic64_t *v = (atomic64_t *)l;
+
+       atomic64_dec(v);
+}
+
+static inline void atomic_long_add(long i, atomic_long_t *l)
+{
+       atomic64_t *v = (atomic64_t *)l;
+
+       atomic64_add(i, v);
+}
+
+static inline void atomic_long_sub(long i, atomic_long_t *l)
+{
+       atomic64_t *v = (atomic64_t *)l;
+
+       atomic64_sub(i, v);
+}
+
+static inline int atomic_long_sub_and_test(long i, atomic_long_t *l)
+{
+       atomic64_t *v = (atomic64_t *)l;
+
+       return atomic64_sub_and_test(i, v);
+}
+
+static inline int atomic_long_dec_and_test(atomic_long_t *l)
+{
+       atomic64_t *v = (atomic64_t *)l;
+
+       return atomic64_dec_and_test(v);
+}
+
+static inline int atomic_long_inc_and_test(atomic_long_t *l)
+{
+       atomic64_t *v = (atomic64_t *)l;
+
+       return atomic64_inc_and_test(v);
+}
+
+static inline int atomic_long_add_negative(long i, atomic_long_t *l)
+{
+       atomic64_t *v = (atomic64_t *)l;
+
+       return atomic64_add_negative(i, v);
+}
+
+static inline long atomic_long_add_return(long i, atomic_long_t *l)
+{
+       atomic64_t *v = (atomic64_t *)l;
+
+       return (long)atomic64_add_return(i, v);
+}
+
+static inline long atomic_long_sub_return(long i, atomic_long_t *l)
+{
+       atomic64_t *v = (atomic64_t *)l;
+
+       return (long)atomic64_sub_return(i, v);
+}
+
+static inline long atomic_long_inc_return(atomic_long_t *l)
+{
+       atomic64_t *v = (atomic64_t *)l;
+
+       return (long)atomic64_inc_return(v);
+}
+
+static inline long atomic_long_dec_return(atomic_long_t *l)
+{
+       atomic64_t *v = (atomic64_t *)l;
+
+       return (long)atomic64_dec_return(v);
+}
+
+static inline long atomic_long_add_unless(atomic_long_t *l, long a, long u)
+{
+       atomic64_t *v = (atomic64_t *)l;
+
+       return (long)atomic64_add_unless(v, a, u);
+}
+
+#define atomic_long_inc_not_zero(l) atomic64_inc_not_zero((atomic64_t *)(l))
+
+#define atomic_long_cmpxchg(l, old, new) \
+       (atomic64_cmpxchg((atomic64_t *)(l), (old), (new)))
+#define atomic_long_xchg(v, new) \
+       (atomic64_xchg((atomic64_t *)(v), (new)))
+
+#else  /*  BITS_PER_LONG == 64  */
+
+typedef atomic_t atomic_long_t;
+
+#define ATOMIC_LONG_INIT(i)    ATOMIC_INIT(i)
+static inline long atomic_long_read(atomic_long_t *l)
+{
+       atomic_t *v = (atomic_t *)l;
+
+       return (long)atomic_read(v);
+}
+
+static inline void atomic_long_set(atomic_long_t *l, long i)
+{
+       atomic_t *v = (atomic_t *)l;
+
+       atomic_set(v, i);
+}
+
+static inline void atomic_long_inc(atomic_long_t *l)
+{
+       atomic_t *v = (atomic_t *)l;
+
+       atomic_inc(v);
+}
+
+static inline void atomic_long_dec(atomic_long_t *l)
+{
+       atomic_t *v = (atomic_t *)l;
+
+       atomic_dec(v);
+}
+
+static inline void atomic_long_add(long i, atomic_long_t *l)
+{
+       atomic_t *v = (atomic_t *)l;
+
+       atomic_add(i, v);
+}
+
+static inline void atomic_long_sub(long i, atomic_long_t *l)
+{
+       atomic_t *v = (atomic_t *)l;
+
+       atomic_sub(i, v);
+}
+
+static inline int atomic_long_sub_and_test(long i, atomic_long_t *l)
+{
+       atomic_t *v = (atomic_t *)l;
+
+       return atomic_sub_and_test(i, v);
+}
+
+static inline int atomic_long_dec_and_test(atomic_long_t *l)
+{
+       atomic_t *v = (atomic_t *)l;
+
+       return atomic_dec_and_test(v);
+}
+
+static inline int atomic_long_inc_and_test(atomic_long_t *l)
+{
+       atomic_t *v = (atomic_t *)l;
+
+       return atomic_inc_and_test(v);
+}
+
+static inline int atomic_long_add_negative(long i, atomic_long_t *l)
+{
+       atomic_t *v = (atomic_t *)l;
+
+       return atomic_add_negative(i, v);
+}
+
+static inline long atomic_long_add_return(long i, atomic_long_t *l)
+{
+       atomic_t *v = (atomic_t *)l;
+
+       return (long)atomic_add_return(i, v);
+}
+
+static inline long atomic_long_sub_return(long i, atomic_long_t *l)
+{
+       atomic_t *v = (atomic_t *)l;
+
+       return (long)atomic_sub_return(i, v);
+}
+
+static inline long atomic_long_inc_return(atomic_long_t *l)
+{
+       atomic_t *v = (atomic_t *)l;
+
+       return (long)atomic_inc_return(v);
+}
+
+static inline long atomic_long_dec_return(atomic_long_t *l)
+{
+       atomic_t *v = (atomic_t *)l;
+
+       return (long)atomic_dec_return(v);
+}
+
+static inline long atomic_long_add_unless(atomic_long_t *l, long a, long u)
+{
+       atomic_t *v = (atomic_t *)l;
+
+       return (long)atomic_add_unless(v, a, u);
+}
+
+#define atomic_long_inc_not_zero(l) atomic_inc_not_zero((atomic_t *)(l))
+
+#define atomic_long_cmpxchg(l, old, new) \
+       (atomic_cmpxchg((atomic_t *)(l), (old), (new)))
+#define atomic_long_xchg(v, new) \
+       (atomic_xchg((atomic_t *)(v), (new)))
+
+#endif  /*  BITS_PER_LONG == 64  */
+
+#endif  /*  _ASM_GENERIC_ATOMIC_LONG_H  */
index 81d3be459efb55e931662ad492c072749538d5e8..c99c64dc5f3dcfe1a9fbf192c44f30bf84a47ea9 100644 (file)
-#ifndef _ASM_GENERIC_ATOMIC_H
-#define _ASM_GENERIC_ATOMIC_H
 /*
- * Copyright (C) 2005 Silicon Graphics, Inc.
- *     Christoph Lameter
+ * Generic C implementation of atomic counter operations
+ * Originally implemented for MN10300.
  *
- * Allows to provide arch independent atomic definitions without the need to
- * edit all arch specific atomic.h files.
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
  */
+#ifndef __ASM_GENERIC_ATOMIC_H
+#define __ASM_GENERIC_ATOMIC_H
 
-#include <asm/types.h>
+#ifdef CONFIG_SMP
+#error not SMP safe
+#endif
 
 /*
- * Suppport for atomic_long_t
- *
- * Casts for parameters are avoided for existing atomic functions in order to
- * avoid issues with cast-as-lval under gcc 4.x and other limitations that the
- * macros of a platform may have.
+ * Atomic operations that C can't guarantee us.  Useful for
+ * resource counting etc..
  */
 
-#if BITS_PER_LONG == 64
-
-typedef atomic64_t atomic_long_t;
-
-#define ATOMIC_LONG_INIT(i)    ATOMIC64_INIT(i)
+#define ATOMIC_INIT(i) { (i) }
 
-static inline long atomic_long_read(atomic_long_t *l)
-{
-       atomic64_t *v = (atomic64_t *)l;
-
-       return (long)atomic64_read(v);
-}
-
-static inline void atomic_long_set(atomic_long_t *l, long i)
-{
-       atomic64_t *v = (atomic64_t *)l;
-
-       atomic64_set(v, i);
-}
-
-static inline void atomic_long_inc(atomic_long_t *l)
-{
-       atomic64_t *v = (atomic64_t *)l;
-
-       atomic64_inc(v);
-}
-
-static inline void atomic_long_dec(atomic_long_t *l)
-{
-       atomic64_t *v = (atomic64_t *)l;
-
-       atomic64_dec(v);
-}
-
-static inline void atomic_long_add(long i, atomic_long_t *l)
-{
-       atomic64_t *v = (atomic64_t *)l;
-
-       atomic64_add(i, v);
-}
-
-static inline void atomic_long_sub(long i, atomic_long_t *l)
-{
-       atomic64_t *v = (atomic64_t *)l;
-
-       atomic64_sub(i, v);
-}
-
-static inline int atomic_long_sub_and_test(long i, atomic_long_t *l)
-{
-       atomic64_t *v = (atomic64_t *)l;
-
-       return atomic64_sub_and_test(i, v);
-}
-
-static inline int atomic_long_dec_and_test(atomic_long_t *l)
-{
-       atomic64_t *v = (atomic64_t *)l;
-
-       return atomic64_dec_and_test(v);
-}
-
-static inline int atomic_long_inc_and_test(atomic_long_t *l)
-{
-       atomic64_t *v = (atomic64_t *)l;
-
-       return atomic64_inc_and_test(v);
-}
-
-static inline int atomic_long_add_negative(long i, atomic_long_t *l)
-{
-       atomic64_t *v = (atomic64_t *)l;
-
-       return atomic64_add_negative(i, v);
-}
-
-static inline long atomic_long_add_return(long i, atomic_long_t *l)
-{
-       atomic64_t *v = (atomic64_t *)l;
-
-       return (long)atomic64_add_return(i, v);
-}
-
-static inline long atomic_long_sub_return(long i, atomic_long_t *l)
-{
-       atomic64_t *v = (atomic64_t *)l;
+#ifdef __KERNEL__
 
-       return (long)atomic64_sub_return(i, v);
-}
-
-static inline long atomic_long_inc_return(atomic_long_t *l)
-{
-       atomic64_t *v = (atomic64_t *)l;
-
-       return (long)atomic64_inc_return(v);
-}
-
-static inline long atomic_long_dec_return(atomic_long_t *l)
-{
-       atomic64_t *v = (atomic64_t *)l;
-
-       return (long)atomic64_dec_return(v);
-}
-
-static inline long atomic_long_add_unless(atomic_long_t *l, long a, long u)
-{
-       atomic64_t *v = (atomic64_t *)l;
-
-       return (long)atomic64_add_unless(v, a, u);
-}
-
-#define atomic_long_inc_not_zero(l) atomic64_inc_not_zero((atomic64_t *)(l))
-
-#define atomic_long_cmpxchg(l, old, new) \
-       (atomic64_cmpxchg((atomic64_t *)(l), (old), (new)))
-#define atomic_long_xchg(v, new) \
-       (atomic64_xchg((atomic64_t *)(v), (new)))
-
-#else  /*  BITS_PER_LONG == 64  */
-
-typedef atomic_t atomic_long_t;
-
-#define ATOMIC_LONG_INIT(i)    ATOMIC_INIT(i)
-static inline long atomic_long_read(atomic_long_t *l)
-{
-       atomic_t *v = (atomic_t *)l;
-
-       return (long)atomic_read(v);
-}
+/**
+ * atomic_read - read atomic variable
+ * @v: pointer of type atomic_t
+ *
+ * Atomically reads the value of @v.  Note that the guaranteed
+ * useful range of an atomic_t is only 24 bits.
+ */
+#define atomic_read(v) ((v)->counter)
 
-static inline void atomic_long_set(atomic_long_t *l, long i)
-{
-       atomic_t *v = (atomic_t *)l;
+/**
+ * atomic_set - set atomic variable
+ * @v: pointer of type atomic_t
+ * @i: required value
+ *
+ * Atomically sets the value of @v to @i.  Note that the guaranteed
+ * useful range of an atomic_t is only 24 bits.
+ */
+#define atomic_set(v, i) (((v)->counter) = (i))
 
-       atomic_set(v, i);
-}
+#include <asm/system.h>
 
-static inline void atomic_long_inc(atomic_long_t *l)
+/**
+ * atomic_add_return - add integer to atomic variable
+ * @i: integer value to add
+ * @v: pointer of type atomic_t
+ *
+ * Atomically adds @i to @v and returns the result
+ * Note that the guaranteed useful range of an atomic_t is only 24 bits.
+ */
+static inline int atomic_add_return(int i, atomic_t *v)
 {
-       atomic_t *v = (atomic_t *)l;
-
-       atomic_inc(v);
-}
+       unsigned long flags;
+       int temp;
 
-static inline void atomic_long_dec(atomic_long_t *l)
-{
-       atomic_t *v = (atomic_t *)l;
+       local_irq_save(flags);
+       temp = v->counter;
+       temp += i;
+       v->counter = temp;
+       local_irq_restore(flags);
 
-       atomic_dec(v);
+       return temp;
 }
 
-static inline void atomic_long_add(long i, atomic_long_t *l)
+/**
+ * atomic_sub_return - subtract integer from atomic variable
+ * @i: integer value to subtract
+ * @v: pointer of type atomic_t
+ *
+ * Atomically subtracts @i from @v and returns the result
+ * Note that the guaranteed useful range of an atomic_t is only 24 bits.
+ */
+static inline int atomic_sub_return(int i, atomic_t *v)
 {
-       atomic_t *v = (atomic_t *)l;
-
-       atomic_add(i, v);
-}
+       unsigned long flags;
+       int temp;
 
-static inline void atomic_long_sub(long i, atomic_long_t *l)
-{
-       atomic_t *v = (atomic_t *)l;
+       local_irq_save(flags);
+       temp = v->counter;
+       temp -= i;
+       v->counter = temp;
+       local_irq_restore(flags);
 
-       atomic_sub(i, v);
+       return temp;
 }
 
-static inline int atomic_long_sub_and_test(long i, atomic_long_t *l)
+static inline int atomic_add_negative(int i, atomic_t *v)
 {
-       atomic_t *v = (atomic_t *)l;
-
-       return atomic_sub_and_test(i, v);
+       return atomic_add_return(i, v) < 0;
 }
 
-static inline int atomic_long_dec_and_test(atomic_long_t *l)
+static inline void atomic_add(int i, atomic_t *v)
 {
-       atomic_t *v = (atomic_t *)l;
-
-       return atomic_dec_and_test(v);
+       atomic_add_return(i, v);
 }
 
-static inline int atomic_long_inc_and_test(atomic_long_t *l)
+static inline void atomic_sub(int i, atomic_t *v)
 {
-       atomic_t *v = (atomic_t *)l;
-
-       return atomic_inc_and_test(v);
+       atomic_sub_return(i, v);
 }
 
-static inline int atomic_long_add_negative(long i, atomic_long_t *l)
+static inline void atomic_inc(atomic_t *v)
 {
-       atomic_t *v = (atomic_t *)l;
-
-       return atomic_add_negative(i, v);
+       atomic_add_return(1, v);
 }
 
-static inline long atomic_long_add_return(long i, atomic_long_t *l)
+static inline void atomic_dec(atomic_t *v)
 {
-       atomic_t *v = (atomic_t *)l;
-
-       return (long)atomic_add_return(i, v);
+       atomic_sub_return(1, v);
 }
 
-static inline long atomic_long_sub_return(long i, atomic_long_t *l)
-{
-       atomic_t *v = (atomic_t *)l;
+#define atomic_dec_return(v)           atomic_sub_return(1, (v))
+#define atomic_inc_return(v)           atomic_add_return(1, (v))
 
-       return (long)atomic_sub_return(i, v);
-}
+#define atomic_sub_and_test(i, v)      (atomic_sub_return((i), (v)) == 0)
+#define atomic_dec_and_test(v)         (atomic_sub_return(1, (v)) == 0)
+#define atomic_inc_and_test(v)         (atomic_add_return(1, (v)) == 0)
 
-static inline long atomic_long_inc_return(atomic_long_t *l)
-{
-       atomic_t *v = (atomic_t *)l;
+#define atomic_add_unless(v, a, u)                             \
+({                                                             \
+       int c, old;                                             \
+       c = atomic_read(v);                                     \
+       while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c) \
+               c = old;                                        \
+       c != (u);                                               \
+})
 
-       return (long)atomic_inc_return(v);
-}
+#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
-static inline long atomic_long_dec_return(atomic_long_t *l)
+static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr)
 {
-       atomic_t *v = (atomic_t *)l;
+       unsigned long flags;
 
-       return (long)atomic_dec_return(v);
+       mask = ~mask;
+       local_irq_save(flags);
+       *addr &= mask;
+       local_irq_restore(flags);
 }
 
-static inline long atomic_long_add_unless(atomic_long_t *l, long a, long u)
-{
-       atomic_t *v = (atomic_t *)l;
+#define atomic_xchg(ptr, v)            (xchg(&(ptr)->counter, (v)))
+#define atomic_cmpxchg(v, old, new)    (cmpxchg(&((v)->counter), (old), (new)))
 
-       return (long)atomic_add_unless(v, a, u);
-}
+#define cmpxchg_local(ptr, o, n)                                              \
+       ((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), (unsigned long)(o),\
+                       (unsigned long)(n), sizeof(*(ptr))))
 
-#define atomic_long_inc_not_zero(l) atomic_inc_not_zero((atomic_t *)(l))
+#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n))
 
-#define atomic_long_cmpxchg(l, old, new) \
-       (atomic_cmpxchg((atomic_t *)(l), (old), (new)))
-#define atomic_long_xchg(v, new) \
-       (atomic_xchg((atomic_t *)(v), (new)))
+/* Assume that atomic operations are already serializing */
+#define smp_mb__before_atomic_dec()    barrier()
+#define smp_mb__after_atomic_dec()     barrier()
+#define smp_mb__before_atomic_inc()    barrier()
+#define smp_mb__after_atomic_inc()     barrier()
 
-#endif  /*  BITS_PER_LONG == 64  */
+#include <asm-generic/atomic-long.h>
 
-#endif  /*  _ASM_GENERIC_ATOMIC_H  */
+#endif /* __KERNEL__ */
+#endif /* __ASM_GENERIC_ATOMIC_H */
diff --git a/include/asm-generic/auxvec.h b/include/asm-generic/auxvec.h
new file mode 100644 (file)
index 0000000..b99573b
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __ASM_GENERIC_AUXVEC_H
+#define __ASM_GENERIC_AUXVEC_H
+/*
+ * Not all architectures need their own auxvec.h, the most
+ * common definitions are already in linux/auxvec.h.
+ */
+
+#endif /* __ASM_GENERIC_AUXVEC_H */
index c9f369c4bd7ed3d9fd2e65ff5ee65634d48cf77e..a54f4421a24d8bc738815fdf9c1c5884795a94f3 100644 (file)
@@ -1,19 +1,29 @@
-#ifndef _ASM_GENERIC_BITOPS_H_
-#define _ASM_GENERIC_BITOPS_H_
+#ifndef __ASM_GENERIC_BITOPS_H
+#define __ASM_GENERIC_BITOPS_H
 
 /*
  * For the benefit of those who are trying to port Linux to another
  * architecture, here are some C-language equivalents.  You should
  * recode these in the native assembly language, if at all possible.
- * 
+ *
  * C language equivalents written by Theodore Ts'o, 9/26/92
  */
 
-#include <asm-generic/bitops/atomic.h>
-#include <asm-generic/bitops/non-atomic.h>
+#include <linux/irqflags.h>
+#include <linux/compiler.h>
+
+/*
+ * clear_bit may not imply a memory barrier
+ */
+#ifndef smp_mb__before_clear_bit
+#define smp_mb__before_clear_bit()     smp_mb()
+#define smp_mb__after_clear_bit()      smp_mb()
+#endif
+
 #include <asm-generic/bitops/__ffs.h>
 #include <asm-generic/bitops/ffz.h>
 #include <asm-generic/bitops/fls.h>
+#include <asm-generic/bitops/__fls.h>
 #include <asm-generic/bitops/fls64.h>
 #include <asm-generic/bitops/find.h>
 
 #include <asm-generic/bitops/hweight.h>
 #include <asm-generic/bitops/lock.h>
 
+#include <asm-generic/bitops/atomic.h>
+#include <asm-generic/bitops/non-atomic.h>
 #include <asm-generic/bitops/ext2-non-atomic.h>
 #include <asm-generic/bitops/ext2-atomic.h>
 #include <asm-generic/bitops/minix.h>
 
-#endif /* _ASM_GENERIC_BITOPS_H */
+#endif /* __ASM_GENERIC_BITOPS_H */
index 4657f3e410fce4a71ca7e7903f97e92a92676bd9..c8946465e63ac872113c74241e06028aba8086f3 100644 (file)
@@ -2,6 +2,7 @@
 #define _ASM_GENERIC_BITOPS_ATOMIC_H_
 
 #include <asm/types.h>
+#include <asm/system.h>
 
 #ifdef CONFIG_SMP
 #include <asm/spinlock.h>
diff --git a/include/asm-generic/bitsperlong.h b/include/asm-generic/bitsperlong.h
new file mode 100644 (file)
index 0000000..4ae54e0
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef __ASM_GENERIC_BITS_PER_LONG
+#define __ASM_GENERIC_BITS_PER_LONG
+
+/*
+ * There seems to be no way of detecting this automatically from user
+ * space, so 64 bit architectures should override this in their
+ * bitsperlong.h. In particular, an architecture that supports
+ * both 32 and 64 bit user space must not rely on CONFIG_64BIT
+ * to decide it, but rather check a compiler provided macro.
+ */
+#ifndef __BITS_PER_LONG
+#define __BITS_PER_LONG 32
+#endif
+
+#ifdef __KERNEL__
+
+#ifdef CONFIG_64BIT
+#define BITS_PER_LONG 64
+#else
+#define BITS_PER_LONG 32
+#endif /* CONFIG_64BIT */
+
+/*
+ * FIXME: The check currently breaks x86-64 build, so it's
+ * temporarily disabled. Please fix x86-64 and reenable
+ */
+#if 0 && BITS_PER_LONG != __BITS_PER_LONG
+#error Inconsistent word size. Check asm/bitsperlong.h
+#endif
+
+#endif /* __KERNEL__ */
+#endif /* __ASM_GENERIC_BITS_PER_LONG */
diff --git a/include/asm-generic/bugs.h b/include/asm-generic/bugs.h
new file mode 100644 (file)
index 0000000..6c4f62e
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __ASM_GENERIC_BUGS_H
+#define __ASM_GENERIC_BUGS_H
+/*
+ * This file is included by 'init/main.c' to check for
+ * architecture-dependent bugs.
+ */
+
+static inline void check_bugs(void) { }
+
+#endif /* __ASM_GENERIC_BUGS_H */
diff --git a/include/asm-generic/cache.h b/include/asm-generic/cache.h
new file mode 100644 (file)
index 0000000..1bfcfe5
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __ASM_GENERIC_CACHE_H
+#define __ASM_GENERIC_CACHE_H
+/*
+ * 32 bytes appears to be the most common cache line size,
+ * so make that the default here. Architectures with larger
+ * cache lines need to provide their own cache.h.
+ */
+
+#define L1_CACHE_SHIFT         5
+#define L1_CACHE_BYTES         (1 << L1_CACHE_SHIFT)
+
+#endif /* __ASM_GENERIC_CACHE_H */
diff --git a/include/asm-generic/cacheflush.h b/include/asm-generic/cacheflush.h
new file mode 100644 (file)
index 0000000..ba4ec39
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef __ASM_CACHEFLUSH_H
+#define __ASM_CACHEFLUSH_H
+
+/* Keep includes the same across arches.  */
+#include <linux/mm.h>
+
+/*
+ * The cache doesn't need to be flushed when TLB entries change when
+ * the cache is mapped to physical memory, not virtual memory
+ */
+#define flush_cache_all()                      do { } while (0)
+#define flush_cache_mm(mm)                     do { } while (0)
+#define flush_cache_dup_mm(mm)                 do { } while (0)
+#define flush_cache_range(vma, start, end)     do { } while (0)
+#define flush_cache_page(vma, vmaddr, pfn)     do { } while (0)
+#define flush_dcache_page(page)                        do { } while (0)
+#define flush_dcache_mmap_lock(mapping)                do { } while (0)
+#define flush_dcache_mmap_unlock(mapping)      do { } while (0)
+#define flush_icache_range(start, end)         do { } while (0)
+#define flush_icache_page(vma,pg)              do { } while (0)
+#define flush_icache_user_range(vma,pg,adr,len)        do { } while (0)
+#define flush_cache_vmap(start, end)           do { } while (0)
+#define flush_cache_vunmap(start, end)         do { } while (0)
+
+#define copy_to_user_page(vma, page, vaddr, dst, src, len) \
+       memcpy(dst, src, len)
+#define copy_from_user_page(vma, page, vaddr, dst, src, len) \
+       memcpy(dst, src, len)
+
+#endif /* __ASM_CACHEFLUSH_H */
diff --git a/include/asm-generic/checksum.h b/include/asm-generic/checksum.h
new file mode 100644 (file)
index 0000000..4647c76
--- /dev/null
@@ -0,0 +1,79 @@
+#ifndef __ASM_GENERIC_CHECKSUM_H
+#define __ASM_GENERIC_CHECKSUM_H
+
+/*
+ * computes the checksum of a memory block at buff, length len,
+ * and adds in "sum" (32-bit)
+ *
+ * returns a 32-bit number suitable for feeding into itself
+ * or csum_tcpudp_magic
+ *
+ * this function must be called with even lengths, except
+ * for the last fragment, which may be odd
+ *
+ * it's best to have buff aligned on a 32-bit boundary
+ */
+extern __wsum csum_partial(const void *buff, int len, __wsum sum);
+
+/*
+ * the same as csum_partial, but copies from src while it
+ * checksums
+ *
+ * here even more important to align src and dst on a 32-bit (or even
+ * better 64-bit) boundary
+ */
+extern __wsum csum_partial_copy(const void *src, void *dst, int len, __wsum sum);
+
+/*
+ * the same as csum_partial_copy, but copies from user space.
+ *
+ * here even more important to align src and dst on a 32-bit (or even
+ * better 64-bit) boundary
+ */
+extern __wsum csum_partial_copy_from_user(const void __user *src, void *dst,
+                                       int len, __wsum sum, int *csum_err);
+
+#define csum_partial_copy_nocheck(src, dst, len, sum)  \
+       csum_partial_copy((src), (dst), (len), (sum))
+
+/*
+ * This is a version of ip_compute_csum() optimized for IP headers,
+ * which always checksum on 4 octet boundaries.
+ */
+extern __sum16 ip_fast_csum(const void *iph, unsigned int ihl);
+
+/*
+ * Fold a partial checksum
+ */
+static inline __sum16 csum_fold(__wsum csum)
+{
+       u32 sum = (__force u32)csum;
+       sum = (sum & 0xffff) + (sum >> 16);
+       sum = (sum & 0xffff) + (sum >> 16);
+       return (__force __sum16)~sum;
+}
+
+#ifndef csum_tcpudp_nofold
+/*
+ * computes the checksum of the TCP/UDP pseudo-header
+ * returns a 16-bit checksum, already complemented
+ */
+extern __wsum
+csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len,
+               unsigned short proto, __wsum sum);
+#endif
+
+static inline __sum16
+csum_tcpudp_magic(__be32 saddr, __be32 daddr, unsigned short len,
+                 unsigned short proto, __wsum sum)
+{
+       return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
+}
+
+/*
+ * this routine is used for miscellaneous IP-like checksums, mainly
+ * in icmp.c
+ */
+extern __sum16 ip_compute_csum(const void *buff, int len);
+
+#endif /* __ASM_GENERIC_CHECKSUM_H */
diff --git a/include/asm-generic/current.h b/include/asm-generic/current.h
new file mode 100644 (file)
index 0000000..5e86f6a
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef __ASM_GENERIC_CURRENT_H
+#define __ASM_GENERIC_CURRENT_H
+
+#include <linux/thread_info.h>
+
+#define get_current() (current_thread_info()->task)
+#define current get_current()
+
+#endif /* __ASM_GENERIC_CURRENT_H */
diff --git a/include/asm-generic/delay.h b/include/asm-generic/delay.h
new file mode 100644 (file)
index 0000000..4586fec
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef __ASM_GENERIC_DELAY_H
+#define __ASM_GENERIC_DELAY_H
+
+extern void __udelay(unsigned long usecs);
+extern void __delay(unsigned long loops);
+
+#define udelay(n) __udelay(n)
+
+#endif /* __ASM_GENERIC_DELAY_H */
diff --git a/include/asm-generic/dma.h b/include/asm-generic/dma.h
new file mode 100644 (file)
index 0000000..9dfc3a7
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __ASM_GENERIC_DMA_H
+#define __ASM_GENERIC_DMA_H
+/*
+ * This file traditionally describes the i8237 PC style DMA controller.
+ * Most architectures don't have these any more and can get the minimal
+ * implementation from kernel/dma.c by not defining MAX_DMA_CHANNELS.
+ *
+ * Some code relies on seeing MAX_DMA_ADDRESS though.
+ */
+#define MAX_DMA_ADDRESS PAGE_OFFSET
+
+extern int request_dma(unsigned int dmanr, const char *device_id);
+extern void free_dma(unsigned int dmanr);
+
+#endif /* __ASM_GENERIC_DMA_H */
diff --git a/include/asm-generic/fb.h b/include/asm-generic/fb.h
new file mode 100644 (file)
index 0000000..fe8ca7f
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __ASM_GENERIC_FB_H_
+#define __ASM_GENERIC_FB_H_
+#include <linux/fb.h>
+
+#define fb_pgprotect(...) do {} while (0)
+
+static inline int fb_is_primary_device(struct fb_info *info)
+{
+       return 0;
+}
+
+#endif /* __ASM_GENERIC_FB_H_ */
diff --git a/include/asm-generic/getorder.h b/include/asm-generic/getorder.h
new file mode 100644 (file)
index 0000000..67e7245
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef __ASM_GENERIC_GETORDER_H
+#define __ASM_GENERIC_GETORDER_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/compiler.h>
+
+/* Pure 2^n version of get_order */
+static inline __attribute_const__ int get_order(unsigned long size)
+{
+       int order;
+
+       size = (size - 1) >> (PAGE_SHIFT - 1);
+       order = -1;
+       do {
+               size >>= 1;
+               order++;
+       } while (size);
+       return order;
+}
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ASM_GENERIC_GETORDER_H */
diff --git a/include/asm-generic/hardirq.h b/include/asm-generic/hardirq.h
new file mode 100644 (file)
index 0000000..3d5d2c9
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef __ASM_GENERIC_HARDIRQ_H
+#define __ASM_GENERIC_HARDIRQ_H
+
+#include <linux/cache.h>
+#include <linux/threads.h>
+#include <linux/irq.h>
+
+typedef struct {
+       unsigned long __softirq_pending;
+} ____cacheline_aligned irq_cpustat_t;
+
+#include <linux/irq_cpustat.h> /* Standard mappings for irq_cpustat_t above */
+
+#ifndef HARDIRQ_BITS
+#define HARDIRQ_BITS   8
+#endif
+
+/*
+ * The hardirq mask has to be large enough to have
+ * space for potentially all IRQ sources in the system
+ * nesting on a single CPU:
+ */
+#if (1 << HARDIRQ_BITS) < NR_IRQS
+# error HARDIRQ_BITS is too low!
+#endif
+
+#ifndef ack_bad_irq
+static inline void ack_bad_irq(unsigned int irq)
+{
+       printk(KERN_CRIT "unexpected IRQ trap at vector %02x\n", irq);
+}
+#endif
+
+#endif /* __ASM_GENERIC_HARDIRQ_H */
diff --git a/include/asm-generic/hw_irq.h b/include/asm-generic/hw_irq.h
new file mode 100644 (file)
index 0000000..89036d7
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef __ASM_GENERIC_HW_IRQ_H
+#define __ASM_GENERIC_HW_IRQ_H
+/*
+ * hw_irq.h has internal declarations for the low-level interrupt
+ * controller, like the original i8259A.
+ * In general, this is not needed for new architectures.
+ */
+
+#endif /* __ASM_GENERIC_HW_IRQ_H */
index 2af9b75d77db330cc5da1b64fee6a8eb0807f23b..1ca3efc976cc0b66541c4a7dfa8f4800361bf05f 100644 (file)
@@ -8,6 +8,8 @@
 #ifndef _ASM_GENERIC_INT_L64_H
 #define _ASM_GENERIC_INT_L64_H
 
+#include <asm/bitsperlong.h>
+
 #ifndef __ASSEMBLY__
 /*
  * __xx is ok: it doesn't pollute the POSIX namespace. Use these in the
index f9bc9ac29b36edafca055190ca4df361f9152f52..f394147c07397b776185f9e502b3966035c838c7 100644 (file)
@@ -8,6 +8,8 @@
 #ifndef _ASM_GENERIC_INT_LL64_H
 #define _ASM_GENERIC_INT_LL64_H
 
+#include <asm/bitsperlong.h>
+
 #ifndef __ASSEMBLY__
 /*
  * __xx is ok: it doesn't pollute the POSIX namespace. Use these in the
diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
new file mode 100644 (file)
index 0000000..bcee636
--- /dev/null
@@ -0,0 +1,300 @@
+/* Generic I/O port emulation, based on MN10300 code
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#ifndef __ASM_GENERIC_IO_H
+#define __ASM_GENERIC_IO_H
+
+#include <asm/page.h> /* I/O is all done through memory accesses */
+#include <asm/cacheflush.h>
+#include <linux/types.h>
+
+#ifdef CONFIG_GENERIC_IOMAP
+#include <asm-generic/iomap.h>
+#endif
+
+#define mmiowb() do {} while (0)
+
+/*****************************************************************************/
+/*
+ * readX/writeX() are used to access memory mapped devices. On some
+ * architectures the memory mapped IO stuff needs to be accessed
+ * differently. On the simple architectures, we just read/write the
+ * memory location directly.
+ */
+static inline u8 __raw_readb(const volatile void __iomem *addr)
+{
+       return *(const volatile u8 __force *) addr;
+}
+
+static inline u16 __raw_readw(const volatile void __iomem *addr)
+{
+       return *(const volatile u16 __force *) addr;
+}
+
+static inline u32 __raw_readl(const volatile void __iomem *addr)
+{
+       return *(const volatile u32 __force *) addr;
+}
+
+#define readb __raw_readb
+#define readw(addr) __le16_to_cpu(__raw_readw(addr))
+#define readl(addr) __le32_to_cpu(__raw_readl(addr))
+
+static inline void __raw_writeb(u8 b, volatile void __iomem *addr)
+{
+       *(volatile u8 __force *) addr = b;
+}
+
+static inline void __raw_writew(u16 b, volatile void __iomem *addr)
+{
+       *(volatile u16 __force *) addr = b;
+}
+
+static inline void __raw_writel(u32 b, volatile void __iomem *addr)
+{
+       *(volatile u32 __force *) addr = b;
+}
+
+#define writeb __raw_writeb
+#define writew(b,addr) __raw_writew(__cpu_to_le16(b),addr)
+#define writel(b,addr) __raw_writel(__cpu_to_le32(b),addr)
+
+#ifdef CONFIG_64BIT
+static inline u64 __raw_readq(const volatile void __iomem *addr)
+{
+       return *(const volatile u64 __force *) addr;
+}
+#define readq(addr) __le64_to_cpu(__raw_readq(addr))
+
+static inline void __raw_writeq(u64 b, volatile void __iomem *addr)
+{
+       *(volatile u64 __force *) addr = b;
+}
+#define writeq(b,addr) __raw_writeq(__cpu_to_le64(b),addr)
+#endif
+
+/*****************************************************************************/
+/*
+ * traditional input/output functions
+ */
+
+static inline u8 inb(unsigned long addr)
+{
+       return readb((volatile void __iomem *) addr);
+}
+
+static inline u16 inw(unsigned long addr)
+{
+       return readw((volatile void __iomem *) addr);
+}
+
+static inline u32 inl(unsigned long addr)
+{
+       return readl((volatile void __iomem *) addr);
+}
+
+static inline void outb(u8 b, unsigned long addr)
+{
+       writeb(b, (volatile void __iomem *) addr);
+}
+
+static inline void outw(u16 b, unsigned long addr)
+{
+       writew(b, (volatile void __iomem *) addr);
+}
+
+static inline void outl(u32 b, unsigned long addr)
+{
+       writel(b, (volatile void __iomem *) addr);
+}
+
+#define inb_p(addr)    inb(addr)
+#define inw_p(addr)    inw(addr)
+#define inl_p(addr)    inl(addr)
+#define outb_p(x, addr)        outb((x), (addr))
+#define outw_p(x, addr)        outw((x), (addr))
+#define outl_p(x, addr)        outl((x), (addr))
+
+static inline void insb(unsigned long addr, void *buffer, int count)
+{
+       if (count) {
+               u8 *buf = buffer;
+               do {
+                       u8 x = inb(addr);
+                       *buf++ = x;
+               } while (--count);
+       }
+}
+
+static inline void insw(unsigned long addr, void *buffer, int count)
+{
+       if (count) {
+               u16 *buf = buffer;
+               do {
+                       u16 x = inw(addr);
+                       *buf++ = x;
+               } while (--count);
+       }
+}
+
+static inline void insl(unsigned long addr, void *buffer, int count)
+{
+       if (count) {
+               u32 *buf = buffer;
+               do {
+                       u32 x = inl(addr);
+                       *buf++ = x;
+               } while (--count);
+       }
+}
+
+static inline void outsb(unsigned long addr, const void *buffer, int count)
+{
+       if (count) {
+               const u8 *buf = buffer;
+               do {
+                       outb(*buf++, addr);
+               } while (--count);
+       }
+}
+
+static inline void outsw(unsigned long addr, const void *buffer, int count)
+{
+       if (count) {
+               const u16 *buf = buffer;
+               do {
+                       outw(*buf++, addr);
+               } while (--count);
+       }
+}
+
+static inline void outsl(unsigned long addr, const void *buffer, int count)
+{
+       if (count) {
+               const u32 *buf = buffer;
+               do {
+                       outl(*buf++, addr);
+               } while (--count);
+       }
+}
+
+#ifndef CONFIG_GENERIC_IOMAP
+#define ioread8(addr)          readb(addr)
+#define ioread16(addr)         readw(addr)
+#define ioread32(addr)         readl(addr)
+
+#define iowrite8(v, addr)      writeb((v), (addr))
+#define iowrite16(v, addr)     writew((v), (addr))
+#define iowrite32(v, addr)     writel((v), (addr))
+
+#define ioread8_rep(p, dst, count) \
+       insb((unsigned long) (p), (dst), (count))
+#define ioread16_rep(p, dst, count) \
+       insw((unsigned long) (p), (dst), (count))
+#define ioread32_rep(p, dst, count) \
+       insl((unsigned long) (p), (dst), (count))
+
+#define iowrite8_rep(p, src, count) \
+       outsb((unsigned long) (p), (src), (count))
+#define iowrite16_rep(p, src, count) \
+       outsw((unsigned long) (p), (src), (count))
+#define iowrite32_rep(p, src, count) \
+       outsl((unsigned long) (p), (src), (count))
+#endif /* CONFIG_GENERIC_IOMAP */
+
+
+#define IO_SPACE_LIMIT 0xffffffff
+
+#ifdef __KERNEL__
+
+#include <linux/vmalloc.h>
+#define __io_virt(x) ((void __force *) (x))
+
+#ifndef CONFIG_GENERIC_IOMAP
+/* Create a virtual mapping cookie for a PCI BAR (memory or IO) */
+struct pci_dev;
+extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max);
+static inline void pci_iounmap(struct pci_dev *dev, void __iomem *p)
+{
+}
+#endif /* CONFIG_GENERIC_IOMAP */
+
+/*
+ * Change virtual addresses to physical addresses and vv.
+ * These are pretty trivial
+ */
+static inline unsigned long virt_to_phys(volatile void *address)
+{
+       return __pa((unsigned long)address);
+}
+
+static inline void *phys_to_virt(unsigned long address)
+{
+       return __va(address);
+}
+
+/*
+ * Change "struct page" to physical address.
+ */
+static inline void __iomem *ioremap(phys_addr_t offset, unsigned long size)
+{
+       return (void __iomem*) (unsigned long)offset;
+}
+
+#define __ioremap(offset, size, flags) ioremap(offset, size)
+
+#ifndef ioremap_nocache
+#define ioremap_nocache ioremap
+#endif
+
+#ifndef ioremap_wc
+#define ioremap_wc ioremap_nocache
+#endif
+
+static inline void iounmap(void *addr)
+{
+}
+
+#ifndef CONFIG_GENERIC_IOMAP
+static inline void __iomem *ioport_map(unsigned long port, unsigned int nr)
+{
+       return (void __iomem *) port;
+}
+
+static inline void ioport_unmap(void __iomem *p)
+{
+}
+#else /* CONFIG_GENERIC_IOMAP */
+extern void __iomem *ioport_map(unsigned long port, unsigned int nr);
+extern void ioport_unmap(void __iomem *p);
+#endif /* CONFIG_GENERIC_IOMAP */
+
+#define xlate_dev_kmem_ptr(p)  p
+#define xlate_dev_mem_ptr(p)   ((void *) (p))
+
+#ifndef virt_to_bus
+static inline unsigned long virt_to_bus(volatile void *address)
+{
+       return ((unsigned long) address);
+}
+
+static inline void *bus_to_virt(unsigned long address)
+{
+       return (void *) address;
+}
+#endif
+
+#define memset_io(a, b, c)     memset(__io_virt(a), (b), (c))
+#define memcpy_fromio(a, b, c) memcpy((a), __io_virt(b), (c))
+#define memcpy_toio(a, b, c)   memcpy(__io_virt(a), (b), (c))
+
+#endif /* __KERNEL__ */
+
+#endif /* __ASM_GENERIC_IO_H */
diff --git a/include/asm-generic/ioctls.h b/include/asm-generic/ioctls.h
new file mode 100644 (file)
index 0000000..a799e20
--- /dev/null
@@ -0,0 +1,110 @@
+#ifndef __ASM_GENERIC_IOCTLS_H
+#define __ASM_GENERIC_IOCTLS_H
+
+#include <linux/ioctl.h>
+
+/*
+ * These are the most common definitions for tty ioctl numbers.
+ * Most of them do not use the recommended _IOC(), but there is
+ * probably some source code out there hardcoding the number,
+ * so we might as well use them for all new platforms.
+ *
+ * The architectures that use different values here typically
+ * try to be compatible with some Unix variants for the same
+ * architecture.
+ */
+
+/* 0x54 is just a magic number to make these relatively unique ('T') */
+
+#define TCGETS         0x5401
+#define TCSETS         0x5402
+#define TCSETSW                0x5403
+#define TCSETSF                0x5404
+#define TCGETA         0x5405
+#define TCSETA         0x5406
+#define TCSETAW                0x5407
+#define TCSETAF                0x5408
+#define TCSBRK         0x5409
+#define TCXONC         0x540A
+#define TCFLSH         0x540B
+#define TIOCEXCL       0x540C
+#define TIOCNXCL       0x540D
+#define TIOCSCTTY      0x540E
+#define TIOCGPGRP      0x540F
+#define TIOCSPGRP      0x5410
+#define TIOCOUTQ       0x5411
+#define TIOCSTI                0x5412
+#define TIOCGWINSZ     0x5413
+#define TIOCSWINSZ     0x5414
+#define TIOCMGET       0x5415
+#define TIOCMBIS       0x5416
+#define TIOCMBIC       0x5417
+#define TIOCMSET       0x5418
+#define TIOCGSOFTCAR   0x5419
+#define TIOCSSOFTCAR   0x541A
+#define FIONREAD       0x541B
+#define TIOCINQ                FIONREAD
+#define TIOCLINUX      0x541C
+#define TIOCCONS       0x541D
+#define TIOCGSERIAL    0x541E
+#define TIOCSSERIAL    0x541F
+#define TIOCPKT                0x5420
+#define FIONBIO                0x5421
+#define TIOCNOTTY      0x5422
+#define TIOCSETD       0x5423
+#define TIOCGETD       0x5424
+#define TCSBRKP                0x5425  /* Needed for POSIX tcsendbreak() */
+#define TIOCSBRK       0x5427  /* BSD compatibility */
+#define TIOCCBRK       0x5428  /* BSD compatibility */
+#define TIOCGSID       0x5429  /* Return the session ID of FD */
+#define TCGETS2                _IOR('T', 0x2A, struct termios2)
+#define TCSETS2                _IOW('T', 0x2B, struct termios2)
+#define TCSETSW2       _IOW('T', 0x2C, struct termios2)
+#define TCSETSF2       _IOW('T', 0x2D, struct termios2)
+#define TIOCGRS485     0x542E
+#define TIOCSRS485     0x542F
+#define TIOCGPTN       _IOR('T', 0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+#define TIOCSPTLCK     _IOW('T', 0x31, int)  /* Lock/unlock Pty */
+#define TCGETX         0x5432 /* SYS5 TCGETX compatibility */
+#define TCSETX         0x5433
+#define TCSETXF                0x5434
+#define TCSETXW                0x5435
+
+#define FIONCLEX       0x5450
+#define FIOCLEX                0x5451
+#define FIOASYNC       0x5452
+#define TIOCSERCONFIG  0x5453
+#define TIOCSERGWILD   0x5454
+#define TIOCSERSWILD   0x5455
+#define TIOCGLCKTRMIOS 0x5456
+#define TIOCSLCKTRMIOS 0x5457
+#define TIOCSERGSTRUCT 0x5458 /* For debugging only */
+#define TIOCSERGETLSR   0x5459 /* Get line status register */
+#define TIOCSERGETMULTI 0x545A /* Get multiport config  */
+#define TIOCSERSETMULTI 0x545B /* Set multiport config */
+
+#define TIOCMIWAIT     0x545C  /* wait for a change on serial input line(s) */
+#define TIOCGICOUNT    0x545D  /* read serial port inline interrupt counts */
+
+/*
+ * some architectures define FIOQSIZE as 0x545E, which is used for
+ * TIOCGHAYESESP on others
+ */
+#ifndef FIOQSIZE
+# define TIOCGHAYESESP 0x545E  /* Get Hayes ESP configuration */
+# define TIOCSHAYESESP 0x545F  /* Set Hayes ESP configuration */
+# define FIOQSIZE      0x5460
+#endif
+
+/* Used for packet mode */
+#define TIOCPKT_DATA            0
+#define TIOCPKT_FLUSHREAD       1
+#define TIOCPKT_FLUSHWRITE      2
+#define TIOCPKT_STOP            4
+#define TIOCPKT_START           8
+#define TIOCPKT_NOSTOP         16
+#define TIOCPKT_DOSTOP         32
+
+#define TIOCSER_TEMT   0x01    /* Transmitter physically empty */
+
+#endif /* __ASM_GENERIC_IOCTLS_H */
diff --git a/include/asm-generic/ipcbuf.h b/include/asm-generic/ipcbuf.h
new file mode 100644 (file)
index 0000000..76982b2
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef __ASM_GENERIC_IPCBUF_H
+#define __ASM_GENERIC_IPCBUF_H
+
+/*
+ * The generic ipc64_perm structure:
+ * Note extra padding because this structure is passed back and forth
+ * between kernel and user space.
+ *
+ * ipc64_perm was originally meant to be architecture specific, but
+ * everyone just ended up making identical copies without specific
+ * optimizations, so we may just as well all use the same one.
+ *
+ * Pad space is left for:
+ * - 32-bit mode_t on architectures that only had 16 bit
+ * - 32-bit seq
+ * - 2 miscellaneous 32-bit values
+ */
+
+struct ipc64_perm {
+       __kernel_key_t          key;
+       __kernel_uid32_t        uid;
+       __kernel_gid32_t        gid;
+       __kernel_uid32_t        cuid;
+       __kernel_gid32_t        cgid;
+       __kernel_mode_t         mode;
+                               /* pad if mode_t is u16: */
+       unsigned char           __pad1[4 - sizeof(__kernel_mode_t)];
+       unsigned short          seq;
+       unsigned short          __pad2;
+       unsigned long           __unused1;
+       unsigned long           __unused2;
+};
+
+#endif /* __ASM_GENERIC_IPCBUF_H */
diff --git a/include/asm-generic/irq.h b/include/asm-generic/irq.h
new file mode 100644 (file)
index 0000000..b90ec0b
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef __ASM_GENERIC_IRQ_H
+#define __ASM_GENERIC_IRQ_H
+
+/*
+ * NR_IRQS is the upper bound of how many interrupts can be handled
+ * in the platform. It is used to size the static irq_map array,
+ * so don't make it too big.
+ */
+#ifndef NR_IRQS
+#define NR_IRQS 64
+#endif
+
+static inline int irq_canonicalize(int irq)
+{
+       return irq;
+}
+
+#endif /* __ASM_GENERIC_IRQ_H */
diff --git a/include/asm-generic/irqflags.h b/include/asm-generic/irqflags.h
new file mode 100644 (file)
index 0000000..9aebf61
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef __ASM_GENERIC_IRQFLAGS_H
+#define __ASM_GENERIC_IRQFLAGS_H
+
+/*
+ * All architectures should implement at least the first two functions,
+ * usually inline assembly will be the best way.
+ */
+#ifndef RAW_IRQ_DISABLED
+#define RAW_IRQ_DISABLED 0
+#define RAW_IRQ_ENABLED 1
+#endif
+
+/* read interrupt enabled status */
+#ifndef __raw_local_save_flags
+unsigned long __raw_local_save_flags(void);
+#endif
+
+/* set interrupt enabled status */
+#ifndef raw_local_irq_restore
+void raw_local_irq_restore(unsigned long flags);
+#endif
+
+/* get status and disable interrupts */
+#ifndef __raw_local_irq_save
+static inline unsigned long __raw_local_irq_save(void)
+{
+       unsigned long flags;
+       flags = __raw_local_save_flags();
+       raw_local_irq_restore(RAW_IRQ_DISABLED);
+       return flags;
+}
+#endif
+
+/* test flags */
+#ifndef raw_irqs_disabled_flags
+static inline int raw_irqs_disabled_flags(unsigned long flags)
+{
+       return flags == RAW_IRQ_DISABLED;
+}
+#endif
+
+/* unconditionally enable interrupts */
+#ifndef raw_local_irq_enable
+static inline void raw_local_irq_enable(void)
+{
+       raw_local_irq_restore(RAW_IRQ_ENABLED);
+}
+#endif
+
+/* unconditionally disable interrupts */
+#ifndef raw_local_irq_disable
+static inline void raw_local_irq_disable(void)
+{
+       raw_local_irq_restore(RAW_IRQ_DISABLED);
+}
+#endif
+
+/* test hardware interrupt enable bit */
+#ifndef raw_irqs_disabled
+static inline int raw_irqs_disabled(void)
+{
+       return raw_irqs_disabled_flags(__raw_local_save_flags());
+}
+#endif
+
+#define raw_local_save_flags(flags) \
+       do { (flags) = __raw_local_save_flags(); } while (0)
+
+#define raw_local_irq_save(flags) \
+       do { (flags) = __raw_local_irq_save(); } while (0)
+
+#endif /* __ASM_GENERIC_IRQFLAGS_H */
diff --git a/include/asm-generic/kmap_types.h b/include/asm-generic/kmap_types.h
new file mode 100644 (file)
index 0000000..58c3305
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef _ASM_GENERIC_KMAP_TYPES_H
+#define _ASM_GENERIC_KMAP_TYPES_H
+
+#ifdef CONFIG_DEBUG_HIGHMEM
+# define D(n) __KM_FENCE_##n ,
+#else
+# define D(n)
+#endif
+
+enum km_type {
+D(0)   KM_BOUNCE_READ,
+D(1)   KM_SKB_SUNRPC_DATA,
+D(2)   KM_SKB_DATA_SOFTIRQ,
+D(3)   KM_USER0,
+D(4)   KM_USER1,
+D(5)   KM_BIO_SRC_IRQ,
+D(6)   KM_BIO_DST_IRQ,
+D(7)   KM_PTE0,
+D(8)   KM_PTE1,
+D(9)   KM_IRQ0,
+D(10)  KM_IRQ1,
+D(11)  KM_SOFTIRQ0,
+D(12)  KM_SOFTIRQ1,
+D(13)  KM_SYNC_ICACHE,
+D(14)  KM_SYNC_DCACHE,
+D(15)  KM_UML_USERCOPY, /* UML specific, for copy_*_user - used in do_op_one_page */
+D(16)  KM_TYPE_NR
+};
+
+#undef D
+
+#endif
diff --git a/include/asm-generic/linkage.h b/include/asm-generic/linkage.h
new file mode 100644 (file)
index 0000000..fef7a01
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __ASM_GENERIC_LINKAGE_H
+#define __ASM_GENERIC_LINKAGE_H
+/*
+ * linux/linkage.h provides reasonable defaults.
+ * an architecture can override them by providing its own version.
+ */
+
+#endif /* __ASM_GENERIC_LINKAGE_H */
diff --git a/include/asm-generic/mman-common.h b/include/asm-generic/mman-common.h
new file mode 100644 (file)
index 0000000..3b69ad3
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef __ASM_GENERIC_MMAN_COMMON_H
+#define __ASM_GENERIC_MMAN_COMMON_H
+
+/*
+ Author: Michael S. Tsirkin <mst@mellanox.co.il>, Mellanox Technologies Ltd.
+ Based on: asm-xxx/mman.h
+*/
+
+#define PROT_READ      0x1             /* page can be read */
+#define PROT_WRITE     0x2             /* page can be written */
+#define PROT_EXEC      0x4             /* page can be executed */
+#define PROT_SEM       0x8             /* page may be used for atomic ops */
+#define PROT_NONE      0x0             /* page can not be accessed */
+#define PROT_GROWSDOWN 0x01000000      /* mprotect flag: extend change to start of growsdown vma */
+#define PROT_GROWSUP   0x02000000      /* mprotect flag: extend change to end of growsup vma */
+
+#define MAP_SHARED     0x01            /* Share changes */
+#define MAP_PRIVATE    0x02            /* Changes are private */
+#define MAP_TYPE       0x0f            /* Mask for type of mapping */
+#define MAP_FIXED      0x10            /* Interpret addr exactly */
+#define MAP_ANONYMOUS  0x20            /* don't use a file */
+
+#define MS_ASYNC       1               /* sync memory asynchronously */
+#define MS_INVALIDATE  2               /* invalidate the caches */
+#define MS_SYNC                4               /* synchronous memory sync */
+
+#define MADV_NORMAL    0               /* no further special treatment */
+#define MADV_RANDOM    1               /* expect random page references */
+#define MADV_SEQUENTIAL        2               /* expect sequential page references */
+#define MADV_WILLNEED  3               /* will need these pages */
+#define MADV_DONTNEED  4               /* don't need these pages */
+
+/* common parameters: try to keep these consistent across architectures */
+#define MADV_REMOVE    9               /* remove these pages & resources */
+#define MADV_DONTFORK  10              /* don't inherit across fork */
+#define MADV_DOFORK    11              /* do inherit across fork */
+
+/* compatibility flags */
+#define MAP_FILE       0
+
+#endif /* __ASM_GENERIC_MMAN_COMMON_H */
index 5e3dde2ee5adf37f5ccd62fa11f9b3878bc5a653..7cab4de2bca6a3a74cad234301fa69a013f8273b 100644 (file)
@@ -1,41 +1,18 @@
-#ifndef _ASM_GENERIC_MMAN_H
-#define _ASM_GENERIC_MMAN_H
+#ifndef __ASM_GENERIC_MMAN_H
+#define __ASM_GENERIC_MMAN_H
 
-/*
- Author: Michael S. Tsirkin <mst@mellanox.co.il>, Mellanox Technologies Ltd.
- Based on: asm-xxx/mman.h
-*/
+#include <asm-generic/mman-common.h>
 
-#define PROT_READ      0x1             /* page can be read */
-#define PROT_WRITE     0x2             /* page can be written */
-#define PROT_EXEC      0x4             /* page can be executed */
-#define PROT_SEM       0x8             /* page may be used for atomic ops */
-#define PROT_NONE      0x0             /* page can not be accessed */
-#define PROT_GROWSDOWN 0x01000000      /* mprotect flag: extend change to start of growsdown vma */
-#define PROT_GROWSUP   0x02000000      /* mprotect flag: extend change to end of growsup vma */
+#define MAP_GROWSDOWN  0x0100          /* stack-like segment */
+#define MAP_DENYWRITE  0x0800          /* ETXTBSY */
+#define MAP_EXECUTABLE 0x1000          /* mark it as an executable */
+#define MAP_LOCKED     0x2000          /* pages are locked */
+#define MAP_NORESERVE  0x4000          /* don't check for reservations */
+#define MAP_POPULATE   0x8000          /* populate (prefault) pagetables */
+#define MAP_NONBLOCK   0x10000         /* do not block on IO */
+#define MAP_STACK      0x20000         /* give out an address that is best suited for process/thread stacks */
 
-#define MAP_SHARED     0x01            /* Share changes */
-#define MAP_PRIVATE    0x02            /* Changes are private */
-#define MAP_TYPE       0x0f            /* Mask for type of mapping */
-#define MAP_FIXED      0x10            /* Interpret addr exactly */
-#define MAP_ANONYMOUS  0x20            /* don't use a file */
+#define MCL_CURRENT    1               /* lock all current mappings */
+#define MCL_FUTURE     2               /* lock all future mappings */
 
-#define MS_ASYNC       1               /* sync memory asynchronously */
-#define MS_INVALIDATE  2               /* invalidate the caches */
-#define MS_SYNC                4               /* synchronous memory sync */
-
-#define MADV_NORMAL    0               /* no further special treatment */
-#define MADV_RANDOM    1               /* expect random page references */
-#define MADV_SEQUENTIAL        2               /* expect sequential page references */
-#define MADV_WILLNEED  3               /* will need these pages */
-#define MADV_DONTNEED  4               /* don't need these pages */
-
-/* common parameters: try to keep these consistent across architectures */
-#define MADV_REMOVE    9               /* remove these pages & resources */
-#define MADV_DONTFORK  10              /* don't inherit across fork */
-#define MADV_DOFORK    11              /* do inherit across fork */
-
-/* compatibility flags */
-#define MAP_FILE       0
-
-#endif
+#endif /* __ASM_GENERIC_MMAN_H */
diff --git a/include/asm-generic/mmu.h b/include/asm-generic/mmu.h
new file mode 100644 (file)
index 0000000..4f4aa56
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __ASM_GENERIC_MMU_H
+#define __ASM_GENERIC_MMU_H
+
+/*
+ * This is the mmu.h header for nommu implementations.
+ * Architectures with an MMU need something more complex.
+ */
+#ifndef __ASSEMBLY__
+typedef struct {
+       struct vm_list_struct   *vmlist;
+       unsigned long           end_brk;
+} mm_context_t;
+#endif
+
+#endif /* __ASM_GENERIC_MMU_H */
diff --git a/include/asm-generic/mmu_context.h b/include/asm-generic/mmu_context.h
new file mode 100644 (file)
index 0000000..a7eec91
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef __ASM_GENERIC_MMU_CONTEXT_H
+#define __ASM_GENERIC_MMU_CONTEXT_H
+
+/*
+ * Generic hooks for NOMMU architectures, which do not need to do
+ * anything special here.
+ */
+
+#include <asm-generic/mm_hooks.h>
+
+struct task_struct;
+struct mm_struct;
+
+static inline void enter_lazy_tlb(struct mm_struct *mm,
+                       struct task_struct *tsk)
+{
+}
+
+static inline int init_new_context(struct task_struct *tsk,
+                       struct mm_struct *mm)
+{
+       return 0;
+}
+
+static inline void destroy_context(struct mm_struct *mm)
+{
+}
+
+static inline void deactivate_mm(struct task_struct *task,
+                       struct mm_struct *mm)
+{
+}
+
+static inline void switch_mm(struct mm_struct *prev,
+                       struct mm_struct *next,
+                       struct task_struct *tsk)
+{
+}
+
+static inline void activate_mm(struct mm_struct *prev_mm,
+                              struct mm_struct *next_mm)
+{
+}
+
+#endif /* __ASM_GENERIC_MMU_CONTEXT_H */
diff --git a/include/asm-generic/module.h b/include/asm-generic/module.h
new file mode 100644 (file)
index 0000000..ed5b44d
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef __ASM_GENERIC_MODULE_H
+#define __ASM_GENERIC_MODULE_H
+
+/*
+ * Many architectures just need a simple module
+ * loader without arch specific data.
+ */
+struct mod_arch_specific
+{
+};
+
+#ifdef CONFIG_64BIT
+#define Elf_Shdr Elf64_Shdr
+#define Elf_Sym Elf64_Sym
+#define Elf_Ehdr Elf64_Ehdr
+#else
+#define Elf_Shdr Elf32_Shdr
+#define Elf_Sym Elf32_Sym
+#define Elf_Ehdr Elf32_Ehdr
+#endif
+
+#endif /* __ASM_GENERIC_MODULE_H */
diff --git a/include/asm-generic/msgbuf.h b/include/asm-generic/msgbuf.h
new file mode 100644 (file)
index 0000000..aec850d
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef __ASM_GENERIC_MSGBUF_H
+#define __ASM_GENERIC_MSGBUF_H
+
+#include <asm/bitsperlong.h>
+/*
+ * generic msqid64_ds structure.
+ *
+ * Note extra padding because this structure is passed back and forth
+ * between kernel and user space.
+ *
+ * msqid64_ds was originally meant to be architecture specific, but
+ * everyone just ended up making identical copies without specific
+ * optimizations, so we may just as well all use the same one.
+ *
+ * 64 bit architectures typically define a 64 bit __kernel_time_t,
+ * so they do not need the first three padding words.
+ * On big-endian systems, the padding is in the wrong place.
+ *
+ * Pad space is left for:
+ * - 64-bit time_t to solve y2038 problem
+ * - 2 miscellaneous 32-bit values
+ */
+
+struct msqid64_ds {
+       struct ipc64_perm msg_perm;
+       __kernel_time_t msg_stime;      /* last msgsnd time */
+#if __BITS_PER_LONG != 64
+       unsigned long   __unused1;
+#endif
+       __kernel_time_t msg_rtime;      /* last msgrcv time */
+#if __BITS_PER_LONG != 64
+       unsigned long   __unused2;
+#endif
+       __kernel_time_t msg_ctime;      /* last change time */
+#if __BITS_PER_LONG != 64
+       unsigned long   __unused3;
+#endif
+       unsigned long  msg_cbytes;      /* current number of bytes on queue */
+       unsigned long  msg_qnum;        /* number of messages in queue */
+       unsigned long  msg_qbytes;      /* max number of bytes on queue */
+       __kernel_pid_t msg_lspid;       /* pid of last msgsnd */
+       __kernel_pid_t msg_lrpid;       /* last receive pid */
+       unsigned long  __unused4;
+       unsigned long  __unused5;
+};
+
+#endif /* __ASM_GENERIC_MSGBUF_H */
diff --git a/include/asm-generic/mutex.h b/include/asm-generic/mutex.h
new file mode 100644 (file)
index 0000000..fe91ab5
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef __ASM_GENERIC_MUTEX_H
+#define __ASM_GENERIC_MUTEX_H
+/*
+ * Pull in the generic implementation for the mutex fastpath,
+ * which is a reasonable default on many architectures.
+ */
+
+#include <asm-generic/mutex-dec.h>
+#endif /* __ASM_GENERIC_MUTEX_H */
index 14db733b8e68e7df3cb95bb685eb97cc0125a094..75fec18cdc59e1f92ee55ce5f93414e47e76dc5a 100644 (file)
@@ -1,24 +1,99 @@
-#ifndef _ASM_GENERIC_PAGE_H
-#define _ASM_GENERIC_PAGE_H
+#ifndef __ASM_GENERIC_PAGE_H
+#define __ASM_GENERIC_PAGE_H
+/*
+ * Generic page.h implementation, for NOMMU architectures.
+ * This provides the dummy definitions for the memory management.
+ */
+
+#ifdef CONFIG_MMU
+#error need to prove a real asm/page.h
+#endif
+
+
+/* PAGE_SHIFT determines the page size */
+
+#define PAGE_SHIFT     12
+#ifdef __ASSEMBLY__
+#define PAGE_SIZE      (1 << PAGE_SHIFT)
+#else
+#define PAGE_SIZE      (1UL << PAGE_SHIFT)
+#endif
+#define PAGE_MASK      (~(PAGE_SIZE-1))
+
+#include <asm/setup.h>
+
+#ifndef __ASSEMBLY__
+
+#define get_user_page(vaddr)           __get_free_page(GFP_KERNEL)
+#define free_user_page(page, addr)     free_page(addr)
+
+#define clear_page(page)       memset((page), 0, PAGE_SIZE)
+#define copy_page(to,from)     memcpy((to), (from), PAGE_SIZE)
+
+#define clear_user_page(page, vaddr, pg)       clear_page(page)
+#define copy_user_page(to, from, vaddr, pg)    copy_page(to, from)
+
+/*
+ * These are used to make use of C type-checking..
+ */
+typedef struct {
+       unsigned long pte;
+} pte_t;
+typedef struct {
+       unsigned long pmd[16];
+} pmd_t;
+typedef struct {
+       unsigned long pgd;
+} pgd_t;
+typedef struct {
+       unsigned long pgprot;
+} pgprot_t;
+typedef struct page *pgtable_t;
+
+#define pte_val(x)     ((x).pte)
+#define pmd_val(x)     ((&x)->pmd[0])
+#define pgd_val(x)     ((x).pgd)
+#define pgprot_val(x)  ((x).pgprot)
+
+#define __pte(x)       ((pte_t) { (x) } )
+#define __pmd(x)       ((pmd_t) { (x) } )
+#define __pgd(x)       ((pgd_t) { (x) } )
+#define __pgprot(x)    ((pgprot_t) { (x) } )
+
+extern unsigned long memory_start;
+extern unsigned long memory_end;
+
+#endif /* !__ASSEMBLY__ */
+
+#ifdef CONFIG_KERNEL_RAM_BASE_ADDRESS
+#define PAGE_OFFSET            (CONFIG_KERNEL_RAM_BASE_ADDRESS)
+#else
+#define PAGE_OFFSET            (0)
+#endif
 
 #ifndef __ASSEMBLY__
 
-#include <linux/compiler.h>
+#define __va(x) ((void *)((unsigned long)(x) + PAGE_OFFSET))
+#define __pa(x) ((unsigned long) (x) - PAGE_OFFSET)
+
+#define virt_to_pfn(kaddr)     (__pa(kaddr) >> PAGE_SHIFT)
+#define pfn_to_virt(pfn)       __va((pfn) << PAGE_SHIFT)
+
+#define virt_to_page(addr)     (mem_map + (((unsigned long)(addr)-PAGE_OFFSET) >> PAGE_SHIFT))
+#define page_to_virt(page)     ((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET)
+
+#ifndef page_to_phys
+#define page_to_phys(page)      ((dma_addr_t)page_to_pfn(page) << PAGE_SHIFT)
+#endif
+
+#define pfn_valid(pfn)         ((pfn) < max_mapnr)
 
-/* Pure 2^n version of get_order */
-static __inline__ __attribute_const__ int get_order(unsigned long size)
-{
-       int order;
+#define        virt_addr_valid(kaddr)  (((void *)(kaddr) >= (void *)PAGE_OFFSET) && \
+                               ((void *)(kaddr) < (void *)memory_end))
 
-       size = (size - 1) >> (PAGE_SHIFT - 1);
-       order = -1;
-       do {
-               size >>= 1;
-               order++;
-       } while (size);
-       return order;
-}
+#endif /* __ASSEMBLY__ */
 
-#endif /* __ASSEMBLY__ */
+#include <asm-generic/memory_model.h>
+#include <asm-generic/getorder.h>
 
-#endif /* _ASM_GENERIC_PAGE_H */
+#endif /* __ASM_GENERIC_PAGE_H */
diff --git a/include/asm-generic/param.h b/include/asm-generic/param.h
new file mode 100644 (file)
index 0000000..cdf8251
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef __ASM_GENERIC_PARAM_H
+#define __ASM_GENERIC_PARAM_H
+
+#ifdef __KERNEL__
+# define HZ            CONFIG_HZ       /* Internal kernel timer frequency */
+# define USER_HZ       100             /* some user interfaces are */
+# define CLOCKS_PER_SEC        (USER_HZ)       /* in "ticks" like times() */
+#endif
+
+#ifndef HZ
+#define HZ 100
+#endif
+
+#ifndef EXEC_PAGESIZE
+#define EXEC_PAGESIZE  4096
+#endif
+
+#ifndef NOGROUP
+#define NOGROUP                (-1)
+#endif
+
+#define MAXHOSTNAMELEN 64      /* max length of hostname */
+
+#endif /* __ASM_GENERIC_PARAM_H */
diff --git a/include/asm-generic/parport.h b/include/asm-generic/parport.h
new file mode 100644 (file)
index 0000000..40528cb
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef __ASM_GENERIC_PARPORT_H
+#define __ASM_GENERIC_PARPORT_H
+
+/*
+ * An ISA bus may have i8255 parallel ports at well-known
+ * locations in the I/O space, which are scanned by
+ * parport_pc_find_isa_ports.
+ *
+ * Without ISA support, the driver will only attach
+ * to devices on the PCI bus.
+ */
+
+static int __devinit parport_pc_find_isa_ports(int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports(int autoirq, int autodma)
+{
+#ifdef CONFIG_ISA
+       return parport_pc_find_isa_ports(autoirq, autodma);
+#else
+       return 0;
+#endif
+}
+
+#endif /* __ASM_GENERIC_PARPORT_H */
index c36a77d3bf44473b603b9c7d2a4749521f708a79..515c6e2e3218e5c193275e6b420ac3312218cae6 100644 (file)
@@ -52,4 +52,12 @@ static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
 }
 #endif /* HAVE_ARCH_PCI_GET_LEGACY_IDE_IRQ */
 
+/*
+ * By default, assume that no iommu is in use and that the PCI
+ * space is mapped to address physical 0.
+ */
+#ifndef PCI_DMA_BUS_IS_PHYS
+#define PCI_DMA_BUS_IS_PHYS    (1)
 #endif
+
+#endif /* _ASM_GENERIC_PCI_H */
diff --git a/include/asm-generic/pgalloc.h b/include/asm-generic/pgalloc.h
new file mode 100644 (file)
index 0000000..9e429d0
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __ASM_GENERIC_PGALLOC_H
+#define __ASM_GENERIC_PGALLOC_H
+/*
+ * an empty file is enough for a nommu architecture
+ */
+#ifdef CONFIG_MMU
+#error need to implement an architecture specific asm/pgalloc.h
+#endif
+
+#define check_pgt_cache()          do { } while (0)
+
+#endif /* __ASM_GENERIC_PGALLOC_H */
diff --git a/include/asm-generic/posix_types.h b/include/asm-generic/posix_types.h
new file mode 100644 (file)
index 0000000..3dab008
--- /dev/null
@@ -0,0 +1,165 @@
+#ifndef __ASM_GENERIC_POSIX_TYPES_H
+#define __ASM_GENERIC_POSIX_TYPES_H
+
+#include <asm/bitsperlong.h>
+/*
+ * This file is generally used by user-level software, so you need to
+ * be a little careful about namespace pollution etc.
+ *
+ * First the types that are often defined in different ways across
+ * architectures, so that you can override them.
+ */
+
+#ifndef __kernel_ino_t
+typedef unsigned long  __kernel_ino_t;
+#endif
+
+#ifndef __kernel_mode_t
+typedef unsigned int   __kernel_mode_t;
+#endif
+
+#ifndef __kernel_nlink_t
+typedef unsigned long  __kernel_nlink_t;
+#endif
+
+#ifndef __kernel_pid_t
+typedef int            __kernel_pid_t;
+#endif
+
+#ifndef __kernel_ipc_pid_t
+typedef int            __kernel_ipc_pid_t;
+#endif
+
+#ifndef __kernel_uid_t
+typedef unsigned int   __kernel_uid_t;
+typedef unsigned int   __kernel_gid_t;
+#endif
+
+#ifndef __kernel_suseconds_t
+typedef long           __kernel_suseconds_t;
+#endif
+
+#ifndef __kernel_daddr_t
+typedef int            __kernel_daddr_t;
+#endif
+
+#ifndef __kernel_uid32_t
+typedef __kernel_uid_t __kernel_uid32_t;
+typedef __kernel_gid_t __kernel_gid32_t;
+#endif
+
+#ifndef __kernel_old_uid_t
+typedef __kernel_uid_t __kernel_old_uid_t;
+typedef __kernel_gid_t __kernel_old_gid_t;
+#endif
+
+#ifndef __kernel_old_dev_t
+typedef unsigned int   __kernel_old_dev_t;
+#endif
+
+/*
+ * Most 32 bit architectures use "unsigned int" size_t,
+ * and all 64 bit architectures use "unsigned long" size_t.
+ */
+#ifndef __kernel_size_t
+#if __BITS_PER_LONG != 64
+typedef unsigned int   __kernel_size_t;
+typedef int            __kernel_ssize_t;
+typedef int            __kernel_ptrdiff_t;
+#else
+typedef unsigned long  __kernel_size_t;
+typedef long           __kernel_ssize_t;
+typedef long           __kernel_ptrdiff_t;
+#endif
+#endif
+
+/*
+ * anything below here should be completely generic
+ */
+typedef long           __kernel_off_t;
+typedef long long      __kernel_loff_t;
+typedef long           __kernel_time_t;
+typedef long           __kernel_clock_t;
+typedef int            __kernel_timer_t;
+typedef int            __kernel_clockid_t;
+typedef char *         __kernel_caddr_t;
+typedef unsigned short __kernel_uid16_t;
+typedef unsigned short __kernel_gid16_t;
+
+typedef struct {
+       int     val[2];
+} __kernel_fsid_t;
+
+#ifdef __KERNEL__
+
+#undef __FD_SET
+static inline void __FD_SET(unsigned long __fd, __kernel_fd_set *__fdsetp)
+{
+       unsigned long __tmp = __fd / __NFDBITS;
+       unsigned long __rem = __fd % __NFDBITS;
+       __fdsetp->fds_bits[__tmp] |= (1UL<<__rem);
+}
+
+#undef __FD_CLR
+static inline void __FD_CLR(unsigned long __fd, __kernel_fd_set *__fdsetp)
+{
+       unsigned long __tmp = __fd / __NFDBITS;
+       unsigned long __rem = __fd % __NFDBITS;
+       __fdsetp->fds_bits[__tmp] &= ~(1UL<<__rem);
+}
+
+#undef __FD_ISSET
+static inline int __FD_ISSET(unsigned long __fd, const __kernel_fd_set *__p)
+{
+       unsigned long __tmp = __fd / __NFDBITS;
+       unsigned long __rem = __fd % __NFDBITS;
+       return (__p->fds_bits[__tmp] & (1UL<<__rem)) != 0;
+}
+
+/*
+ * This will unroll the loop for the normal constant case (8 ints,
+ * for a 256-bit fd_set)
+ */
+#undef __FD_ZERO
+static inline void __FD_ZERO(__kernel_fd_set *__p)
+{
+       unsigned long *__tmp = __p->fds_bits;
+       int __i;
+
+       if (__builtin_constant_p(__FDSET_LONGS)) {
+               switch (__FDSET_LONGS) {
+               case 16:
+                       __tmp[ 0] = 0; __tmp[ 1] = 0;
+                       __tmp[ 2] = 0; __tmp[ 3] = 0;
+                       __tmp[ 4] = 0; __tmp[ 5] = 0;
+                       __tmp[ 6] = 0; __tmp[ 7] = 0;
+                       __tmp[ 8] = 0; __tmp[ 9] = 0;
+                       __tmp[10] = 0; __tmp[11] = 0;
+                       __tmp[12] = 0; __tmp[13] = 0;
+                       __tmp[14] = 0; __tmp[15] = 0;
+                       return;
+
+               case 8:
+                       __tmp[ 0] = 0; __tmp[ 1] = 0;
+                       __tmp[ 2] = 0; __tmp[ 3] = 0;
+                       __tmp[ 4] = 0; __tmp[ 5] = 0;
+                       __tmp[ 6] = 0; __tmp[ 7] = 0;
+                       return;
+
+               case 4:
+                       __tmp[ 0] = 0; __tmp[ 1] = 0;
+                       __tmp[ 2] = 0; __tmp[ 3] = 0;
+                       return;
+               }
+       }
+       __i = __FDSET_LONGS;
+       while (__i) {
+               __i--;
+               *__tmp = 0;
+               __tmp++;
+       }
+}
+
+#endif /* __KERNEL__ */
+
+#endif /* __ASM_GENERIC_POSIX_TYPES_H */
index 763e3b060f432542e07a6730a837185853418148..fa86f240c874328121862d0332930abc7e053ba8 100644 (file)
@@ -202,7 +202,7 @@ static inline unsigned int get_rtc_ss(void)
 {
        struct rtc_time h;
 
-       __get_rtc_time(&h);
+       get_rtc_time(&h);
        return h.tm_sec;
 }
 
diff --git a/include/asm-generic/scatterlist.h b/include/asm-generic/scatterlist.h
new file mode 100644 (file)
index 0000000..8b94544
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef __ASM_GENERIC_SCATTERLIST_H
+#define __ASM_GENERIC_SCATTERLIST_H
+
+#include <linux/types.h>
+
+struct scatterlist {
+#ifdef CONFIG_DEBUG_SG
+       unsigned long   sg_magic;
+#endif
+       unsigned long   page_link;
+       unsigned int    offset;
+       unsigned int    length;
+       dma_addr_t      dma_address;
+       unsigned int    dma_length;
+};
+
+/*
+ * These macros should be used after a dma_map_sg call has been done
+ * to get bus addresses of each of the SG entries and their lengths.
+ * You should only work with the number of sg entries pci_map_sg
+ * returns, or alternatively stop on the first sg_dma_len(sg) which
+ * is 0.
+ */
+#define sg_dma_address(sg)     ((sg)->dma_address)
+#ifndef sg_dma_len
+/*
+ * Normally, you have an iommu on 64 bit machines, but not on 32 bit
+ * machines. Architectures that are differnt should override this.
+ */
+#if __BITS_PER_LONG == 64
+#define sg_dma_len(sg)         ((sg)->dma_length)
+#else
+#define sg_dma_len(sg)         ((sg)->length)
+#endif /* 64 bit */
+#endif /* sg_dma_len */
+
+#ifndef ISA_DMA_THRESHOLD
+#define ISA_DMA_THRESHOLD      (~0UL)
+#endif
+
+#define ARCH_HAS_SG_CHAIN
+
+#endif /* __ASM_GENERIC_SCATTERLIST_H */
diff --git a/include/asm-generic/segment.h b/include/asm-generic/segment.h
new file mode 100644 (file)
index 0000000..5580eac
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef __ASM_GENERIC_SEGMENT_H
+#define __ASM_GENERIC_SEGMENT_H
+/*
+ * Only here because we have some old header files that expect it...
+ *
+ * New architectures probably don't want to have their own version.
+ */
+
+#endif /* __ASM_GENERIC_SEGMENT_H */
diff --git a/include/asm-generic/sembuf.h b/include/asm-generic/sembuf.h
new file mode 100644 (file)
index 0000000..4cb2c13
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef __ASM_GENERIC_SEMBUF_H
+#define __ASM_GENERIC_SEMBUF_H
+
+#include <asm/bitsperlong.h>
+
+/*
+ * The semid64_ds structure for x86 architecture.
+ * Note extra padding because this structure is passed back and forth
+ * between kernel and user space.
+ *
+ * semid64_ds was originally meant to be architecture specific, but
+ * everyone just ended up making identical copies without specific
+ * optimizations, so we may just as well all use the same one.
+ *
+ * 64 bit architectures typically define a 64 bit __kernel_time_t,
+ * so they do not need the first two padding words.
+ * On big-endian systems, the padding is in the wrong place.
+ *
+ * Pad space is left for:
+ * - 64-bit time_t to solve y2038 problem
+ * - 2 miscellaneous 32-bit values
+ */
+struct semid64_ds {
+       struct ipc64_perm sem_perm;     /* permissions .. see ipc.h */
+       __kernel_time_t sem_otime;      /* last semop time */
+#if __BITS_PER_LONG != 64
+       unsigned long   __unused1;
+#endif
+       __kernel_time_t sem_ctime;      /* last change time */
+#if __BITS_PER_LONG != 64
+       unsigned long   __unused2;
+#endif
+       unsigned long   sem_nsems;      /* no. of semaphores in array */
+       unsigned long   __unused3;
+       unsigned long   __unused4;
+};
+
+#endif /* __ASM_GENERIC_SEMBUF_H */
diff --git a/include/asm-generic/serial.h b/include/asm-generic/serial.h
new file mode 100644 (file)
index 0000000..5e29109
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __ASM_GENERIC_SERIAL_H
+#define __ASM_GENERIC_SERIAL_H
+
+/*
+ * This should not be an architecture specific #define, oh well.
+ *
+ * Traditionally, it just describes i8250 and related serial ports
+ * that have this clock rate.
+ */
+
+#define BASE_BAUD (1843200 / 16)
+
+#endif /* __ASM_GENERIC_SERIAL_H */
diff --git a/include/asm-generic/setup.h b/include/asm-generic/setup.h
new file mode 100644 (file)
index 0000000..6fc26a5
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __ASM_GENERIC_SETUP_H
+#define __ASM_GENERIC_SETUP_H
+
+#define COMMAND_LINE_SIZE      512
+
+#endif /* __ASM_GENERIC_SETUP_H */
diff --git a/include/asm-generic/shmbuf.h b/include/asm-generic/shmbuf.h
new file mode 100644 (file)
index 0000000..5768fa6
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef __ASM_GENERIC_SHMBUF_H
+#define __ASM_GENERIC_SHMBUF_H
+
+#include <asm/bitsperlong.h>
+
+/*
+ * The shmid64_ds structure for x86 architecture.
+ * Note extra padding because this structure is passed back and forth
+ * between kernel and user space.
+ *
+ * shmid64_ds was originally meant to be architecture specific, but
+ * everyone just ended up making identical copies without specific
+ * optimizations, so we may just as well all use the same one.
+ *
+ * 64 bit architectures typically define a 64 bit __kernel_time_t,
+ * so they do not need the first two padding words.
+ * On big-endian systems, the padding is in the wrong place.
+ *
+ *
+ * Pad space is left for:
+ * - 64-bit time_t to solve y2038 problem
+ * - 2 miscellaneous 32-bit values
+ */
+
+struct shmid64_ds {
+       struct ipc64_perm       shm_perm;       /* operation perms */
+       size_t                  shm_segsz;      /* size of segment (bytes) */
+       __kernel_time_t         shm_atime;      /* last attach time */
+#if __BITS_PER_LONG != 64
+       unsigned long           __unused1;
+#endif
+       __kernel_time_t         shm_dtime;      /* last detach time */
+#if __BITS_PER_LONG != 64
+       unsigned long           __unused2;
+#endif
+       __kernel_time_t         shm_ctime;      /* last change time */
+#if __BITS_PER_LONG != 64
+       unsigned long           __unused3;
+#endif
+       __kernel_pid_t          shm_cpid;       /* pid of creator */
+       __kernel_pid_t          shm_lpid;       /* pid of last operator */
+       unsigned long           shm_nattch;     /* no. of current attaches */
+       unsigned long           __unused4;
+       unsigned long           __unused5;
+};
+
+struct shminfo64 {
+       unsigned long   shmmax;
+       unsigned long   shmmin;
+       unsigned long   shmmni;
+       unsigned long   shmseg;
+       unsigned long   shmall;
+       unsigned long   __unused1;
+       unsigned long   __unused2;
+       unsigned long   __unused3;
+       unsigned long   __unused4;
+};
+
+#endif /* __ASM_GENERIC_SHMBUF_H */
diff --git a/include/asm-generic/shmparam.h b/include/asm-generic/shmparam.h
new file mode 100644 (file)
index 0000000..51a3852
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __ASM_GENERIC_SHMPARAM_H
+#define __ASM_GENERIC_SHMPARAM_H
+
+#define SHMLBA PAGE_SIZE        /* attach addr a multiple of this */
+
+#endif /* _ASM_GENERIC_SHMPARAM_H */
diff --git a/include/asm-generic/signal-defs.h b/include/asm-generic/signal-defs.h
new file mode 100644 (file)
index 0000000..00f95df
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef __ASM_GENERIC_SIGNAL_DEFS_H
+#define __ASM_GENERIC_SIGNAL_DEFS_H
+
+#include <linux/compiler.h>
+
+#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
+
+#endif /* __ASM_GENERIC_SIGNAL_DEFS_H */
index dae1d8720076fde1aad89772b6390f22b1db3345..555c0aee8a47844c360e3510fc5ec326b434436e 100644 (file)
 #ifndef __ASM_GENERIC_SIGNAL_H
 #define __ASM_GENERIC_SIGNAL_H
 
-#include <linux/compiler.h>
+#include <linux/types.h>
 
-#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 */
+#define _NSIG          64
+#define _NSIG_BPW      __BITS_PER_LONG
+#define _NSIG_WORDS    (_NSIG / _NSIG_BPW)
+
+#define SIGHUP          1
+#define SIGINT          2
+#define SIGQUIT                 3
+#define SIGILL          4
+#define SIGTRAP                 5
+#define SIGABRT                 6
+#define SIGIOT          6
+#define SIGBUS          7
+#define SIGFPE          8
+#define SIGKILL                 9
+#define SIGUSR1                10
+#define SIGSEGV                11
+#define SIGUSR2                12
+#define SIGPIPE                13
+#define SIGALRM                14
+#define SIGTERM                15
+#define SIGSTKFLT      16
+#define SIGCHLD                17
+#define SIGCONT                18
+#define SIGSTOP                19
+#define SIGTSTP                20
+#define SIGTTIN                21
+#define SIGTTOU                22
+#define SIGURG         23
+#define SIGXCPU                24
+#define SIGXFSZ                25
+#define SIGVTALRM      26
+#define SIGPROF                27
+#define SIGWINCH       28
+#define SIGIO          29
+#define SIGPOLL                SIGIO
+/*
+#define SIGLOST                29
+*/
+#define SIGPWR         30
+#define SIGSYS         31
+#define        SIGUNUSED       31
+
+/* These should not be considered constants from userland.  */
+#define SIGRTMIN       32
+#ifndef SIGRTMAX
+#define SIGRTMAX       _NSIG
 #endif
 
+/*
+ * SA_FLAGS values:
+ *
+ * SA_ONSTACK indicates that a registered stack_t will be used.
+ * SA_RESTART flag to get restarting signals (which were the default long ago)
+ * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop.
+ * SA_RESETHAND clears the handler when the signal is delivered.
+ * SA_NOCLDWAIT flag on SIGCHLD to inhibit zombies.
+ * SA_NODEFER prevents the current signal from being masked in the handler.
+ *
+ * SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single
+ * Unix names RESETHAND and NODEFER respectively.
+ */
+#define SA_NOCLDSTOP   0x00000001
+#define SA_NOCLDWAIT   0x00000002
+#define SA_SIGINFO     0x00000004
+#define SA_ONSTACK     0x08000000
+#define SA_RESTART     0x10000000
+#define SA_NODEFER     0x40000000
+#define SA_RESETHAND   0x80000000
+
+#define SA_NOMASK      SA_NODEFER
+#define SA_ONESHOT     SA_RESETHAND
+
+/*
+ * New architectures should not define the obsolete
+ *     SA_RESTORER     0x04000000
+ */
+
+/*
+ * sigaltstack controls
+ */
+#define SS_ONSTACK     1
+#define SS_DISABLE     2
+
+#define MINSIGSTKSZ    2048
+#define SIGSTKSZ       8192
+
 #ifndef __ASSEMBLY__
-typedef void __signalfn_t(int);
-typedef __signalfn_t __user *__sighandler_t;
+typedef struct {
+       unsigned long sig[_NSIG_WORDS];
+} sigset_t;
+
+/* not actually used, but required for linux/syscalls.h */
+typedef unsigned long old_sigset_t;
 
-typedef void __restorefn_t(void);
-typedef __restorefn_t __user *__sigrestore_t;
+#include <asm-generic/signal-defs.h>
 
-#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 */
+struct sigaction {
+       __sighandler_t sa_handler;
+       unsigned long sa_flags;
+#ifdef SA_RESTORER
+       __sigrestore_t sa_restorer;
 #endif
+       sigset_t sa_mask;               /* mask last for extensibility */
+};
+
+struct k_sigaction {
+       struct sigaction sa;
+};
+
+typedef struct sigaltstack {
+       void __user *ss_sp;
+       int ss_flags;
+       size_t ss_size;
+} stack_t;
+
+#ifdef __KERNEL__
+
+#include <asm/sigcontext.h>
+#undef __HAVE_ARCH_SIG_BITOPS
+
+#define ptrace_signal_deliver(regs, cookie) do { } while (0)
+
+#endif /* __KERNEL__ */
+#endif /* __ASSEMBLY__ */
 
-#endif /* __ASM_GENERIC_SIGNAL_H */
+#endif /* _ASM_GENERIC_SIGNAL_H */
diff --git a/include/asm-generic/socket.h b/include/asm-generic/socket.h
new file mode 100644 (file)
index 0000000..5d79e40
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef __ASM_GENERIC_SOCKET_H
+#define __ASM_GENERIC_SOCKET_H
+
+#include <asm/sockios.h>
+
+/* For setsockopt(2) */
+#define SOL_SOCKET     1
+
+#define SO_DEBUG       1
+#define SO_REUSEADDR   2
+#define SO_TYPE                3
+#define SO_ERROR       4
+#define SO_DONTROUTE   5
+#define SO_BROADCAST   6
+#define SO_SNDBUF      7
+#define SO_RCVBUF      8
+#define SO_SNDBUFFORCE 32
+#define SO_RCVBUFFORCE 33
+#define SO_KEEPALIVE   9
+#define SO_OOBINLINE   10
+#define SO_NO_CHECK    11
+#define SO_PRIORITY    12
+#define SO_LINGER      13
+#define SO_BSDCOMPAT   14
+/* To add :#define SO_REUSEPORT 15 */
+
+#ifndef SO_PASSCRED /* powerpc only differs in these */
+#define SO_PASSCRED    16
+#define SO_PEERCRED    17
+#define SO_RCVLOWAT    18
+#define SO_SNDLOWAT    19
+#define SO_RCVTIMEO    20
+#define SO_SNDTIMEO    21
+#endif
+
+/* Security levels - as per NRL IPv6 - don't actually do anything */
+#define SO_SECURITY_AUTHENTICATION             22
+#define SO_SECURITY_ENCRYPTION_TRANSPORT       23
+#define SO_SECURITY_ENCRYPTION_NETWORK         24
+
+#define SO_BINDTODEVICE        25
+
+/* Socket filtering */
+#define SO_ATTACH_FILTER       26
+#define SO_DETACH_FILTER       27
+
+#define SO_PEERNAME            28
+#define SO_TIMESTAMP           29
+#define SCM_TIMESTAMP          SO_TIMESTAMP
+
+#define SO_ACCEPTCONN          30
+
+#define SO_PEERSEC             31
+#define SO_PASSSEC             34
+#define SO_TIMESTAMPNS         35
+#define SCM_TIMESTAMPNS                SO_TIMESTAMPNS
+
+#define SO_MARK                        36
+
+#define SO_TIMESTAMPING                37
+#define SCM_TIMESTAMPING       SO_TIMESTAMPING
+
+#endif /* __ASM_GENERIC_SOCKET_H */
diff --git a/include/asm-generic/sockios.h b/include/asm-generic/sockios.h
new file mode 100644 (file)
index 0000000..9a61a36
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __ASM_GENERIC_SOCKIOS_H
+#define __ASM_GENERIC_SOCKIOS_H
+
+/* Socket-level I/O control calls. */
+#define FIOSETOWN      0x8901
+#define SIOCSPGRP      0x8902
+#define FIOGETOWN      0x8903
+#define SIOCGPGRP      0x8904
+#define SIOCATMARK     0x8905
+#define SIOCGSTAMP     0x8906          /* Get stamp (timeval) */
+#define SIOCGSTAMPNS   0x8907          /* Get stamp (timespec) */
+
+#endif /* __ASM_GENERIC_SOCKIOS_H */
diff --git a/include/asm-generic/spinlock.h b/include/asm-generic/spinlock.h
new file mode 100644 (file)
index 0000000..1547a03
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef __ASM_GENERIC_SPINLOCK_H
+#define __ASM_GENERIC_SPINLOCK_H
+/*
+ * You need to implement asm/spinlock.h for SMP support. The generic
+ * version does not handle SMP.
+ */
+#ifdef CONFIG_SMP
+#error need an architecture specific asm/spinlock.h
+#endif
+
+#endif /* __ASM_GENERIC_SPINLOCK_H */
diff --git a/include/asm-generic/stat.h b/include/asm-generic/stat.h
new file mode 100644 (file)
index 0000000..47e6417
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef __ASM_GENERIC_STAT_H
+#define __ASM_GENERIC_STAT_H
+
+/*
+ * Everybody gets this wrong and has to stick with it for all
+ * eternity. Hopefully, this version gets used by new architectures
+ * so they don't fall into the same traps.
+ *
+ * stat64 is copied from powerpc64, with explicit padding added.
+ * stat is the same structure layout on 64-bit, without the 'long long'
+ * types.
+ *
+ * By convention, 64 bit architectures use the stat interface, while
+ * 32 bit architectures use the stat64 interface. Note that we don't
+ * provide an __old_kernel_stat here, which new architecture should
+ * not have to start with.
+ */
+
+#include <asm/bitsperlong.h>
+
+#define STAT_HAVE_NSEC 1
+
+struct stat {
+       unsigned long   st_dev;         /* Device.  */
+       unsigned long   st_ino;         /* File serial number.  */
+       unsigned int    st_mode;        /* File mode.  */
+       unsigned int    st_nlink;       /* Link count.  */
+       unsigned int    st_uid;         /* User ID of the file's owner.  */
+       unsigned int    st_gid;         /* Group ID of the file's group. */
+       unsigned long   st_rdev;        /* Device number, if device.  */
+       unsigned long   __pad1;
+       long            st_size;        /* Size of file, in bytes.  */
+       int             st_blksize;     /* Optimal block size for I/O.  */
+       int             __pad2;
+       long            st_blocks;      /* Number 512-byte blocks allocated. */
+       int             st_atime;       /* Time of last access.  */
+       unsigned int    st_atime_nsec;
+       int             st_mtime;       /* Time of last modification.  */
+       unsigned int    st_mtime_nsec;
+       int             st_ctime;       /* Time of last status change.  */
+       unsigned int    st_ctime_nsec;
+       unsigned int    __unused4;
+       unsigned int    __unused5;
+};
+
+#if __BITS_PER_LONG != 64
+/* This matches struct stat64 in glibc2.1. Only used for 32 bit. */
+struct stat64 {
+       unsigned long long st_dev;      /* Device.  */
+       unsigned long long st_ino;      /* File serial number.  */
+       unsigned int    st_mode;        /* File mode.  */
+       unsigned int    st_nlink;       /* Link count.  */
+       unsigned int    st_uid;         /* User ID of the file's owner.  */
+       unsigned int    st_gid;         /* Group ID of the file's group. */
+       unsigned long long st_rdev;     /* Device number, if device.  */
+       unsigned long long __pad1;
+       long long       st_size;        /* Size of file, in bytes.  */
+       int             st_blksize;     /* Optimal block size for I/O.  */
+       int             __pad2;
+       long long       st_blocks;      /* Number 512-byte blocks allocated. */
+       int             st_atime;       /* Time of last access.  */
+       unsigned int    st_atime_nsec;
+       int             st_mtime;       /* Time of last modification.  */
+       unsigned int    st_mtime_nsec;
+       int             st_ctime;       /* Time of last status change.  */
+       unsigned int    st_ctime_nsec;
+       unsigned int    __unused4;
+       unsigned int    __unused5;
+};
+#endif
+
+#endif /* __ASM_GENERIC_STAT_H */
diff --git a/include/asm-generic/string.h b/include/asm-generic/string.h
new file mode 100644 (file)
index 0000000..de5e020
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __ASM_GENERIC_STRING_H
+#define __ASM_GENERIC_STRING_H
+/*
+ * The kernel provides all required functions in lib/string.c
+ *
+ * Architectures probably want to provide at least their own optimized
+ * memcpy and memset functions though.
+ */
+
+#endif /* __ASM_GENERIC_STRING_H */
diff --git a/include/asm-generic/swab.h b/include/asm-generic/swab.h
new file mode 100644 (file)
index 0000000..a8e9029
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef _ASM_GENERIC_SWAB_H
+#define _ASM_GENERIC_SWAB_H
+
+#include <asm/bitsperlong.h>
+
+/*
+ * 32 bit architectures typically (but not always) want to
+ * set __SWAB_64_THRU_32__. In user space, this is only
+ * valid if the compiler supports 64 bit data types.
+ */
+
+#if __BITS_PER_LONG == 32
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__) || defined(__KERNEL__)
+#define __SWAB_64_THRU_32__
+#endif
+#endif
+
+#endif /* _ASM_GENERIC_SWAB_H */
diff --git a/include/asm-generic/syscalls.h b/include/asm-generic/syscalls.h
new file mode 100644 (file)
index 0000000..df84e3b
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef __ASM_GENERIC_SYSCALLS_H
+#define __ASM_GENERIC_SYSCALLS_H
+
+#include <linux/compiler.h>
+#include <linux/linkage.h>
+
+/*
+ * Calling conventions for these system calls can differ, so
+ * it's possible to override them.
+ */
+#ifndef sys_clone
+asmlinkage long sys_clone(unsigned long clone_flags, unsigned long newsp,
+                       void __user *parent_tid, void __user *child_tid,
+                       struct pt_regs *regs);
+#endif
+
+#ifndef sys_fork
+asmlinkage long sys_fork(struct pt_regs *regs);
+#endif
+
+#ifndef sys_vfork
+asmlinkage long sys_vfork(struct pt_regs *regs);
+#endif
+
+#ifndef sys_execve
+asmlinkage long sys_execve(char __user *filename, char __user * __user *argv,
+                       char __user * __user *envp, struct pt_regs *regs);
+#endif
+
+#ifndef sys_mmap2
+asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
+                       unsigned long prot, unsigned long flags,
+                       unsigned long fd, unsigned long pgoff);
+#endif
+
+#ifndef sys_mmap
+asmlinkage long sys_mmap(unsigned long addr, unsigned long len,
+                       unsigned long prot, unsigned long flags,
+                       unsigned long fd, off_t pgoff);
+#endif
+
+#ifndef sys_sigaltstack
+asmlinkage long sys_sigaltstack(const stack_t __user *, stack_t __user *,
+                       struct pt_regs *);
+#endif
+
+#ifndef sys_rt_sigreturn
+asmlinkage long sys_rt_sigreturn(struct pt_regs *regs);
+#endif
+
+#ifndef sys_rt_sigsuspend
+asmlinkage long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize);
+#endif
+
+#ifndef sys_rt_sigaction
+asmlinkage long sys_rt_sigaction(int sig, const struct sigaction __user *act,
+                        struct sigaction __user *oact, size_t sigsetsize);
+#endif
+
+#endif /* __ASM_GENERIC_SYSCALLS_H */
diff --git a/include/asm-generic/system.h b/include/asm-generic/system.h
new file mode 100644 (file)
index 0000000..efa403b
--- /dev/null
@@ -0,0 +1,161 @@
+/* Generic system definitions, based on MN10300 definitions.
+ *
+ * It should be possible to use these on really simple architectures,
+ * but it serves more as a starting point for new ports.
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#ifndef __ASM_GENERIC_SYSTEM_H
+#define __ASM_GENERIC_SYSTEM_H
+
+#ifdef __KERNEL__
+#ifndef __ASSEMBLY__
+
+#include <linux/types.h>
+#include <linux/irqflags.h>
+
+#include <asm/cmpxchg-local.h>
+
+struct task_struct;
+
+/* context switching is now performed out-of-line in switch_to.S */
+extern struct task_struct *__switch_to(struct task_struct *,
+               struct task_struct *);
+#define switch_to(prev, next, last)                                    \
+       do {                                                            \
+               ((last) = __switch_to((prev), (next)));                 \
+       } while (0)
+
+#define arch_align_stack(x) (x)
+
+#define nop() asm volatile ("nop")
+
+#endif /* !__ASSEMBLY__ */
+
+/*
+ * Force strict CPU ordering.
+ * And yes, this is required on UP too when we're talking
+ * to devices.
+ *
+ * This implementation only contains a compiler barrier.
+ */
+
+#define mb()   asm volatile ("": : :"memory")
+#define rmb()  mb()
+#define wmb()  asm volatile ("": : :"memory")
+
+#ifdef CONFIG_SMP
+#define smp_mb()       mb()
+#define smp_rmb()      rmb()
+#define smp_wmb()      wmb()
+#else
+#define smp_mb()       barrier()
+#define smp_rmb()      barrier()
+#define smp_wmb()      barrier()
+#endif
+
+#define set_mb(var, value)  do { var = value;  mb(); } while (0)
+#define set_wmb(var, value) do { var = value; wmb(); } while (0)
+
+#define read_barrier_depends()         do {} while (0)
+#define smp_read_barrier_depends()     do {} while (0)
+
+/*
+ * we make sure local_irq_enable() doesn't cause priority inversion
+ */
+#ifndef __ASSEMBLY__
+
+/* This function doesn't exist, so you'll get a linker error
+ *    if something tries to do an invalid xchg().  */
+extern void __xchg_called_with_bad_pointer(void);
+
+static inline
+unsigned long __xchg(unsigned long x, volatile void *ptr, int size)
+{
+       unsigned long ret, flags;
+
+       switch (size) {
+       case 1:
+#ifdef __xchg_u8
+               return __xchg_u8(x, ptr);
+#else
+               local_irq_save(flags);
+               ret = *(volatile u8 *)ptr;
+               *(volatile u8 *)ptr = x;
+               local_irq_restore(flags);
+               return ret;
+#endif /* __xchg_u8 */
+
+       case 2:
+#ifdef __xchg_u16
+               return __xchg_u16(x, ptr);
+#else
+               local_irq_save(flags);
+               ret = *(volatile u16 *)ptr;
+               *(volatile u16 *)ptr = x;
+               local_irq_restore(flags);
+               return ret;
+#endif /* __xchg_u16 */
+
+       case 4:
+#ifdef __xchg_u32
+               return __xchg_u32(x, ptr);
+#else
+               local_irq_save(flags);
+               ret = *(volatile u32 *)ptr;
+               *(volatile u32 *)ptr = x;
+               local_irq_restore(flags);
+               return ret;
+#endif /* __xchg_u32 */
+
+#ifdef CONFIG_64BIT
+       case 8:
+#ifdef __xchg_u64
+               return __xchg_u64(x, ptr);
+#else
+               local_irq_save(flags);
+               ret = *(volatile u64 *)ptr;
+               *(volatile u64 *)ptr = x;
+               local_irq_restore(flags);
+               return ret;
+#endif /* __xchg_u64 */
+#endif /* CONFIG_64BIT */
+
+       default:
+               __xchg_called_with_bad_pointer();
+               return x;
+       }
+}
+
+#define xchg(ptr, x) \
+       ((__typeof__(*(ptr))) __xchg((unsigned long)(x), (ptr), sizeof(*(ptr))))
+
+static inline unsigned long __cmpxchg(volatile unsigned long *m,
+                                     unsigned long old, unsigned long new)
+{
+       unsigned long retval;
+       unsigned long flags;
+
+       local_irq_save(flags);
+       retval = *m;
+       if (retval == old)
+               *m = new;
+       local_irq_restore(flags);
+       return retval;
+}
+
+#define cmpxchg(ptr, o, n)                                     \
+       ((__typeof__(*(ptr))) __cmpxchg((unsigned long *)(ptr), \
+                                       (unsigned long)(o),     \
+                                       (unsigned long)(n)))
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* __KERNEL__ */
+#endif /* __ASM_GENERIC_SYSTEM_H */
diff --git a/include/asm-generic/termbits.h b/include/asm-generic/termbits.h
new file mode 100644 (file)
index 0000000..1c9773d
--- /dev/null
@@ -0,0 +1,198 @@
+#ifndef __ASM_GENERIC_TERMBITS_H
+#define __ASM_GENERIC_TERMBITS_H
+
+#include <linux/posix_types.h>
+
+typedef unsigned char  cc_t;
+typedef unsigned int   speed_t;
+typedef unsigned int   tcflag_t;
+
+#define NCCS 19
+struct termios {
+       tcflag_t c_iflag;               /* input mode flags */
+       tcflag_t c_oflag;               /* output mode flags */
+       tcflag_t c_cflag;               /* control mode flags */
+       tcflag_t c_lflag;               /* local mode flags */
+       cc_t c_line;                    /* line discipline */
+       cc_t c_cc[NCCS];                /* control characters */
+};
+
+struct termios2 {
+       tcflag_t c_iflag;               /* input mode flags */
+       tcflag_t c_oflag;               /* output mode flags */
+       tcflag_t c_cflag;               /* control mode flags */
+       tcflag_t c_lflag;               /* local mode flags */
+       cc_t c_line;                    /* line discipline */
+       cc_t c_cc[NCCS];                /* control characters */
+       speed_t c_ispeed;               /* input speed */
+       speed_t c_ospeed;               /* output speed */
+};
+
+struct ktermios {
+       tcflag_t c_iflag;               /* input mode flags */
+       tcflag_t c_oflag;               /* output mode flags */
+       tcflag_t c_cflag;               /* control mode flags */
+       tcflag_t c_lflag;               /* local mode flags */
+       cc_t c_line;                    /* line discipline */
+       cc_t c_cc[NCCS];                /* control characters */
+       speed_t c_ispeed;               /* input speed */
+       speed_t c_ospeed;               /* output speed */
+};
+
+/* c_cc characters */
+#define VINTR 0
+#define VQUIT 1
+#define VERASE 2
+#define VKILL 3
+#define VEOF 4
+#define VTIME 5
+#define VMIN 6
+#define VSWTC 7
+#define VSTART 8
+#define VSTOP 9
+#define VSUSP 10
+#define VEOL 11
+#define VREPRINT 12
+#define VDISCARD 13
+#define VWERASE 14
+#define VLNEXT 15
+#define VEOL2 16
+
+/* c_iflag bits */
+#define IGNBRK 0000001
+#define BRKINT 0000002
+#define IGNPAR 0000004
+#define PARMRK 0000010
+#define INPCK  0000020
+#define ISTRIP 0000040
+#define INLCR  0000100
+#define IGNCR  0000200
+#define ICRNL  0000400
+#define IUCLC  0001000
+#define IXON   0002000
+#define IXANY  0004000
+#define IXOFF  0010000
+#define IMAXBEL        0020000
+#define IUTF8  0040000
+
+/* c_oflag bits */
+#define OPOST  0000001
+#define OLCUC  0000002
+#define ONLCR  0000004
+#define OCRNL  0000010
+#define ONOCR  0000020
+#define ONLRET 0000040
+#define OFILL  0000100
+#define OFDEL  0000200
+#define NLDLY  0000400
+#define   NL0  0000000
+#define   NL1  0000400
+#define CRDLY  0003000
+#define   CR0  0000000
+#define   CR1  0001000
+#define   CR2  0002000
+#define   CR3  0003000
+#define TABDLY 0014000
+#define   TAB0 0000000
+#define   TAB1 0004000
+#define   TAB2 0010000
+#define   TAB3 0014000
+#define   XTABS        0014000
+#define BSDLY  0020000
+#define   BS0  0000000
+#define   BS1  0020000
+#define VTDLY  0040000
+#define   VT0  0000000
+#define   VT1  0040000
+#define FFDLY  0100000
+#define   FF0  0000000
+#define   FF1  0100000
+
+/* c_cflag bit meaning */
+#define CBAUD  0010017
+#define  B0    0000000         /* hang up */
+#define  B50   0000001
+#define  B75   0000002
+#define  B110  0000003
+#define  B134  0000004
+#define  B150  0000005
+#define  B200  0000006
+#define  B300  0000007
+#define  B600  0000010
+#define  B1200 0000011
+#define  B1800 0000012
+#define  B2400 0000013
+#define  B4800 0000014
+#define  B9600 0000015
+#define  B19200        0000016
+#define  B38400        0000017
+#define EXTA B19200
+#define EXTB B38400
+#define CSIZE  0000060
+#define   CS5  0000000
+#define   CS6  0000020
+#define   CS7  0000040
+#define   CS8  0000060
+#define CSTOPB 0000100
+#define CREAD  0000200
+#define PARENB 0000400
+#define PARODD 0001000
+#define HUPCL  0002000
+#define CLOCAL 0004000
+#define CBAUDEX 0010000
+#define    BOTHER 0010000
+#define    B57600 0010001
+#define   B115200 0010002
+#define   B230400 0010003
+#define   B460800 0010004
+#define   B500000 0010005
+#define   B576000 0010006
+#define   B921600 0010007
+#define  B1000000 0010010
+#define  B1152000 0010011
+#define  B1500000 0010012
+#define  B2000000 0010013
+#define  B2500000 0010014
+#define  B3000000 0010015
+#define  B3500000 0010016
+#define  B4000000 0010017
+#define CIBAUD   002003600000  /* input baud rate */
+#define CMSPAR   010000000000  /* mark or space (stick) parity */
+#define CRTSCTS          020000000000  /* flow control */
+
+#define IBSHIFT          16            /* Shift from CBAUD to CIBAUD */
+
+/* c_lflag bits */
+#define ISIG   0000001
+#define ICANON 0000002
+#define XCASE  0000004
+#define ECHO   0000010
+#define ECHOE  0000020
+#define ECHOK  0000040
+#define ECHONL 0000100
+#define NOFLSH 0000200
+#define TOSTOP 0000400
+#define ECHOCTL        0001000
+#define ECHOPRT        0002000
+#define ECHOKE 0004000
+#define FLUSHO 0010000
+#define PENDIN 0040000
+#define IEXTEN 0100000
+
+/* tcflow() and TCXONC use these */
+#define        TCOOFF          0
+#define        TCOON           1
+#define        TCIOFF          2
+#define        TCION           3
+
+/* tcflush() and TCFLSH use these */
+#define        TCIFLUSH        0
+#define        TCOFLUSH        1
+#define        TCIOFLUSH       2
+
+/* tcsetattr uses these */
+#define        TCSANOW         0
+#define        TCSADRAIN       1
+#define        TCSAFLUSH       2
+
+#endif /* __ASM_GENERIC_TERMBITS_H */
diff --git a/include/asm-generic/termios-base.h b/include/asm-generic/termios-base.h
new file mode 100644 (file)
index 0000000..0a769fe
--- /dev/null
@@ -0,0 +1,77 @@
+/* termios.h: generic termios/termio user copying/translation
+ */
+
+#ifndef _ASM_GENERIC_TERMIOS_BASE_H
+#define _ASM_GENERIC_TERMIOS_BASE_H
+
+#include <asm/uaccess.h>
+
+#ifndef __ARCH_TERMIO_GETPUT
+
+/*
+ * Translate a "termio" structure into a "termios". Ugh.
+ */
+static inline int user_termio_to_kernel_termios(struct ktermios *termios,
+                                               struct termio __user *termio)
+{
+       unsigned short tmp;
+
+       if (get_user(tmp, &termio->c_iflag) < 0)
+               goto fault;
+       termios->c_iflag = (0xffff0000 & termios->c_iflag) | tmp;
+
+       if (get_user(tmp, &termio->c_oflag) < 0)
+               goto fault;
+       termios->c_oflag = (0xffff0000 & termios->c_oflag) | tmp;
+
+       if (get_user(tmp, &termio->c_cflag) < 0)
+               goto fault;
+       termios->c_cflag = (0xffff0000 & termios->c_cflag) | tmp;
+
+       if (get_user(tmp, &termio->c_lflag) < 0)
+               goto fault;
+       termios->c_lflag = (0xffff0000 & termios->c_lflag) | tmp;
+
+       if (get_user(termios->c_line, &termio->c_line) < 0)
+               goto fault;
+
+       if (copy_from_user(termios->c_cc, termio->c_cc, NCC) != 0)
+               goto fault;
+
+       return 0;
+
+ fault:
+       return -EFAULT;
+}
+
+/*
+ * Translate a "termios" structure into a "termio". Ugh.
+ */
+static inline int kernel_termios_to_user_termio(struct termio __user *termio,
+                                               struct ktermios *termios)
+{
+       if (put_user(termios->c_iflag, &termio->c_iflag) < 0 ||
+           put_user(termios->c_oflag, &termio->c_oflag) < 0 ||
+           put_user(termios->c_cflag, &termio->c_cflag) < 0 ||
+           put_user(termios->c_lflag, &termio->c_lflag) < 0 ||
+           put_user(termios->c_line,  &termio->c_line) < 0 ||
+           copy_to_user(termio->c_cc, termios->c_cc, NCC) != 0)
+               return -EFAULT;
+
+       return 0;
+}
+
+#ifndef user_termios_to_kernel_termios
+#define user_termios_to_kernel_termios(k, u) copy_from_user(k, u, sizeof(struct termios))
+#endif
+
+#ifndef kernel_termios_to_user_termios
+#define kernel_termios_to_user_termios(u, k) copy_to_user(u, k, sizeof(struct termios))
+#endif
+
+#define user_termios_to_kernel_termios_1(k, u) copy_from_user(k, u, sizeof(struct termios))
+#define kernel_termios_to_user_termios_1(u, k) copy_to_user(u, k, sizeof(struct termios))
+
+#endif /* __ARCH_TERMIO_GETPUT */
+
+#endif /* _ASM_GENERIC_TERMIOS_BASE_H */
index 7d39ecc92d9430380ccd00d4be4021932b1db8c3..d0922adc56d46949cf4fca9b768ad1296b1feb88 100644 (file)
@@ -1,18 +1,68 @@
-/* termios.h: generic termios/termio user copying/translation
- */
-
 #ifndef _ASM_GENERIC_TERMIOS_H
 #define _ASM_GENERIC_TERMIOS_H
+/*
+ * Most architectures have straight copies of the x86 code, with
+ * varying levels of bug fixes on top. Usually it's a good idea
+ * to use this generic version instead, but be careful to avoid
+ * ABI changes.
+ * New architectures should not provide their own version.
+ */
+
+#include <asm/termbits.h>
+#include <asm/ioctls.h>
+
+struct winsize {
+       unsigned short ws_row;
+       unsigned short ws_col;
+       unsigned short ws_xpixel;
+       unsigned short ws_ypixel;
+};
+
+#define NCC 8
+struct termio {
+       unsigned short c_iflag;         /* input mode flags */
+       unsigned short c_oflag;         /* output mode flags */
+       unsigned short c_cflag;         /* control mode flags */
+       unsigned short c_lflag;         /* local mode flags */
+       unsigned char c_line;           /* line discipline */
+       unsigned char c_cc[NCC];        /* control characters */
+};
+
+/* modem lines */
+#define TIOCM_LE       0x001
+#define TIOCM_DTR      0x002
+#define TIOCM_RTS      0x004
+#define TIOCM_ST       0x008
+#define TIOCM_SR       0x010
+#define TIOCM_CTS      0x020
+#define TIOCM_CAR      0x040
+#define TIOCM_RNG      0x080
+#define TIOCM_DSR      0x100
+#define TIOCM_CD       TIOCM_CAR
+#define TIOCM_RI       TIOCM_RNG
+#define TIOCM_OUT1     0x2000
+#define TIOCM_OUT2     0x4000
+#define TIOCM_LOOP     0x8000
+
+/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
+
+#ifdef __KERNEL__
 
 #include <asm/uaccess.h>
 
-#ifndef __ARCH_TERMIO_GETPUT
+/*     intr=^C         quit=^\         erase=del       kill=^U
+       eof=^D          vtime=\0        vmin=\1         sxtc=\0
+       start=^Q        stop=^S         susp=^Z         eol=\0
+       reprint=^R      discard=^U      werase=^W       lnext=^V
+       eol2=\0
+*/
+#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0"
 
 /*
  * Translate a "termio" structure into a "termios". Ugh.
  */
 static inline int user_termio_to_kernel_termios(struct ktermios *termios,
-                                               struct termio __user *termio)
+                                               const struct termio __user *termio)
 {
        unsigned short tmp;
 
@@ -61,17 +111,44 @@ static inline int kernel_termios_to_user_termio(struct termio __user *termio,
        return 0;
 }
 
-#ifndef user_termios_to_kernel_termios
-#define user_termios_to_kernel_termios(k, u) copy_from_user(k, u, sizeof(struct termios))
-#endif
+#ifdef TCGETS2
+static inline int user_termios_to_kernel_termios(struct ktermios *k,
+                                                struct termios2 __user *u)
+{
+       return copy_from_user(k, u, sizeof(struct termios2));
+}
 
-#ifndef kernel_termios_to_user_termios
-#define kernel_termios_to_user_termios(u, k) copy_to_user(u, k, sizeof(struct termios))
-#endif
+static inline int kernel_termios_to_user_termios(struct termios2 __user *u,
+                                                struct ktermios *k)
+{
+       return copy_to_user(u, k, sizeof(struct termios2));
+}
 
-#define user_termios_to_kernel_termios_1(k, u) copy_from_user(k, u, sizeof(struct termios))
-#define kernel_termios_to_user_termios_1(u, k) copy_to_user(u, k, sizeof(struct termios))
+static inline int user_termios_to_kernel_termios_1(struct ktermios *k,
+                                                  struct termios __user *u)
+{
+       return copy_from_user(k, u, sizeof(struct termios));
+}
+
+static inline int kernel_termios_to_user_termios_1(struct termios __user *u,
+                                                  struct ktermios *k)
+{
+       return copy_to_user(u, k, sizeof(struct termios));
+}
+#else /* TCGETS2 */
+static inline int user_termios_to_kernel_termios(struct ktermios *k,
+                                                struct termios __user *u)
+{
+       return copy_from_user(k, u, sizeof(struct termios));
+}
+
+static inline int kernel_termios_to_user_termios(struct termios __user *u,
+                                                struct ktermios *k)
+{
+       return copy_to_user(u, k, sizeof(struct termios));
+}
+#endif /* TCGETS2 */
 
-#endif /* __ARCH_TERMIO_GETPUT */
+#endif /* __KERNEL__ */
 
 #endif /* _ASM_GENERIC_TERMIOS_H */
diff --git a/include/asm-generic/timex.h b/include/asm-generic/timex.h
new file mode 100644 (file)
index 0000000..b2243cb
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef __ASM_GENERIC_TIMEX_H
+#define __ASM_GENERIC_TIMEX_H
+
+/*
+ * If you have a cycle counter, return the value here.
+ */
+typedef unsigned long cycles_t;
+#ifndef get_cycles
+static inline cycles_t get_cycles(void)
+{
+       return 0;
+}
+#endif
+
+/*
+ * Architectures are encouraged to implement read_current_timer
+ * and define this in order to avoid the expensive delay loop
+ * calibration during boot.
+ */
+#undef ARCH_HAS_READ_CURRENT_TIMER
+
+#endif /* __ASM_GENERIC_TIMEX_H */
diff --git a/include/asm-generic/tlbflush.h b/include/asm-generic/tlbflush.h
new file mode 100644 (file)
index 0000000..c7af037
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef __ASM_GENERIC_TLBFLUSH_H
+#define __ASM_GENERIC_TLBFLUSH_H
+/*
+ * This is a dummy tlbflush implementation that can be used on all
+ * nommu architectures.
+ * If you have an MMU, you need to write your own functions.
+ */
+#ifdef CONFIG_MMU
+#error need to implement an architecture specific asm/tlbflush.h
+#endif
+
+static inline void flush_tlb_mm(struct mm_struct *mm)
+{
+       BUG();
+}
+
+
+#endif /* __ASM_GENERIC_TLBFLUSH_H */
diff --git a/include/asm-generic/types.h b/include/asm-generic/types.h
new file mode 100644 (file)
index 0000000..fba7d33
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef _ASM_GENERIC_TYPES_H
+#define _ASM_GENERIC_TYPES_H
+/*
+ * int-ll64 is used practically everywhere now,
+ * so use it as a reasonable default.
+ */
+#include <asm-generic/int-ll64.h>
+
+#ifndef __ASSEMBLY__
+
+typedef unsigned short umode_t;
+
+#endif /* __ASSEMBLY__ */
+
+/*
+ * These aren't exported outside the kernel to avoid name space clashes
+ */
+#ifdef __KERNEL__
+#ifndef __ASSEMBLY__
+/*
+ * DMA addresses may be very different from physical addresses
+ * and pointers. i386 and powerpc may have 64 bit DMA on 32 bit
+ * systems, while sparc64 uses 32 bit DMA addresses for 64 bit
+ * physical addresses.
+ * This default defines dma_addr_t to have the same size as
+ * phys_addr_t, which is the most common way.
+ * Do not define the dma64_addr_t type, which never really
+ * worked.
+ */
+#ifndef dma_addr_t
+#ifdef CONFIG_PHYS_ADDR_T_64BIT
+typedef u64 dma_addr_t;
+#else
+typedef u32 dma_addr_t;
+#endif /* CONFIG_PHYS_ADDR_T_64BIT */
+#endif /* dma_addr_t */
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_GENERIC_TYPES_H */
diff --git a/include/asm-generic/uaccess-unaligned.h b/include/asm-generic/uaccess-unaligned.h
new file mode 100644 (file)
index 0000000..67deb89
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef __ASM_GENERIC_UACCESS_UNALIGNED_H
+#define __ASM_GENERIC_UACCESS_UNALIGNED_H
+
+/*
+ * This macro should be used instead of __get_user() when accessing
+ * values at locations that are not known to be aligned.
+ */
+#define __get_user_unaligned(x, ptr)                                   \
+({                                                                     \
+       __typeof__ (*(ptr)) __x;                                        \
+       __copy_from_user(&__x, (ptr), sizeof(*(ptr))) ? -EFAULT : 0;    \
+       (x) = __x;                                                      \
+})
+
+
+/*
+ * This macro should be used instead of __put_user() when accessing
+ * values at locations that are not known to be aligned.
+ */
+#define __put_user_unaligned(x, ptr)                                   \
+({                                                                     \
+       __typeof__ (*(ptr)) __x = (x);                                  \
+       __copy_to_user((ptr), &__x, sizeof(*(ptr))) ? -EFAULT : 0;      \
+})
+
+#endif /* __ASM_GENERIC_UACCESS_UNALIGNED_H */
index 549cb3a1640a5c7d3afba681d68a58cfbcc8664c..6d8cab22e2941b943dda136a924f4698114a44a9 100644 (file)
-#ifndef _ASM_GENERIC_UACCESS_H_
-#define _ASM_GENERIC_UACCESS_H_
+#ifndef __ASM_GENERIC_UACCESS_H
+#define __ASM_GENERIC_UACCESS_H
 
 /*
- * This macro should be used instead of __get_user() when accessing
- * values at locations that are not known to be aligned.
+ * User space memory access functions, these should work
+ * on a ny machine that has kernel and user data in the same
+ * address space, e.g. all NOMMU machines.
  */
-#define __get_user_unaligned(x, ptr)                                   \
-({                                                                     \
-       __typeof__ (*(ptr)) __x;                                        \
-       __copy_from_user(&__x, (ptr), sizeof(*(ptr))) ? -EFAULT : 0;    \
-       (x) = __x;                                                      \
-})
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+
+#include <asm/segment.h>
+
+#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) })
+
+#ifndef KERNEL_DS
+#define KERNEL_DS      MAKE_MM_SEG(~0UL)
+#endif
+
+#ifndef USER_DS
+#define USER_DS                MAKE_MM_SEG(TASK_SIZE - 1)
+#endif
+
+#ifndef get_fs
+#define get_ds()       (KERNEL_DS)
+#define get_fs()       (current_thread_info()->addr_limit)
+
+static inline void set_fs(mm_segment_t fs)
+{
+       current_thread_info()->addr_limit = fs;
+}
+#endif
 
+#define segment_eq(a, b) ((a).seg == (b).seg)
+
+#define VERIFY_READ    0
+#define VERIFY_WRITE   1
+
+#define access_ok(type, addr, size) __access_ok((unsigned long)(addr),(size))
+
+/*
+ * The architecture should really override this if possible, at least
+ * doing a check on the get_fs()
+ */
+#ifndef __access_ok
+static inline int __access_ok(unsigned long addr, unsigned long size)
+{
+       return 1;
+}
+#endif
 
 /*
- * This macro should be used instead of __put_user() when accessing
- * values at locations that are not known to be aligned.
+ * The exception table consists of pairs of addresses: the first is the
+ * address of an instruction that is allowed to fault, and the second is
+ * the address at which the program should continue.  No registers are
+ * modified, so it is entirely up to the continuation code to figure out
+ * what to do.
+ *
+ * All the routines below use bits of fixup code that are out of line
+ * with the main instruction path.  This means when everything is well,
+ * we don't even have to jump over them.  Further, they do not intrude
+ * on our cache or tlb entries.
  */
-#define __put_user_unaligned(x, ptr)                                   \
-({                                                                     \
-       __typeof__ (*(ptr)) __x = (x);                                  \
-       __copy_to_user((ptr), &__x, sizeof(*(ptr))) ? -EFAULT : 0;      \
+
+struct exception_table_entry
+{
+       unsigned long insn, fixup;
+};
+
+/* Returns 0 if exception not found and fixup otherwise.  */
+extern unsigned long search_exception_table(unsigned long);
+
+/*
+ * architectures with an MMU should override these two
+ */
+#ifndef __copy_from_user
+static inline __must_check long __copy_from_user(void *to,
+               const void __user * from, unsigned long n)
+{
+       if (__builtin_constant_p(n)) {
+               switch(n) {
+               case 1:
+                       *(u8 *)to = *(u8 __force *)from;
+                       return 0;
+               case 2:
+                       *(u16 *)to = *(u16 __force *)from;
+                       return 0;
+               case 4:
+                       *(u32 *)to = *(u32 __force *)from;
+                       return 0;
+#ifdef CONFIG_64BIT
+               case 8:
+                       *(u64 *)to = *(u64 __force *)from;
+                       return 0;
+#endif
+               default:
+                       break;
+               }
+       }
+
+       memcpy(to, (const void __force *)from, n);
+       return 0;
+}
+#endif
+
+#ifndef __copy_to_user
+static inline __must_check long __copy_to_user(void __user *to,
+               const void *from, unsigned long n)
+{
+       if (__builtin_constant_p(n)) {
+               switch(n) {
+               case 1:
+                       *(u8 __force *)to = *(u8 *)from;
+                       return 0;
+               case 2:
+                       *(u16 __force *)to = *(u16 *)from;
+                       return 0;
+               case 4:
+                       *(u32 __force *)to = *(u32 *)from;
+                       return 0;
+#ifdef CONFIG_64BIT
+               case 8:
+                       *(u64 __force *)to = *(u64 *)from;
+                       return 0;
+#endif
+               default:
+                       break;
+               }
+       }
+
+       memcpy((void __force *)to, from, n);
+       return 0;
+}
+#endif
+
+/*
+ * These are the main single-value transfer routines.  They automatically
+ * use the right size if we just have the right pointer type.
+ * This version just falls back to copy_{from,to}_user, which should
+ * provide a fast-path for small values.
+ */
+#define __put_user(x, ptr) \
+({                                                             \
+       __typeof__(*(ptr)) __x = (x);                           \
+       int __pu_err = -EFAULT;                                 \
+        __chk_user_ptr(ptr);                                    \
+       switch (sizeof (*(ptr))) {                              \
+       case 1:                                                 \
+       case 2:                                                 \
+       case 4:                                                 \
+       case 8:                                                 \
+               __pu_err = __put_user_fn(sizeof (*(ptr)),       \
+                                        ptr, &__x);            \
+               break;                                          \
+       default:                                                \
+               __put_user_bad();                               \
+               break;                                          \
+        }                                                      \
+       __pu_err;                                               \
+})
+
+#define put_user(x, ptr)                                       \
+({                                                             \
+       might_sleep();                                          \
+       __access_ok(ptr, sizeof (*ptr)) ?                       \
+               __put_user(x, ptr) :                            \
+               -EFAULT;                                        \
 })
 
-#endif /* _ASM_GENERIC_UACCESS_H */
+static inline int __put_user_fn(size_t size, void __user *ptr, void *x)
+{
+       size = __copy_to_user(ptr, x, size);
+       return size ? -EFAULT : size;
+}
+
+extern int __put_user_bad(void) __attribute__((noreturn));
+
+#define __get_user(x, ptr)                                     \
+({                                                             \
+       int __gu_err = -EFAULT;                                 \
+       __chk_user_ptr(ptr);                                    \
+       switch (sizeof(*(ptr))) {                               \
+       case 1: {                                               \
+               unsigned char __x;                              \
+               __gu_err = __get_user_fn(sizeof (*(ptr)),       \
+                                        ptr, &__x);            \
+               (x) = *(__force __typeof__(*(ptr)) *) &__x;     \
+               break;                                          \
+       };                                                      \
+       case 2: {                                               \
+               unsigned short __x;                             \
+               __gu_err = __get_user_fn(sizeof (*(ptr)),       \
+                                        ptr, &__x);            \
+               (x) = *(__force __typeof__(*(ptr)) *) &__x;     \
+               break;                                          \
+       };                                                      \
+       case 4: {                                               \
+               unsigned int __x;                               \
+               __gu_err = __get_user_fn(sizeof (*(ptr)),       \
+                                        ptr, &__x);            \
+               (x) = *(__force __typeof__(*(ptr)) *) &__x;     \
+               break;                                          \
+       };                                                      \
+       case 8: {                                               \
+               unsigned long long __x;                         \
+               __gu_err = __get_user_fn(sizeof (*(ptr)),       \
+                                        ptr, &__x);            \
+               (x) = *(__force __typeof__(*(ptr)) *) &__x;     \
+               break;                                          \
+       };                                                      \
+       default:                                                \
+               __get_user_bad();                               \
+               break;                                          \
+       }                                                       \
+       __gu_err;                                               \
+})
+
+#define get_user(x, ptr)                                       \
+({                                                             \
+       might_sleep();                                          \
+       __access_ok(ptr, sizeof (*ptr)) ?                       \
+               __get_user(x, ptr) :                            \
+               -EFAULT;                                        \
+})
+
+static inline int __get_user_fn(size_t size, const void __user *ptr, void *x)
+{
+       size = __copy_from_user(x, ptr, size);
+       return size ? -EFAULT : size;
+}
+
+extern int __get_user_bad(void) __attribute__((noreturn));
+
+#ifndef __copy_from_user_inatomic
+#define __copy_from_user_inatomic __copy_from_user
+#endif
+
+#ifndef __copy_to_user_inatomic
+#define __copy_to_user_inatomic __copy_to_user
+#endif
+
+static inline long copy_from_user(void *to,
+               const void __user * from, unsigned long n)
+{
+       might_sleep();
+       if (__access_ok(from, n))
+               return __copy_from_user(to, from, n);
+       else
+               return n;
+}
+
+static inline long copy_to_user(void __user *to,
+               const void *from, unsigned long n)
+{
+       might_sleep();
+       if (__access_ok(to, n))
+               return __copy_to_user(to, from, n);
+       else
+               return n;
+}
+
+/*
+ * Copy a null terminated string from userspace.
+ */
+#ifndef __strncpy_from_user
+static inline long
+__strncpy_from_user(char *dst, const char __user *src, long count)
+{
+       char *tmp;
+       strncpy(dst, (const char __force *)src, count);
+       for (tmp = dst; *tmp && count > 0; tmp++, count--)
+               ;
+       return (tmp - dst);
+}
+#endif
+
+static inline long
+strncpy_from_user(char *dst, const char __user *src, long count)
+{
+       if (!__access_ok(src, 1))
+               return -EFAULT;
+       return __strncpy_from_user(dst, src, count);
+}
+
+/*
+ * Return the size of a string (including the ending 0)
+ *
+ * Return 0 on exception, a value greater than N if too long
+ */
+#ifndef strnlen_user
+static inline long strnlen_user(const char __user *src, long n)
+{
+       return strlen((void * __force)src) + 1;
+}
+#endif
+
+static inline long strlen_user(const char __user *src)
+{
+       return strnlen_user(src, 32767);
+}
+
+/*
+ * Zero Userspace
+ */
+#ifndef __clear_user
+static inline __must_check unsigned long
+__clear_user(void __user *to, unsigned long n)
+{
+       memset((void __force *)to, 0, n);
+       return 0;
+}
+#endif
+
+static inline __must_check unsigned long
+clear_user(void __user *to, unsigned long n)
+{
+       might_sleep();
+       if (!__access_ok(to, n))
+               return n;
+
+       return __clear_user(to, n);
+}
+
+#endif /* __ASM_GENERIC_UACCESS_H */
diff --git a/include/asm-generic/ucontext.h b/include/asm-generic/ucontext.h
new file mode 100644 (file)
index 0000000..ad77343
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __ASM_GENERIC_UCONTEXT_H
+#define __ASM_GENERIC_UCONTEXT_H
+
+struct ucontext {
+       unsigned long     uc_flags;
+       struct ucontext  *uc_link;
+       stack_t           uc_stack;
+       struct sigcontext uc_mcontext;
+       sigset_t          uc_sigmask;   /* mask last for extensibility */
+};
+
+#endif /* __ASM_GENERIC_UCONTEXT_H */
diff --git a/include/asm-generic/unaligned.h b/include/asm-generic/unaligned.h
new file mode 100644 (file)
index 0000000..03cf593
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef __ASM_GENERIC_UNALIGNED_H
+#define __ASM_GENERIC_UNALIGNED_H
+
+/*
+ * This is the most generic implementation of unaligned accesses
+ * and should work almost anywhere.
+ *
+ * If an architecture can handle unaligned accesses in hardware,
+ * it may want to use the linux/unaligned/access_ok.h implementation
+ * instead.
+ */
+#include <asm/byteorder.h>
+
+#if defined(__LITTLE_ENDIAN)
+# include <linux/unaligned/le_struct.h>
+# include <linux/unaligned/be_byteshift.h>
+# include <linux/unaligned/generic.h>
+# define get_unaligned __get_unaligned_le
+# define put_unaligned __put_unaligned_le
+#elif defined(__BIG_ENDIAN)
+# include <linux/unaligned/be_struct.h>
+# include <linux/unaligned/le_byteshift.h>
+# include <linux/unaligned/generic.h>
+# define get_unaligned __get_unaligned_be
+# define put_unaligned __put_unaligned_be
+#else
+# error need to define endianess
+#endif
+
+#endif /* __ASM_GENERIC_UNALIGNED_H */
diff --git a/include/asm-generic/unistd.h b/include/asm-generic/unistd.h
new file mode 100644 (file)
index 0000000..5b34b62
--- /dev/null
@@ -0,0 +1,854 @@
+#if !defined(_ASM_GENERIC_UNISTD_H) || defined(__SYSCALL)
+#define _ASM_GENERIC_UNISTD_H
+
+#include <asm/bitsperlong.h>
+
+/*
+ * This file contains the system call numbers, based on the
+ * layout of the x86-64 architecture, which embeds the
+ * pointer to the syscall in the table.
+ *
+ * As a basic principle, no duplication of functionality
+ * should be added, e.g. we don't use lseek when llseek
+ * is present. New architectures should use this file
+ * and implement the less feature-full calls in user space.
+ */
+
+#ifndef __SYSCALL
+#define __SYSCALL(x, y)
+#endif
+
+#if __BITS_PER_LONG == 32
+#define __SC_3264(_nr, _32, _64) __SYSCALL(_nr, _32)
+#else
+#define __SC_3264(_nr, _32, _64) __SYSCALL(_nr, _64)
+#endif
+
+#define __NR_io_setup 0
+__SYSCALL(__NR_io_setup, sys_io_setup)
+#define __NR_io_destroy 1
+__SYSCALL(__NR_io_destroy, sys_io_destroy)
+#define __NR_io_submit 2
+__SYSCALL(__NR_io_submit, sys_io_submit)
+#define __NR_io_cancel 3
+__SYSCALL(__NR_io_cancel, sys_io_cancel)
+#define __NR_io_getevents 4
+__SYSCALL(__NR_io_getevents, sys_io_getevents)
+
+/* fs/xattr.c */
+#define __NR_setxattr 5
+__SYSCALL(__NR_setxattr, sys_setxattr)
+#define __NR_lsetxattr 6
+__SYSCALL(__NR_lsetxattr, sys_lsetxattr)
+#define __NR_fsetxattr 7
+__SYSCALL(__NR_fsetxattr, sys_fsetxattr)
+#define __NR_getxattr 8
+__SYSCALL(__NR_getxattr, sys_getxattr)
+#define __NR_lgetxattr 9
+__SYSCALL(__NR_lgetxattr, sys_lgetxattr)
+#define __NR_fgetxattr 10
+__SYSCALL(__NR_fgetxattr, sys_fgetxattr)
+#define __NR_listxattr 11
+__SYSCALL(__NR_listxattr, sys_listxattr)
+#define __NR_llistxattr 12
+__SYSCALL(__NR_llistxattr, sys_llistxattr)
+#define __NR_flistxattr 13
+__SYSCALL(__NR_flistxattr, sys_flistxattr)
+#define __NR_removexattr 14
+__SYSCALL(__NR_removexattr, sys_removexattr)
+#define __NR_lremovexattr 15
+__SYSCALL(__NR_lremovexattr, sys_lremovexattr)
+#define __NR_fremovexattr 16
+__SYSCALL(__NR_fremovexattr, sys_fremovexattr)
+
+/* fs/dcache.c */
+#define __NR_getcwd 17
+__SYSCALL(__NR_getcwd, sys_getcwd)
+
+/* fs/cookies.c */
+#define __NR_lookup_dcookie 18
+__SYSCALL(__NR_lookup_dcookie, sys_lookup_dcookie)
+
+/* fs/eventfd.c */
+#define __NR_eventfd2 19
+__SYSCALL(__NR_eventfd2, sys_eventfd2)
+
+/* fs/eventpoll.c */
+#define __NR_epoll_create1 20
+__SYSCALL(__NR_epoll_create1, sys_epoll_create1)
+#define __NR_epoll_ctl 21
+__SYSCALL(__NR_epoll_ctl, sys_epoll_ctl)
+#define __NR_epoll_pwait 22
+__SYSCALL(__NR_epoll_pwait, sys_epoll_pwait)
+
+/* fs/fcntl.c */
+#define __NR_dup 23
+__SYSCALL(__NR_dup, sys_dup)
+#define __NR_dup3 24
+__SYSCALL(__NR_dup3, sys_dup3)
+#define __NR3264_fcntl 25
+__SC_3264(__NR3264_fcntl, sys_fcntl64, sys_fcntl)
+
+/* fs/inotify_user.c */
+#define __NR_inotify_init1 26
+__SYSCALL(__NR_inotify_init1, sys_inotify_init1)
+#define __NR_inotify_add_watch 27
+__SYSCALL(__NR_inotify_add_watch, sys_inotify_add_watch)
+#define __NR_inotify_rm_watch 28
+__SYSCALL(__NR_inotify_rm_watch, sys_inotify_rm_watch)
+
+/* fs/ioctl.c */
+#define __NR_ioctl 29
+__SYSCALL(__NR_ioctl, sys_ioctl)
+
+/* fs/ioprio.c */
+#define __NR_ioprio_set 30
+__SYSCALL(__NR_ioprio_set, sys_ioprio_set)
+#define __NR_ioprio_get 31
+__SYSCALL(__NR_ioprio_get, sys_ioprio_get)
+
+/* fs/locks.c */
+#define __NR_flock 32
+__SYSCALL(__NR_flock, sys_flock)
+
+/* fs/namei.c */
+#define __NR_mknodat 33
+__SYSCALL(__NR_mknodat, sys_mknodat)
+#define __NR_mkdirat 34
+__SYSCALL(__NR_mkdirat, sys_mkdirat)
+#define __NR_unlinkat 35
+__SYSCALL(__NR_unlinkat, sys_unlinkat)
+#define __NR_symlinkat 36
+__SYSCALL(__NR_symlinkat, sys_symlinkat)
+#define __NR_linkat 37
+__SYSCALL(__NR_linkat, sys_linkat)
+#define __NR_renameat 38
+__SYSCALL(__NR_renameat, sys_renameat)
+
+/* fs/namespace.c */
+#define __NR_umount2 39
+__SYSCALL(__NR_umount2, sys_umount)
+#define __NR_mount 40
+__SYSCALL(__NR_mount, sys_mount)
+#define __NR_pivot_root 41
+__SYSCALL(__NR_pivot_root, sys_pivot_root)
+
+/* fs/nfsctl.c */
+#define __NR_nfsservctl 42
+__SYSCALL(__NR_nfsservctl, sys_nfsservctl)
+
+/* fs/open.c */
+#define __NR3264_statfs 43
+__SC_3264(__NR3264_statfs, sys_statfs64, sys_statfs)
+#define __NR3264_fstatfs 44
+__SC_3264(__NR3264_fstatfs, sys_fstatfs64, sys_fstatfs)
+#define __NR3264_truncate 45
+__SC_3264(__NR3264_truncate, sys_truncate64, sys_truncate)
+#define __NR3264_ftruncate 46
+__SC_3264(__NR3264_ftruncate, sys_ftruncate64, sys_ftruncate)
+
+#define __NR_fallocate 47
+__SYSCALL(__NR_fallocate, sys_fallocate)
+#define __NR_faccessat 48
+__SYSCALL(__NR_faccessat, sys_faccessat)
+#define __NR_chdir 49
+__SYSCALL(__NR_chdir, sys_chdir)
+#define __NR_fchdir 50
+__SYSCALL(__NR_fchdir, sys_fchdir)
+#define __NR_chroot 51
+__SYSCALL(__NR_chroot, sys_chroot)
+#define __NR_fchmod 52
+__SYSCALL(__NR_fchmod, sys_fchmod)
+#define __NR_fchmodat 53
+__SYSCALL(__NR_fchmodat, sys_fchmodat)
+#define __NR_fchownat 54
+__SYSCALL(__NR_fchownat, sys_fchownat)
+#define __NR_fchown 55
+__SYSCALL(__NR_fchown, sys_fchown)
+#define __NR_openat 56
+__SYSCALL(__NR_openat, sys_openat)
+#define __NR_close 57
+__SYSCALL(__NR_close, sys_close)
+#define __NR_vhangup 58
+__SYSCALL(__NR_vhangup, sys_vhangup)
+
+/* fs/pipe.c */
+#define __NR_pipe2 59
+__SYSCALL(__NR_pipe2, sys_pipe2)
+
+/* fs/quota.c */
+#define __NR_quotactl 60
+__SYSCALL(__NR_quotactl, sys_quotactl)
+
+/* fs/readdir.c */
+#define __NR_getdents64 61
+__SYSCALL(__NR_getdents64, sys_getdents64)
+
+/* fs/read_write.c */
+#define __NR3264_lseek 62
+__SC_3264(__NR3264_lseek, sys_llseek, sys_lseek)
+#define __NR_read 63
+__SYSCALL(__NR_read, sys_read)
+#define __NR_write 64
+__SYSCALL(__NR_write, sys_write)
+#define __NR_readv 65
+__SYSCALL(__NR_readv, sys_readv)
+#define __NR_writev 66
+__SYSCALL(__NR_writev, sys_writev)
+#define __NR_pread64 67
+__SYSCALL(__NR_pread64, sys_pread64)
+#define __NR_pwrite64 68
+__SYSCALL(__NR_pwrite64, sys_pwrite64)
+#define __NR_preadv 69
+__SYSCALL(__NR_preadv, sys_preadv)
+#define __NR_pwritev 70
+__SYSCALL(__NR_pwritev, sys_pwritev)
+
+/* fs/sendfile.c */
+#define __NR3264_sendfile 71
+__SC_3264(__NR3264_sendfile, sys_sendfile64, sys_sendfile)
+
+/* fs/select.c */
+#define __NR_pselect6 72
+__SYSCALL(__NR_pselect6, sys_pselect6)
+#define __NR_ppoll 73
+__SYSCALL(__NR_ppoll, sys_ppoll)
+
+/* fs/signalfd.c */
+#define __NR_signalfd4 74
+__SYSCALL(__NR_signalfd4, sys_signalfd4)
+
+/* fs/splice.c */
+#define __NR_vmsplice 75
+__SYSCALL(__NR_vmsplice, sys_vmsplice)
+#define __NR_splice 76
+__SYSCALL(__NR_splice, sys_splice)
+#define __NR_tee 77
+__SYSCALL(__NR_tee, sys_tee)
+
+/* fs/stat.c */
+#define __NR_readlinkat 78
+__SYSCALL(__NR_readlinkat, sys_readlinkat)
+#define __NR3264_fstatat 79
+__SC_3264(__NR3264_fstatat, sys_fstatat64, sys_newfstatat)
+#define __NR3264_fstat 80
+__SC_3264(__NR3264_fstat, sys_fstat64, sys_newfstat)
+
+/* fs/sync.c */
+#define __NR_sync 81
+__SYSCALL(__NR_sync, sys_sync)
+#define __NR_fsync 82
+__SYSCALL(__NR_fsync, sys_fsync)
+#define __NR_fdatasync 83
+__SYSCALL(__NR_fdatasync, sys_fdatasync)
+#define __NR_sync_file_range 84
+__SYSCALL(__NR_sync_file_range, sys_sync_file_range) /* .long sys_sync_file_range2, */
+
+/* fs/timerfd.c */
+#define __NR_timerfd_create 85
+__SYSCALL(__NR_timerfd_create, sys_timerfd_create)
+#define __NR_timerfd_settime 86
+__SYSCALL(__NR_timerfd_settime, sys_timerfd_settime)
+#define __NR_timerfd_gettime 87
+__SYSCALL(__NR_timerfd_gettime, sys_timerfd_gettime)
+
+/* fs/utimes.c */
+#define __NR_utimensat 88
+__SYSCALL(__NR_utimensat, sys_utimensat)
+
+/* kernel/acct.c */
+#define __NR_acct 89
+__SYSCALL(__NR_acct, sys_acct)
+
+/* kernel/capability.c */
+#define __NR_capget 90
+__SYSCALL(__NR_capget, sys_capget)
+#define __NR_capset 91
+__SYSCALL(__NR_capset, sys_capset)
+
+/* kernel/exec_domain.c */
+#define __NR_personality 92
+__SYSCALL(__NR_personality, sys_personality)
+
+/* kernel/exit.c */
+#define __NR_exit 93
+__SYSCALL(__NR_exit, sys_exit)
+#define __NR_exit_group 94
+__SYSCALL(__NR_exit_group, sys_exit_group)
+#define __NR_waitid 95
+__SYSCALL(__NR_waitid, sys_waitid)
+
+/* kernel/fork.c */
+#define __NR_set_tid_address 96
+__SYSCALL(__NR_set_tid_address, sys_set_tid_address)
+#define __NR_unshare 97
+__SYSCALL(__NR_unshare, sys_unshare)
+
+/* kernel/futex.c */
+#define __NR_futex 98
+__SYSCALL(__NR_futex, sys_futex)
+#define __NR_set_robust_list 99
+__SYSCALL(__NR_set_robust_list, sys_set_robust_list)
+#define __NR_get_robust_list 100
+__SYSCALL(__NR_get_robust_list, sys_get_robust_list)
+
+/* kernel/hrtimer.c */
+#define __NR_nanosleep 101
+__SYSCALL(__NR_nanosleep, sys_nanosleep)
+
+/* kernel/itimer.c */
+#define __NR_getitimer 102
+__SYSCALL(__NR_getitimer, sys_getitimer)
+#define __NR_setitimer 103
+__SYSCALL(__NR_setitimer, sys_setitimer)
+
+/* kernel/kexec.c */
+#define __NR_kexec_load 104
+__SYSCALL(__NR_kexec_load, sys_kexec_load)
+
+/* kernel/module.c */
+#define __NR_init_module 105
+__SYSCALL(__NR_init_module, sys_init_module)
+#define __NR_delete_module 106
+__SYSCALL(__NR_delete_module, sys_delete_module)
+
+/* kernel/posix-timers.c */
+#define __NR_timer_create 107
+__SYSCALL(__NR_timer_create, sys_timer_create)
+#define __NR_timer_gettime 108
+__SYSCALL(__NR_timer_gettime, sys_timer_gettime)
+#define __NR_timer_getoverrun 109
+__SYSCALL(__NR_timer_getoverrun, sys_timer_getoverrun)
+#define __NR_timer_settime 110
+__SYSCALL(__NR_timer_settime, sys_timer_settime)
+#define __NR_timer_delete 111
+__SYSCALL(__NR_timer_delete, sys_timer_delete)
+#define __NR_clock_settime 112
+__SYSCALL(__NR_clock_settime, sys_clock_settime)
+#define __NR_clock_gettime 113
+__SYSCALL(__NR_clock_gettime, sys_clock_gettime)
+#define __NR_clock_getres 114
+__SYSCALL(__NR_clock_getres, sys_clock_getres)
+#define __NR_clock_nanosleep 115
+__SYSCALL(__NR_clock_nanosleep, sys_clock_nanosleep)
+
+/* kernel/printk.c */
+#define __NR_syslog 116
+__SYSCALL(__NR_syslog, sys_syslog)
+
+/* kernel/ptrace.c */
+#define __NR_ptrace 117
+__SYSCALL(__NR_ptrace, sys_ptrace)
+
+/* kernel/sched.c */
+#define __NR_sched_setparam 118
+__SYSCALL(__NR_sched_setparam, sys_sched_setparam)
+#define __NR_sched_setscheduler 119
+__SYSCALL(__NR_sched_setscheduler, sys_sched_setscheduler)
+#define __NR_sched_getscheduler 120
+__SYSCALL(__NR_sched_getscheduler, sys_sched_getscheduler)
+#define __NR_sched_getparam 121
+__SYSCALL(__NR_sched_getparam, sys_sched_getparam)
+#define __NR_sched_setaffinity 122
+__SYSCALL(__NR_sched_setaffinity, sys_sched_setaffinity)
+#define __NR_sched_getaffinity 123
+__SYSCALL(__NR_sched_getaffinity, sys_sched_getaffinity)
+#define __NR_sched_yield 124
+__SYSCALL(__NR_sched_yield, sys_sched_yield)
+#define __NR_sched_get_priority_max 125
+__SYSCALL(__NR_sched_get_priority_max, sys_sched_get_priority_max)
+#define __NR_sched_get_priority_min 126
+__SYSCALL(__NR_sched_get_priority_min, sys_sched_get_priority_min)
+#define __NR_sched_rr_get_interval 127
+__SYSCALL(__NR_sched_rr_get_interval, sys_sched_rr_get_interval)
+
+/* kernel/signal.c */
+#define __NR_restart_syscall 128
+__SYSCALL(__NR_restart_syscall, sys_restart_syscall)
+#define __NR_kill 129
+__SYSCALL(__NR_kill, sys_kill)
+#define __NR_tkill 130
+__SYSCALL(__NR_tkill, sys_tkill)
+#define __NR_tgkill 131
+__SYSCALL(__NR_tgkill, sys_tgkill)
+#define __NR_sigaltstack 132
+__SYSCALL(__NR_sigaltstack, sys_sigaltstack)
+#define __NR_rt_sigsuspend 133
+__SYSCALL(__NR_rt_sigsuspend, sys_rt_sigsuspend) /* __ARCH_WANT_SYS_RT_SIGSUSPEND */
+#define __NR_rt_sigaction 134
+__SYSCALL(__NR_rt_sigaction, sys_rt_sigaction) /* __ARCH_WANT_SYS_RT_SIGACTION */
+#define __NR_rt_sigprocmask 135
+__SYSCALL(__NR_rt_sigprocmask, sys_rt_sigprocmask)
+#define __NR_rt_sigpending 136
+__SYSCALL(__NR_rt_sigpending, sys_rt_sigpending)
+#define __NR_rt_sigtimedwait 137
+__SYSCALL(__NR_rt_sigtimedwait, sys_rt_sigtimedwait)
+#define __NR_rt_sigqueueinfo 138
+__SYSCALL(__NR_rt_sigqueueinfo, sys_rt_sigqueueinfo)
+#define __NR_rt_sigreturn 139
+__SYSCALL(__NR_rt_sigreturn, sys_rt_sigreturn) /* sys_rt_sigreturn_wrapper, */
+
+/* kernel/sys.c */
+#define __NR_setpriority 140
+__SYSCALL(__NR_setpriority, sys_setpriority)
+#define __NR_getpriority 141
+__SYSCALL(__NR_getpriority, sys_getpriority)
+#define __NR_reboot 142
+__SYSCALL(__NR_reboot, sys_reboot)
+#define __NR_setregid 143
+__SYSCALL(__NR_setregid, sys_setregid)
+#define __NR_setgid 144
+__SYSCALL(__NR_setgid, sys_setgid)
+#define __NR_setreuid 145
+__SYSCALL(__NR_setreuid, sys_setreuid)
+#define __NR_setuid 146
+__SYSCALL(__NR_setuid, sys_setuid)
+#define __NR_setresuid 147
+__SYSCALL(__NR_setresuid, sys_setresuid)
+#define __NR_getresuid 148
+__SYSCALL(__NR_getresuid, sys_getresuid)
+#define __NR_setresgid 149
+__SYSCALL(__NR_setresgid, sys_setresgid)
+#define __NR_getresgid 150
+__SYSCALL(__NR_getresgid, sys_getresgid)
+#define __NR_setfsuid 151
+__SYSCALL(__NR_setfsuid, sys_setfsuid)
+#define __NR_setfsgid 152
+__SYSCALL(__NR_setfsgid, sys_setfsgid)
+#define __NR_times 153
+__SYSCALL(__NR_times, sys_times)
+#define __NR_setpgid 154
+__SYSCALL(__NR_setpgid, sys_setpgid)
+#define __NR_getpgid 155
+__SYSCALL(__NR_getpgid, sys_getpgid)
+#define __NR_getsid 156
+__SYSCALL(__NR_getsid, sys_getsid)
+#define __NR_setsid 157
+__SYSCALL(__NR_setsid, sys_setsid)
+#define __NR_getgroups 158
+__SYSCALL(__NR_getgroups, sys_getgroups)
+#define __NR_setgroups 159
+__SYSCALL(__NR_setgroups, sys_setgroups)
+#define __NR_uname 160
+__SYSCALL(__NR_uname, sys_newuname)
+#define __NR_sethostname 161
+__SYSCALL(__NR_sethostname, sys_sethostname)
+#define __NR_setdomainname 162
+__SYSCALL(__NR_setdomainname, sys_setdomainname)
+#define __NR_getrlimit 163
+__SYSCALL(__NR_getrlimit, sys_getrlimit)
+#define __NR_setrlimit 164
+__SYSCALL(__NR_setrlimit, sys_setrlimit)
+#define __NR_getrusage 165
+__SYSCALL(__NR_getrusage, sys_getrusage)
+#define __NR_umask 166
+__SYSCALL(__NR_umask, sys_umask)
+#define __NR_prctl 167
+__SYSCALL(__NR_prctl, sys_prctl)
+#define __NR_getcpu 168
+__SYSCALL(__NR_getcpu, sys_getcpu)
+
+/* kernel/time.c */
+#define __NR_gettimeofday 169
+__SYSCALL(__NR_gettimeofday, sys_gettimeofday)
+#define __NR_settimeofday 170
+__SYSCALL(__NR_settimeofday, sys_settimeofday)
+#define __NR_adjtimex 171
+__SYSCALL(__NR_adjtimex, sys_adjtimex)
+
+/* kernel/timer.c */
+#define __NR_getpid 172
+__SYSCALL(__NR_getpid, sys_getpid)
+#define __NR_getppid 173
+__SYSCALL(__NR_getppid, sys_getppid)
+#define __NR_getuid 174
+__SYSCALL(__NR_getuid, sys_getuid)
+#define __NR_geteuid 175
+__SYSCALL(__NR_geteuid, sys_geteuid)
+#define __NR_getgid 176
+__SYSCALL(__NR_getgid, sys_getgid)
+#define __NR_getegid 177
+__SYSCALL(__NR_getegid, sys_getegid)
+#define __NR_gettid 178
+__SYSCALL(__NR_gettid, sys_gettid)
+#define __NR_sysinfo 179
+__SYSCALL(__NR_sysinfo, sys_sysinfo)
+
+/* ipc/mqueue.c */
+#define __NR_mq_open 180
+__SYSCALL(__NR_mq_open, sys_mq_open)
+#define __NR_mq_unlink 181
+__SYSCALL(__NR_mq_unlink, sys_mq_unlink)
+#define __NR_mq_timedsend 182
+__SYSCALL(__NR_mq_timedsend, sys_mq_timedsend)
+#define __NR_mq_timedreceive 183
+__SYSCALL(__NR_mq_timedreceive, sys_mq_timedreceive)
+#define __NR_mq_notify 184
+__SYSCALL(__NR_mq_notify, sys_mq_notify)
+#define __NR_mq_getsetattr 185
+__SYSCALL(__NR_mq_getsetattr, sys_mq_getsetattr)
+
+/* ipc/msg.c */
+#define __NR_msgget 186
+__SYSCALL(__NR_msgget, sys_msgget)
+#define __NR_msgctl 187
+__SYSCALL(__NR_msgctl, sys_msgctl)
+#define __NR_msgrcv 188
+__SYSCALL(__NR_msgrcv, sys_msgrcv)
+#define __NR_msgsnd 189
+__SYSCALL(__NR_msgsnd, sys_msgsnd)
+
+/* ipc/sem.c */
+#define __NR_semget 190
+__SYSCALL(__NR_semget, sys_semget)
+#define __NR_semctl 191
+__SYSCALL(__NR_semctl, sys_semctl)
+#define __NR_semtimedop 192
+__SYSCALL(__NR_semtimedop, sys_semtimedop)
+#define __NR_semop 193
+__SYSCALL(__NR_semop, sys_semop)
+
+/* ipc/shm.c */
+#define __NR_shmget 194
+__SYSCALL(__NR_shmget, sys_shmget)
+#define __NR_shmctl 195
+__SYSCALL(__NR_shmctl, sys_shmctl)
+#define __NR_shmat 196
+__SYSCALL(__NR_shmat, sys_shmat)
+#define __NR_shmdt 197
+__SYSCALL(__NR_shmdt, sys_shmdt)
+
+/* net/socket.c */
+#define __NR_socket 198
+__SYSCALL(__NR_socket, sys_socket)
+#define __NR_socketpair 199
+__SYSCALL(__NR_socketpair, sys_socketpair)
+#define __NR_bind 200
+__SYSCALL(__NR_bind, sys_bind)
+#define __NR_listen 201
+__SYSCALL(__NR_listen, sys_listen)
+#define __NR_accept 202
+__SYSCALL(__NR_accept, sys_accept)
+#define __NR_connect 203
+__SYSCALL(__NR_connect, sys_connect)
+#define __NR_getsockname 204
+__SYSCALL(__NR_getsockname, sys_getsockname)
+#define __NR_getpeername 205
+__SYSCALL(__NR_getpeername, sys_getpeername)
+#define __NR_sendto 206
+__SYSCALL(__NR_sendto, sys_sendto)
+#define __NR_recvfrom 207
+__SYSCALL(__NR_recvfrom, sys_recvfrom)
+#define __NR_setsockopt 208
+__SYSCALL(__NR_setsockopt, sys_setsockopt)
+#define __NR_getsockopt 209
+__SYSCALL(__NR_getsockopt, sys_getsockopt)
+#define __NR_shutdown 210
+__SYSCALL(__NR_shutdown, sys_shutdown)
+#define __NR_sendmsg 211
+__SYSCALL(__NR_sendmsg, sys_sendmsg)
+#define __NR_recvmsg 212
+__SYSCALL(__NR_recvmsg, sys_recvmsg)
+
+/* mm/filemap.c */
+#define __NR_readahead 213
+__SYSCALL(__NR_readahead, sys_readahead)
+
+/* mm/nommu.c, also with MMU */
+#define __NR_brk 214
+__SYSCALL(__NR_brk, sys_brk)
+#define __NR_munmap 215
+__SYSCALL(__NR_munmap, sys_munmap)
+#define __NR_mremap 216
+__SYSCALL(__NR_mremap, sys_mremap)
+
+/* security/keys/keyctl.c */
+#define __NR_add_key 217
+__SYSCALL(__NR_add_key, sys_add_key)
+#define __NR_request_key 218
+__SYSCALL(__NR_request_key, sys_request_key)
+#define __NR_keyctl 219
+__SYSCALL(__NR_keyctl, sys_keyctl)
+
+/* arch/example/kernel/sys_example.c */
+#define __NR_clone 220
+__SYSCALL(__NR_clone, sys_clone)       /* .long sys_clone_wrapper */
+#define __NR_execve 221
+__SYSCALL(__NR_execve, sys_execve)     /* .long sys_execve_wrapper */
+
+#define __NR3264_mmap 222
+__SC_3264(__NR3264_mmap, sys_mmap2, sys_mmap)
+/* mm/fadvise.c */
+#define __NR3264_fadvise64 223
+__SC_3264(__NR3264_fadvise64, sys_fadvise64_64, sys_fadvise64)
+
+/* mm/, CONFIG_MMU only */
+#ifndef __ARCH_NOMMU
+#define __NR_swapon 224
+__SYSCALL(__NR_swapon, sys_swapon)
+#define __NR_swapoff 225
+__SYSCALL(__NR_swapoff, sys_swapoff)
+#define __NR_mprotect 226
+__SYSCALL(__NR_mprotect, sys_mprotect)
+#define __NR_msync 227
+__SYSCALL(__NR_msync, sys_msync)
+#define __NR_mlock 228
+__SYSCALL(__NR_mlock, sys_mlock)
+#define __NR_munlock 229
+__SYSCALL(__NR_munlock, sys_munlock)
+#define __NR_mlockall 230
+__SYSCALL(__NR_mlockall, sys_mlockall)
+#define __NR_munlockall 231
+__SYSCALL(__NR_munlockall, sys_munlockall)
+#define __NR_mincore 232
+__SYSCALL(__NR_mincore, sys_mincore)
+#define __NR_madvise 233
+__SYSCALL(__NR_madvise, sys_madvise)
+#define __NR_remap_file_pages 234
+__SYSCALL(__NR_remap_file_pages, sys_remap_file_pages)
+#define __NR_mbind 235
+__SYSCALL(__NR_mbind, sys_mbind)
+#define __NR_get_mempolicy 236
+__SYSCALL(__NR_get_mempolicy, sys_get_mempolicy)
+#define __NR_set_mempolicy 237
+__SYSCALL(__NR_set_mempolicy, sys_set_mempolicy)
+#define __NR_migrate_pages 238
+__SYSCALL(__NR_migrate_pages, sys_migrate_pages)
+#define __NR_move_pages 239
+__SYSCALL(__NR_move_pages, sys_move_pages)
+#endif
+
+#undef __NR_syscalls
+#define __NR_syscalls 240
+
+/*
+ * All syscalls below here should go away really,
+ * these are provided for both review and as a porting
+ * help for the C library version.
+*
+ * Last chance: are any of these important enought to
+ * enable by default?
+ */
+#ifdef __ARCH_WANT_SYSCALL_NO_AT
+#define __NR_open 1024
+__SYSCALL(__NR_open, sys_open)
+#define __NR_link 1025
+__SYSCALL(__NR_link, sys_link)
+#define __NR_unlink 1026
+__SYSCALL(__NR_unlink, sys_unlink)
+#define __NR_mknod 1027
+__SYSCALL(__NR_mknod, sys_mknod)
+#define __NR_chmod 1028
+__SYSCALL(__NR_chmod, sys_chmod)
+#define __NR_chown 1029
+__SYSCALL(__NR_chown, sys_chown)
+#define __NR_mkdir 1030
+__SYSCALL(__NR_mkdir, sys_mkdir)
+#define __NR_rmdir 1031
+__SYSCALL(__NR_rmdir, sys_rmdir)
+#define __NR_lchown 1032
+__SYSCALL(__NR_lchown, sys_lchown)
+#define __NR_access 1033
+__SYSCALL(__NR_access, sys_access)
+#define __NR_rename 1034
+__SYSCALL(__NR_rename, sys_rename)
+#define __NR_readlink 1035
+__SYSCALL(__NR_readlink, sys_readlink)
+#define __NR_symlink 1036
+__SYSCALL(__NR_symlink, sys_symlink)
+#define __NR_utimes 1037
+__SYSCALL(__NR_utimes, sys_utimes)
+#define __NR3264_stat 1038
+__SC_3264(__NR3264_stat, sys_stat64, sys_newstat)
+#define __NR3264_lstat 1039
+__SC_3264(__NR3264_lstat, sys_lstat64, sys_newlstat)
+
+#undef __NR_syscalls
+#define __NR_syscalls (__NR3264_lstat+1)
+#endif /* __ARCH_WANT_SYSCALL_NO_AT */
+
+#ifdef __ARCH_WANT_SYSCALL_NO_FLAGS
+#define __NR_pipe 1040
+__SYSCALL(__NR_pipe, sys_pipe)
+#define __NR_dup2 1041
+__SYSCALL(__NR_dup2, sys_dup2)
+#define __NR_epoll_create 1042
+__SYSCALL(__NR_epoll_create, sys_epoll_create)
+#define __NR_inotify_init 1043
+__SYSCALL(__NR_inotify_init, sys_inotify_init)
+#define __NR_eventfd 1044
+__SYSCALL(__NR_eventfd, sys_eventfd)
+#define __NR_signalfd 1045
+__SYSCALL(__NR_signalfd, sys_signalfd)
+
+#undef __NR_syscalls
+#define __NR_syscalls (__NR_signalfd+1)
+#endif /* __ARCH_WANT_SYSCALL_NO_FLAGS */
+
+#if __BITS_PER_LONG == 32 && defined(__ARCH_WANT_SYSCALL_OFF_T)
+#define __NR_sendfile 1046
+__SYSCALL(__NR_sendfile, sys_sendfile)
+#define __NR_ftruncate 1047
+__SYSCALL(__NR_ftruncate, sys_ftruncate)
+#define __NR_truncate 1048
+__SYSCALL(__NR_truncate, sys_truncate)
+#define __NR_stat 1049
+__SYSCALL(__NR_stat, sys_newstat)
+#define __NR_lstat 1050
+__SYSCALL(__NR_lstat, sys_newlstat)
+#define __NR_fstat 1051
+__SYSCALL(__NR_fstat, sys_newfstat)
+#define __NR_fcntl 1052
+__SYSCALL(__NR_fcntl, sys_fcntl)
+#define __NR_fadvise64 1053
+#define __ARCH_WANT_SYS_FADVISE64
+__SYSCALL(__NR_fadvise64, sys_fadvise64)
+#define __NR_newfstatat 1054
+#define __ARCH_WANT_SYS_NEWFSTATAT
+__SYSCALL(__NR_newfstatat, sys_newfstatat)
+#define __NR_fstatfs 1055
+__SYSCALL(__NR_fstatfs, sys_fstatfs)
+#define __NR_statfs 1056
+__SYSCALL(__NR_statfs, sys_statfs)
+#define __NR_lseek 1057
+__SYSCALL(__NR_lseek, sys_lseek)
+#define __NR_mmap 1058
+__SYSCALL(__NR_mmap, sys_mmap)
+
+#undef __NR_syscalls
+#define __NR_syscalls (__NR_mmap+1)
+#endif /* 32 bit off_t syscalls */
+
+#ifdef __ARCH_WANT_SYSCALL_DEPRECATED
+#define __NR_alarm 1059
+#define __ARCH_WANT_SYS_ALARM
+__SYSCALL(__NR_alarm, sys_alarm)
+#define __NR_getpgrp 1060
+#define __ARCH_WANT_SYS_GETPGRP
+__SYSCALL(__NR_getpgrp, sys_getpgrp)
+#define __NR_pause 1061
+#define __ARCH_WANT_SYS_PAUSE
+__SYSCALL(__NR_pause, sys_pause)
+#define __NR_time 1062
+#define __ARCH_WANT_SYS_TIME
+__SYSCALL(__NR_time, sys_time)
+#define __NR_utime 1063
+#define __ARCH_WANT_SYS_UTIME
+__SYSCALL(__NR_utime, sys_utime)
+
+#define __NR_creat 1064
+__SYSCALL(__NR_creat, sys_creat)
+#define __NR_getdents 1065
+#define __ARCH_WANT_SYS_GETDENTS
+__SYSCALL(__NR_getdents, sys_getdents)
+#define __NR_futimesat 1066
+__SYSCALL(__NR_futimesat, sys_futimesat)
+#define __NR_select 1067
+#define __ARCH_WANT_SYS_SELECT
+__SYSCALL(__NR_select, sys_select)
+#define __NR_poll 1068
+__SYSCALL(__NR_poll, sys_poll)
+#define __NR_epoll_wait 1069
+__SYSCALL(__NR_epoll_wait, sys_epoll_wait)
+#define __NR_ustat 1070
+__SYSCALL(__NR_ustat, sys_ustat)
+#define __NR_vfork 1071
+__SYSCALL(__NR_vfork, sys_vfork)
+#define __NR_wait4 1072
+__SYSCALL(__NR_wait4, sys_wait4)
+#define __NR_recv 1073
+__SYSCALL(__NR_recv, sys_recv)
+#define __NR_send 1074
+__SYSCALL(__NR_send, sys_send)
+#define __NR_bdflush 1075
+__SYSCALL(__NR_bdflush, sys_bdflush)
+#define __NR_umount 1076
+__SYSCALL(__NR_umount, sys_oldumount)
+#define __ARCH_WANT_SYS_OLDUMOUNT
+#define __NR_uselib 1077
+__SYSCALL(__NR_uselib, sys_uselib)
+#define __NR__sysctl 1078
+__SYSCALL(__NR__sysctl, sys_sysctl)
+
+#define __NR_fork 1079
+#ifdef CONFIG_MMU
+__SYSCALL(__NR_fork, sys_fork)
+#else
+__SYSCALL(__NR_fork, sys_ni_syscall)
+#endif /* CONFIG_MMU */
+
+#undef __NR_syscalls
+#define __NR_syscalls (__NR_fork+1)
+
+#endif /* __ARCH_WANT_SYSCALL_DEPRECATED */
+
+/*
+ * 32 bit systems traditionally used different
+ * syscalls for off_t and loff_t arguments, while
+ * 64 bit systems only need the off_t version.
+ * For new 32 bit platforms, there is no need to
+ * implement the old 32 bit off_t syscalls, so
+ * they take different names.
+ * Here we map the numbers so that both versions
+ * use the same syscall table layout.
+ */
+#if __BITS_PER_LONG == 64
+#define __NR_fcntl __NR3264_fcntl
+#define __NR_statfs __NR3264_statfs
+#define __NR_fstatfs __NR3264_fstatfs
+#define __NR_truncate __NR3264_truncate
+#define __NR_ftruncate __NR3264_truncate
+#define __NR_lseek __NR3264_lseek
+#define __NR_sendfile __NR3264_sendfile
+#define __NR_newfstatat __NR3264_fstatat
+#define __NR_fstat __NR3264_fstat
+#define __NR_mmap __NR3264_mmap
+#define __NR_fadvise64 __NR3264_fadvise64
+#ifdef __NR3264_stat
+#define __NR_stat __NR3264_stat
+#define __NR_lstat __NR3264_lstat
+#endif
+#else
+#define __NR_fcntl64 __NR3264_fcntl
+#define __NR_statfs64 __NR3264_statfs
+#define __NR_fstatfs64 __NR3264_fstatfs
+#define __NR_truncate64 __NR3264_truncate
+#define __NR_ftruncate64 __NR3264_truncate
+#define __NR_llseek __NR3264_lseek
+#define __NR_sendfile64 __NR3264_sendfile
+#define __NR_fstatat64 __NR3264_fstatat
+#define __NR_fstat64 __NR3264_fstat
+#define __NR_mmap2 __NR3264_mmap
+#define __NR_fadvise64_64 __NR3264_fadvise64
+#ifdef __NR3264_stat
+#define __NR_stat64 __NR3264_stat
+#define __NR_lstat64 __NR3264_lstat
+#endif
+#endif
+
+#ifdef __KERNEL__
+
+/*
+ * These are required system calls, we should
+ * invert the logic eventually and let them
+ * be selected by default.
+ */
+#if __BITS_PER_LONG == 32
+#define __ARCH_WANT_STAT64
+#define __ARCH_WANT_SYS_LLSEEK
+#endif
+#define __ARCH_WANT_SYS_RT_SIGACTION
+#define __ARCH_WANT_SYS_RT_SIGSUSPEND
+
+/*
+ * "Conditional" syscalls
+ *
+ * What we want is __attribute__((weak,alias("sys_ni_syscall"))),
+ * but it doesn't work on all toolchains, so we just do it by hand
+ */
+#ifndef cond_syscall
+#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall")
+#endif
+
+#endif /* __KERNEL__ */
+#endif /* _ASM_GENERIC_UNISTD_H */
diff --git a/include/asm-generic/user.h b/include/asm-generic/user.h
new file mode 100644 (file)
index 0000000..8b9c3c9
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __ASM_GENERIC_USER_H
+#define __ASM_GENERIC_USER_H
+/*
+ * This file may define a 'struct user' structure. However, it it only
+ * used for a.out file, which are not supported on new architectures.
+ */
+
+#endif /* __ASM_GENERIC_USER_H */
diff --git a/include/asm-generic/vga.h b/include/asm-generic/vga.h
new file mode 100644 (file)
index 0000000..36c8ff5
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ *     Access to VGA videoram
+ *
+ *     (c) 1998 Martin Mares <mj@ucw.cz>
+ */
+#ifndef __ASM_GENERIC_VGA_H
+#define __ASM_GENERIC_VGA_H
+
+/*
+ *     On most architectures that support VGA, we can just
+ *     recalculate addresses and then access the videoram
+ *     directly without any black magic.
+ *
+ *     Everyone else needs to ioremap the address and use
+ *     proper I/O accesses.
+ */
+#ifndef VGA_MAP_MEM
+#define VGA_MAP_MEM(x, s) (unsigned long)phys_to_virt(x)
+#endif
+
+#define vga_readb(x) (*(x))
+#define vga_writeb(x, y) (*(y) = (x))
+
+#endif /* _ASM_GENERIC_VGA_H */
index b84d8ae35e6f4a8fd9f5cb6d4df08ba8d274d3e6..d4ddc22e46bb5ba91ecd1368315e894461154eab 100644 (file)
@@ -86,7 +86,17 @@ struct drm_device;
 
 #include "drm_os_linux.h"
 #include "drm_hashtab.h"
+#include "drm_mm.h"
 
+#define DRM_UT_CORE            0x01
+#define DRM_UT_DRIVER          0x02
+#define DRM_UT_KMS             0x04
+#define DRM_UT_MODE            0x08
+
+extern void drm_ut_debug_printk(unsigned int request_level,
+                               const char *prefix,
+                               const char *function_name,
+                               const char *format, ...);
 /***********************************************************************/
 /** \name DRM template customization defaults */
 /*@{*/
@@ -186,15 +196,57 @@ struct drm_device;
  * \param arg arguments
  */
 #if DRM_DEBUG_CODE
-#define DRM_DEBUG(fmt, arg...)                                         \
+#define DRM_DEBUG(fmt, args...)                                                \
+       do {                                                            \
+               drm_ut_debug_printk(DRM_UT_CORE, DRM_NAME,              \
+                                       __func__, fmt, ##args);         \
+       } while (0)
+
+#define DRM_DEBUG_DRIVER(prefix, fmt, args...)                         \
+       do {                                                            \
+               drm_ut_debug_printk(DRM_UT_DRIVER, prefix,              \
+                                       __func__, fmt, ##args);         \
+       } while (0)
+#define DRM_DEBUG_KMS(prefix, fmt, args...)                            \
+       do {                                                            \
+               drm_ut_debug_printk(DRM_UT_KMS, prefix,                 \
+                                        __func__, fmt, ##args);        \
+       } while (0)
+#define DRM_DEBUG_MODE(prefix, fmt, args...)                           \
+       do {                                                            \
+               drm_ut_debug_printk(DRM_UT_MODE, prefix,                \
+                                        __func__, fmt, ##args);        \
+       } while (0)
+#define DRM_LOG(fmt, args...)                                          \
+       do {                                                            \
+               drm_ut_debug_printk(DRM_UT_CORE, NULL,                  \
+                                       NULL, fmt, ##args);             \
+       } while (0)
+#define DRM_LOG_KMS(fmt, args...)                                      \
+       do {                                                            \
+               drm_ut_debug_printk(DRM_UT_KMS, NULL,                   \
+                                       NULL, fmt, ##args);             \
+       } while (0)
+#define DRM_LOG_MODE(fmt, args...)                                     \
+       do {                                                            \
+               drm_ut_debug_printk(DRM_UT_MODE, NULL,                  \
+                                       NULL, fmt, ##args);             \
+       } while (0)
+#define DRM_LOG_DRIVER(fmt, args...)                                   \
        do {                                                            \
-               if ( drm_debug )                        \
-                       printk(KERN_DEBUG                               \
-                              "[" DRM_NAME ":%s] " fmt ,       \
-                              __func__ , ##arg);                       \
+               drm_ut_debug_printk(DRM_UT_DRIVER, NULL,                \
+                                       NULL, fmt, ##args);             \
        } while (0)
 #else
+#define DRM_DEBUG_DRIVER(prefix, fmt, args...) do { } while (0)
+#define DRM_DEBUG_KMS(prefix, fmt, args...)    do { } while (0)
+#define DRM_DEBUG_MODE(prefix, fmt, args...)   do { } while (0)
 #define DRM_DEBUG(fmt, arg...)          do { } while (0)
+#define DRM_LOG(fmt, arg...)           do { } while (0)
+#define DRM_LOG_KMS(fmt, args...) do { } while (0)
+#define DRM_LOG_MODE(fmt, arg...) do { } while (0)
+#define DRM_LOG_DRIVER(fmt, arg...) do { } while (0)
+
 #endif
 
 #define DRM_PROC_LIMIT (PAGE_SIZE-80)
@@ -237,15 +289,15 @@ struct drm_device;
  * \param dev DRM device.
  * \param filp file pointer of the caller.
  */
-#define LOCK_TEST_WITH_RETURN( dev, file_priv )                                \
-do {                                                                   \
-       if (!_DRM_LOCK_IS_HELD(file_priv->master->lock.hw_lock->lock) ||                \
-           file_priv->master->lock.file_priv != file_priv)     {                       \
+#define LOCK_TEST_WITH_RETURN( dev, _file_priv )                               \
+do {                                                                           \
+       if (!_DRM_LOCK_IS_HELD(_file_priv->master->lock.hw_lock->lock) ||       \
+           _file_priv->master->lock.file_priv != _file_priv)   {               \
                DRM_ERROR( "%s called without lock held, held  %d owner %p %p\n",\
-                          __func__, _DRM_LOCK_IS_HELD(file_priv->master->lock.hw_lock->lock),\
-                          file_priv->master->lock.file_priv, file_priv);               \
-               return -EINVAL;                                         \
-       }                                                               \
+                          __func__, _DRM_LOCK_IS_HELD(_file_priv->master->lock.hw_lock->lock),\
+                          _file_priv->master->lock.file_priv, _file_priv);     \
+               return -EINVAL;                                                 \
+       }                                                                       \
 } while (0)
 
 /**
@@ -502,26 +554,6 @@ struct drm_sigdata {
 };
 
 
-/*
- * Generic memory manager structs
- */
-
-struct drm_mm_node {
-       struct list_head fl_entry;
-       struct list_head ml_entry;
-       int free;
-       unsigned long start;
-       unsigned long size;
-       struct drm_mm *mm;
-       void *private;
-};
-
-struct drm_mm {
-       struct list_head fl_entry;
-       struct list_head ml_entry;
-};
-
-
 /**
  * Kernel side of a mapping
  */
@@ -1385,22 +1417,6 @@ extern char *drm_get_connector_status_name(enum drm_connector_status status);
 extern int drm_sysfs_connector_add(struct drm_connector *connector);
 extern void drm_sysfs_connector_remove(struct drm_connector *connector);
 
-/*
- * Basic memory manager support (drm_mm.c)
- */
-extern struct drm_mm_node *drm_mm_get_block(struct drm_mm_node * parent,
-                                      unsigned long size,
-                                      unsigned alignment);
-extern void drm_mm_put_block(struct drm_mm_node * cur);
-extern struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm, unsigned long size,
-                                        unsigned alignment, int best_match);
-extern int drm_mm_init(struct drm_mm *mm, unsigned long start, unsigned long size);
-extern void drm_mm_takedown(struct drm_mm *mm);
-extern int drm_mm_clean(struct drm_mm *mm);
-extern unsigned long drm_mm_tail_space(struct drm_mm *mm);
-extern int drm_mm_remove_space_from_tail(struct drm_mm *mm, unsigned long size);
-extern int drm_mm_add_space_to_tail(struct drm_mm *mm, unsigned long size);
-
 /* Graphics Execution Manager library functions (drm_gem.c) */
 int drm_gem_init(struct drm_device *dev);
 void drm_gem_destroy(struct drm_device *dev);
@@ -1522,18 +1538,14 @@ static __inline__ void *drm_calloc(size_t nmemb, size_t size, int area)
 
 static __inline__ void *drm_calloc_large(size_t nmemb, size_t size)
 {
-       u8 *addr;
-
-       if (size <= PAGE_SIZE)
+       if (size * nmemb <= PAGE_SIZE)
            return kcalloc(nmemb, size, GFP_KERNEL);
 
-       addr = vmalloc(nmemb * size);
-       if (!addr)
+       if (size != 0 && nmemb > ULONG_MAX / size)
                return NULL;
 
-       memset(addr, 0, nmemb * size);
-
-       return addr;
+       return __vmalloc(size * nmemb,
+                        GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO, PAGE_KERNEL);
 }
 
 static __inline void drm_free_large(void *ptr)
index cd2b189e1be6caac74fd2402d1d84d4119fbca22..0af087a4d3b38e7ecaf8aeb05f3b3e1ee2d224f0 100644 (file)
@@ -35,6 +35,8 @@
 #ifndef DRM_HASHTAB_H
 #define DRM_HASHTAB_H
 
+#include <linux/list.h>
+
 #define drm_hash_entry(_ptr, _type, _member) container_of(_ptr, _type, _member)
 
 struct drm_hash_item {
diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h
new file mode 100644 (file)
index 0000000..5662f42
--- /dev/null
@@ -0,0 +1,90 @@
+/**************************************************************************
+ *
+ * Copyright 2006-2008 Tungsten Graphics, Inc., Cedar Park, TX. USA.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *
+ **************************************************************************/
+/*
+ * Authors:
+ * Thomas Hellstrom <thomas-at-tungstengraphics-dot-com>
+ */
+
+#ifndef _DRM_MM_H_
+#define _DRM_MM_H_
+
+/*
+ * Generic range manager structs
+ */
+#include <linux/list.h>
+
+struct drm_mm_node {
+       struct list_head fl_entry;
+       struct list_head ml_entry;
+       int free;
+       unsigned long start;
+       unsigned long size;
+       struct drm_mm *mm;
+       void *private;
+};
+
+struct drm_mm {
+       struct list_head fl_entry;
+       struct list_head ml_entry;
+       struct list_head unused_nodes;
+       int num_unused;
+       spinlock_t unused_lock;
+};
+
+/*
+ * Basic range manager support (drm_mm.c)
+ */
+
+extern struct drm_mm_node *drm_mm_get_block(struct drm_mm_node *parent,
+                                           unsigned long size,
+                                           unsigned alignment);
+extern struct drm_mm_node *drm_mm_get_block_atomic(struct drm_mm_node *parent,
+                                                  unsigned long size,
+                                                  unsigned alignment);
+extern void drm_mm_put_block(struct drm_mm_node *cur);
+extern struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm,
+                                             unsigned long size,
+                                             unsigned alignment,
+                                             int best_match);
+extern int drm_mm_init(struct drm_mm *mm, unsigned long start,
+                      unsigned long size);
+extern void drm_mm_takedown(struct drm_mm *mm);
+extern int drm_mm_clean(struct drm_mm *mm);
+extern unsigned long drm_mm_tail_space(struct drm_mm *mm);
+extern int drm_mm_remove_space_from_tail(struct drm_mm *mm,
+                                        unsigned long size);
+extern int drm_mm_add_space_to_tail(struct drm_mm *mm,
+                                   unsigned long size, int atomic);
+extern int drm_mm_pre_get(struct drm_mm *mm);
+
+static inline struct drm_mm *drm_get_mm(struct drm_mm_node *block)
+{
+       return block->mm;
+}
+
+#endif
index fc55db78019943f30637668258682ccaa6c62f09..f8634ab53b8f98166a05af894d8789e790ee7435 100644 (file)
        {0x1002, 0x940A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x940B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x940F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x94A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x94A1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x94B1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x94B3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x94B5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x9440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x9441, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x9442, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x9456, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x945A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x945B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x9460, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x9462, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x946A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x946B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x947A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
        {0x8086, 0xa001, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, 0xffff00, 0}, \
        {0x8086, 0xa011, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, 0xffff00, 0}, \
        {0x8086, 0x35e8, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, 0xffff00, 0}, \
+       {0x8086, 0x0042, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, 0xffff00, 0}, \
+       {0x8086, 0x0046, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, 0xffff00, 0}, \
        {0, 0, 0}
index ebdfde8fe556166f19a81a59736cdb3b9aacaf6f..0b1a6cae9de1ebbc5010c2464ddc43cf33ae462b 100644 (file)
@@ -1226,6 +1226,8 @@ struct block_device_operations {
        int (*direct_access) (struct block_device *, sector_t,
                                                void **, unsigned long *);
        int (*media_changed) (struct gendisk *);
+       unsigned long long (*set_capacity) (struct gendisk *,
+                                               unsigned long long);
        int (*revalidate_disk) (struct gendisk *);
        int (*getgeo)(struct block_device *, struct hd_geometry *);
        struct module *owner;
index 37bcb50a4d7c85b1ac203982cc71f11e75511cde..04fb5135b4e16eea28e3e847d2ed36c01c78a88a 100644 (file)
@@ -261,6 +261,11 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect);
 # define __section(S) __attribute__ ((__section__(#S)))
 #endif
 
+/* Are two types/vars the same type (ignoring qualifiers)? */
+#ifndef __same_type
+# define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
+#endif
+
 /*
  * Prevent the compiler from merging or refetching accesses.  The compiler
  * is also forbidden from reordering successive instances of ACCESS_ONCE(),
index 5d5c197bad456be01863bd258113a5402a7d595c..a4a7b10aaa48a8b086142876272466a888069ce4 100644 (file)
@@ -62,8 +62,6 @@ struct bus_type {
        void (*shutdown)(struct device *dev);
 
        int (*suspend)(struct device *dev, pm_message_t state);
-       int (*suspend_late)(struct device *dev, pm_message_t state);
-       int (*resume_early)(struct device *dev);
        int (*resume)(struct device *dev);
 
        struct dev_pm_ops *pm;
@@ -291,9 +289,6 @@ struct device_type {
        int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
        void (*release)(struct device *dev);
 
-       int (*suspend)(struct device *dev, pm_message_t state);
-       int (*resume)(struct device *dev);
-
        struct dev_pm_ops *pm;
 };
 
index b9cd38603fd8325bf21c21206ab7fe246d026c6c..0b3518c4235697371df40558366345697e361df9 100644 (file)
@@ -81,8 +81,8 @@ struct dlm_lksb {
  * the cluster, the calling node joins it.
  */
 
-int dlm_new_lockspace(char *name, int namelen, dlm_lockspace_t **lockspace,
-                     uint32_t flags, int lvblen);
+int dlm_new_lockspace(const char *name, int namelen,
+                     dlm_lockspace_t **lockspace, uint32_t flags, int lvblen);
 
 /*
  * dlm_release_lockspace
index 162e5defe68358a250535974d4146412c4ff48b9..d41ed593f79fe01f8cc5ea25b3e6ea000cdc0e4c 100644 (file)
@@ -120,6 +120,13 @@ struct fuse_file_lock {
 #define FUSE_EXPORT_SUPPORT    (1 << 4)
 #define FUSE_BIG_WRITES                (1 << 5)
 
+/**
+ * CUSE INIT request/reply flags
+ *
+ * CUSE_UNRESTRICTED_IOCTL:  use unrestricted ioctl
+ */
+#define CUSE_UNRESTRICTED_IOCTL        (1 << 0)
+
 /**
  * Release flags
  */
@@ -210,6 +217,9 @@ enum fuse_opcode {
        FUSE_DESTROY       = 38,
        FUSE_IOCTL         = 39,
        FUSE_POLL          = 40,
+
+       /* CUSE specific operations */
+       CUSE_INIT          = 4096,
 };
 
 enum fuse_notify_code {
@@ -401,6 +411,27 @@ struct fuse_init_out {
        __u32   max_write;
 };
 
+#define CUSE_INIT_INFO_MAX 4096
+
+struct cuse_init_in {
+       __u32   major;
+       __u32   minor;
+       __u32   unused;
+       __u32   flags;
+};
+
+struct cuse_init_out {
+       __u32   major;
+       __u32   minor;
+       __u32   unused;
+       __u32   flags;
+       __u32   max_read;
+       __u32   max_write;
+       __u32   dev_major;              /* chardev major */
+       __u32   dev_minor;              /* chardev minor */
+       __u32   spare[10];
+};
+
 struct fuse_interrupt_in {
        __u64   unique;
 };
index 149fda264c86c9a404befd350d1a7c2006ab3213..7cbd38d363a2f051af1abbd30d7bbf90c066151e 100644 (file)
@@ -114,6 +114,7 @@ struct hd_struct {
 #define GENHD_FL_UP                            16
 #define GENHD_FL_SUPPRESS_PARTITION_INFO       32
 #define GENHD_FL_EXT_DEVT                      64 /* allow extended devt */
+#define GENHD_FL_NATIVE_CAPACITY               128
 
 #define BLK_SCSI_MAX_CMDS      (256)
 #define BLK_SCSI_CMD_PER_LONG  (BLK_SCSI_MAX_CMDS / (sizeof(long) * 8))
index 0bbc15f5453642e2c5057028322b29738cbf8a47..3760e7c5de0264aaec173486daca51035e80b252 100644 (file)
@@ -85,6 +85,9 @@ struct vm_area_struct;
                        __GFP_NOWARN|__GFP_REPEAT|__GFP_NOFAIL|\
                        __GFP_NORETRY|__GFP_NOMEMALLOC)
 
+/* Control slab gfp mask during early boot */
+#define SLAB_GFP_BOOT_MASK __GFP_BITS_MASK & ~(__GFP_WAIT|__GFP_IO|__GFP_FS)
+
 /* Control allocation constraints */
 #define GFP_CONSTRAINT_MASK (__GFP_HARDWALL|__GFP_THISNODE)
 
index 867cb68d84619a8631670f839bf908a2db99b56b..a6c6a2fad7c88b107c6e7cfcb5a22fc3a46e7b26 100644 (file)
@@ -178,7 +178,7 @@ typedef u8 hwif_chipset_t;
 /*
  * Structure to hold all information about the location of this port
  */
-typedef struct hw_regs_s {
+struct ide_hw {
        union {
                struct ide_io_ports     io_ports;
                unsigned long           io_ports_array[IDE_NR_PORTS];
@@ -186,12 +186,11 @@ typedef struct hw_regs_s {
 
        int             irq;                    /* our irq number */
        ide_ack_intr_t  *ack_intr;              /* acknowledge interrupt */
-       hwif_chipset_t  chipset;
        struct device   *dev, *parent;
        unsigned long   config;
-} hw_regs_t;
+};
 
-static inline void ide_std_init_ports(hw_regs_t *hw,
+static inline void ide_std_init_ports(struct ide_hw *hw,
                                      unsigned long io_addr,
                                      unsigned long ctl_addr)
 {
@@ -218,21 +217,12 @@ static inline void ide_std_init_ports(hw_regs_t *hw,
 
 /*
  * Special Driver Flags
- *
- * set_geometry        : respecify drive geometry
- * recalibrate : seek to cyl 0
- * set_multmode        : set multmode count
- * reserved    : unused
  */
-typedef union {
-       unsigned all                    : 8;
-       struct {
-               unsigned set_geometry   : 1;
-               unsigned recalibrate    : 1;
-               unsigned set_multmode   : 1;
-               unsigned reserved       : 5;
-       } b;
-} special_t;
+enum {
+       IDE_SFLAG_SET_GEOMETRY          = (1 << 0),
+       IDE_SFLAG_RECALIBRATE           = (1 << 1),
+       IDE_SFLAG_SET_MULTMODE          = (1 << 2),
+};
 
 /*
  * Status returned from various ide_ functions
@@ -391,6 +381,7 @@ struct ide_drive_s;
 struct ide_disk_ops {
        int             (*check)(struct ide_drive_s *, const char *);
        int             (*get_capacity)(struct ide_drive_s *);
+       u64             (*set_capacity)(struct ide_drive_s *, u64);
        void            (*setup)(struct ide_drive_s *);
        void            (*flush)(struct ide_drive_s *);
        int             (*init_media)(struct ide_drive_s *, struct gendisk *);
@@ -468,6 +459,8 @@ enum {
        IDE_DFLAG_NICE1                 = (1 << 5),
        /* device is physically present */
        IDE_DFLAG_PRESENT               = (1 << 6),
+       /* disable Host Protected Area */
+       IDE_DFLAG_NOHPA                 = (1 << 7),
        /* id read from device (synthetic if not set) */
        IDE_DFLAG_ID_READ               = (1 << 8),
        IDE_DFLAG_NOPROBE               = (1 << 9),
@@ -506,6 +499,7 @@ enum {
        /* write protect */
        IDE_DFLAG_WP                    = (1 << 29),
        IDE_DFLAG_FORMAT_IN_PROGRESS    = (1 << 30),
+       IDE_DFLAG_NIEN_QUIRK            = (1 << 31),
 };
 
 struct ide_drive_s {
@@ -530,14 +524,13 @@ struct ide_drive_s {
        unsigned long sleep;            /* sleep until this time */
        unsigned long timeout;          /* max time to wait for irq */
 
-       special_t       special;        /* special action flags */
+       u8      special_flags;          /* special action flags */
 
        u8      select;                 /* basic drive/head select reg value */
        u8      retry_pio;              /* retrying dma capable host in pio */
        u8      waiting_for_dma;        /* dma currently in progress */
        u8      dma;                    /* atapi dma flag */
 
-        u8     quirk_list;     /* considered quirky, set for a specific host */
         u8     init_speed;     /* transfer rate set at boot */
         u8     current_speed;  /* current transfer rate set */
        u8      desired_speed;  /* desired transfer rate set */
@@ -562,8 +555,7 @@ struct ide_drive_s {
        unsigned int    drive_data;     /* used by set_pio_mode/dev_select() */
        unsigned int    failures;       /* current failure count */
        unsigned int    max_failures;   /* maximum allowed failure count */
-       u64             probed_capacity;/* initial reported media capacity (ide-cd only currently) */
-
+       u64             probed_capacity;/* initial/native media capacity */
        u64             capacity64;     /* total number of sectors */
 
        int             lun;            /* logical unit */
@@ -1222,7 +1214,7 @@ static inline int ide_pci_is_in_compatibility_mode(struct pci_dev *dev)
 }
 
 void ide_pci_setup_ports(struct pci_dev *, const struct ide_port_info *,
-                        hw_regs_t *, hw_regs_t **);
+                        struct ide_hw *, struct ide_hw **);
 void ide_setup_pci_noise(struct pci_dev *, const struct ide_port_info *);
 
 #ifdef CONFIG_BLK_DEV_IDEDMA_PCI
@@ -1461,16 +1453,18 @@ static inline void ide_acpi_set_state(ide_hwif_t *hwif, int on) {}
 void ide_register_region(struct gendisk *);
 void ide_unregister_region(struct gendisk *);
 
+void ide_check_nien_quirk_list(ide_drive_t *);
 void ide_undecoded_slave(ide_drive_t *);
 
 void ide_port_apply_params(ide_hwif_t *);
 int ide_sysfs_register_port(ide_hwif_t *);
 
-struct ide_host *ide_host_alloc(const struct ide_port_info *, hw_regs_t **);
+struct ide_host *ide_host_alloc(const struct ide_port_info *, struct ide_hw **,
+                               unsigned int);
 void ide_host_free(struct ide_host *);
 int ide_host_register(struct ide_host *, const struct ide_port_info *,
-                     hw_regs_t **);
-int ide_host_add(const struct ide_port_info *, hw_regs_t **,
+                     struct ide_hw **);
+int ide_host_add(const struct ide_port_info *, struct ide_hw **, unsigned int,
                 struct ide_host **);
 void ide_host_remove(struct ide_host *);
 int ide_legacy_device_add(const struct ide_port_info *, unsigned long);
index cfe4fe1b7132016c4a30f480895a27812c09c959..60e8934d10b5e070ab2289a19c787935026722b1 100644 (file)
@@ -79,6 +79,7 @@
 #define ETH_P_AOE      0x88A2          /* ATA over Ethernet            */
 #define ETH_P_TIPC     0x88CA          /* TIPC                         */
 #define ETH_P_FCOE     0x8906          /* Fibre Channel over Ethernet  */
+#define ETH_P_FIP      0x8914          /* FCoE Initialization Protocol */
 #define ETH_P_EDSA     0xDADA          /* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
 
 /*
index ff374ceface08d55c889c5698cdf6bcfda753655..c41e812e9d5ef17fcc894c0e316f5c9da95e486f 100644 (file)
@@ -183,6 +183,7 @@ extern void disable_irq(unsigned int irq);
 extern void enable_irq(unsigned int irq);
 
 /* The following three functions are for the core kernel use only. */
+#ifdef CONFIG_GENERIC_HARDIRQS
 extern void suspend_device_irqs(void);
 extern void resume_device_irqs(void);
 #ifdef CONFIG_PM_SLEEP
@@ -190,6 +191,11 @@ extern int check_wakeup_irqs(void);
 #else
 static inline int check_wakeup_irqs(void) { return 0; }
 #endif
+#else
+static inline void suspend_device_irqs(void) { };
+static inline void resume_device_irqs(void) { };
+static inline int check_wakeup_irqs(void) { return 0; }
+#endif
 
 #if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_HARDIRQS)
 
index 175e63f4a8c08d90de05b5a41382c60e8ffcabba..7bc1440fc47305418967134e2f65e0e23ea24572 100644 (file)
@@ -30,6 +30,10 @@ struct lguest_data
        /* Wallclock time set by the Host. */
        struct timespec time;
 
+       /* Interrupt pending set by the Host.  The Guest should do a hypercall
+        * if it re-enables interrupts and sees this set (to X86_EFLAGS_IF). */
+       int irq_pending;
+
        /* Async hypercall ring.  Instead of directly making hypercalls, we can
         * place them in here for processing the next time the Host wants.
         * This batching can be quite efficient. */
index a53407a4165c838ee457ee368e03758e3cea5560..bfefbdf7498a925856ec5e8425a5df24eb4cbac7 100644 (file)
@@ -57,7 +57,8 @@ enum lguest_req
        LHREQ_INITIALIZE, /* + base, pfnlimit, start */
        LHREQ_GETDMA, /* No longer used */
        LHREQ_IRQ, /* + irq */
-       LHREQ_BREAK, /* + on/off flag (on blocks until someone does off) */
+       LHREQ_BREAK, /* No longer used */
+       LHREQ_EVENTFD, /* + address, fd. */
 };
 
 /* The alignment to use between consumer and producer parts of vring.
index a8f2c0aa4c328af87718285f715bc72b19d7f55f..a7bc6e7b43a7c429b9e4993933a900057034cf77 100644 (file)
@@ -77,6 +77,7 @@ search_extable(const struct exception_table_entry *first,
 void sort_extable(struct exception_table_entry *start,
                  struct exception_table_entry *finish);
 void sort_main_extable(void);
+void trim_init_extable(struct module *m);
 
 #ifdef MODULE
 #define MODULE_GENERIC_TABLE(gtype,name)                       \
index a4f0b931846c25e2e0db59e863490287f62d4b63..6547c3cdbc4c17cddda61d0f92fe83352de2f77c 100644 (file)
@@ -36,9 +36,14 @@ typedef int (*param_set_fn)(const char *val, struct kernel_param *kp);
 /* Returns length written or -errno.  Buffer is 4k (ie. be short!) */
 typedef int (*param_get_fn)(char *buffer, struct kernel_param *kp);
 
+/* Flag bits for kernel_param.flags */
+#define KPARAM_KMALLOCED       1
+#define KPARAM_ISBOOL          2
+
 struct kernel_param {
        const char *name;
-       unsigned int perm;
+       u16 perm;
+       u16 flags;
        param_set_fn set;
        param_get_fn get;
        union {
@@ -79,7 +84,7 @@ struct kparam_array
    parameters.  perm sets the visibility in sysfs: 000 means it's
    not there, read bits mean it's readable, write bits mean it's
    writable. */
-#define __module_param_call(prefix, name, set, get, arg, perm)         \
+#define __module_param_call(prefix, name, set, get, arg, isbool, perm) \
        /* Default value instead of permissions? */                     \
        static int __param_perm_check_##name __attribute__((unused)) =  \
        BUILD_BUG_ON_ZERO((perm) < 0 || (perm) > 0777 || ((perm) & 2))  \
@@ -88,10 +93,13 @@ struct kparam_array
        static struct kernel_param __moduleparam_const __param_##name   \
        __used                                                          \
     __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \
-       = { __param_str_##name, perm, set, get, { arg } }
+       = { __param_str_##name, perm, isbool ? KPARAM_ISBOOL : 0,       \
+           set, get, { arg } }
 
 #define module_param_call(name, set, get, arg, perm)                         \
-       __module_param_call(MODULE_PARAM_PREFIX, name, set, get, arg, perm)
+       __module_param_call(MODULE_PARAM_PREFIX,                              \
+                           name, set, get, arg,                              \
+                           __same_type(*(arg), bool), perm)
 
 /* Helper functions: type is byte, short, ushort, int, uint, long,
    ulong, charp, bool or invbool, or XXX if you define param_get_XXX,
@@ -120,15 +128,16 @@ struct kparam_array
 #define core_param(name, var, type, perm)                              \
        param_check_##type(name, &(var));                               \
        __module_param_call("", name, param_set_##type, param_get_##type, \
-                           &var, perm)
+                           &var, __same_type(var, bool), perm)
 #endif /* !MODULE */
 
 /* Actually copy string: maxlen param is usually sizeof(string). */
 #define module_param_string(name, string, len, perm)                   \
        static const struct kparam_string __param_string_##name         \
                = { len, string };                                      \
-       module_param_call(name, param_set_copystring, param_get_string, \
-                         .str = &__param_string_##name, perm);         \
+       __module_param_call(MODULE_PARAM_PREFIX, name,                  \
+                           param_set_copystring, param_get_string,     \
+                           .str = &__param_string_##name, 0, perm);    \
        __MODULE_PARM_TYPE(name, "string")
 
 /* Called on module insert or kernel boot */
@@ -186,21 +195,30 @@ extern int param_set_charp(const char *val, struct kernel_param *kp);
 extern int param_get_charp(char *buffer, struct kernel_param *kp);
 #define param_check_charp(name, p) __param_check(name, p, char *)
 
+/* For historical reasons "bool" parameters can be (unsigned) "int". */
 extern int param_set_bool(const char *val, struct kernel_param *kp);
 extern int param_get_bool(char *buffer, struct kernel_param *kp);
-#define param_check_bool(name, p) __param_check(name, p, int)
+#define param_check_bool(name, p)                                      \
+       static inline void __check_##name(void)                         \
+       {                                                               \
+               BUILD_BUG_ON(!__same_type(*(p), bool) &&                \
+                            !__same_type(*(p), unsigned int) &&        \
+                            !__same_type(*(p), int));                  \
+       }
 
 extern int param_set_invbool(const char *val, struct kernel_param *kp);
 extern int param_get_invbool(char *buffer, struct kernel_param *kp);
-#define param_check_invbool(name, p) __param_check(name, p, int)
+#define param_check_invbool(name, p) __param_check(name, p, bool)
 
 /* Comma-separated array: *nump is set to number they actually specified. */
 #define module_param_array_named(name, array, type, nump, perm)                \
        static const struct kparam_array __param_arr_##name             \
        = { ARRAY_SIZE(array), nump, param_set_##type, param_get_##type,\
            sizeof(array[0]), array };                                  \
-       module_param_call(name, param_array_set, param_array_get,       \
-                         .arr = &__param_arr_##name, perm);            \
+       __module_param_call(MODULE_PARAM_PREFIX, name,                  \
+                           param_array_set, param_array_get,           \
+                           .arr = &__param_arr_##name,                 \
+                           __same_type(array[0], bool), perm);         \
        __MODULE_PARM_TYPE(name, "array of " #type)
 
 #define module_param_array(name, type, nump, perm)             \
index 7339c7bf73315e56a981b382229abfc39384e23b..13f126c89ae8f1e5e35355eff8b94e1c9095906e 100644 (file)
@@ -18,7 +18,19 @@ struct page_cgroup {
 };
 
 void __meminit pgdat_page_cgroup_init(struct pglist_data *pgdat);
-void __init page_cgroup_init(void);
+
+#ifdef CONFIG_SPARSEMEM
+static inline void __init page_cgroup_init_flatmem(void)
+{
+}
+extern void __init page_cgroup_init(void);
+#else
+void __init page_cgroup_init_flatmem(void);
+static inline void __init page_cgroup_init(void)
+{
+}
+#endif
+
 struct page_cgroup *lookup_page_cgroup(struct page *page);
 
 enum {
@@ -87,6 +99,10 @@ static inline void page_cgroup_init(void)
 {
 }
 
+static inline void __init page_cgroup_init_flatmem(void)
+{
+}
+
 #endif
 
 #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
index d7d1c41a0b17db03da5768b35233f409b98b9b7d..19f8e6d1a4d22bb47105a387a7b109a252b17d76 100644 (file)
 #define PCI_DEVICE_ID_PLX_PCI200SYN    0x3196
 #define PCI_DEVICE_ID_PLX_9030          0x9030
 #define PCI_DEVICE_ID_PLX_9050         0x9050
+#define PCI_DEVICE_ID_PLX_9056         0x9056
 #define PCI_DEVICE_ID_PLX_9080         0x9080
 #define PCI_DEVICE_ID_PLX_GTEK_SERIAL2 0xa001
 
 
 #define PCI_VENDOR_ID_CREATIVE         0x1102 /* duplicate: ECTIVA */
 #define PCI_DEVICE_ID_CREATIVE_EMU10K1 0x0002
+#define PCI_DEVICE_ID_CREATIVE_20K1    0x0005
+#define PCI_DEVICE_ID_CREATIVE_20K2    0x000b
+#define PCI_SUBDEVICE_ID_CREATIVE_SB0760       0x0024
+#define PCI_SUBDEVICE_ID_CREATIVE_SB08801      0x0041
+#define PCI_SUBDEVICE_ID_CREATIVE_SB08802      0x0042
+#define PCI_SUBDEVICE_ID_CREATIVE_SB08803      0x0043
+#define PCI_SUBDEVICE_ID_CREATIVE_HENDRIX      0x6000
 
 #define PCI_VENDOR_ID_ECTIVA           0x1102 /* duplicate: CREATIVE */
 #define PCI_DEVICE_ID_ECTIVA_EV1938    0x8938
 #define PCI_SUBDEVICE_ID_HYPERCOPE_METRO       0x0107
 #define PCI_SUBDEVICE_ID_HYPERCOPE_CHAMP2      0x0108
 
+#define PCI_VENDOR_ID_DIGIGRAM         0x1369
+#define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM    0xc001
+#define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM        0xc002
+
 #define PCI_VENDOR_ID_KAWASAKI         0x136b
 #define PCI_DEVICE_ID_MCHIP_KL5A72002  0xff01
 
index 6e133954e2e44a95e968c09a7a8b3669280ce74d..1b3118a1023abfb3f8c7c3f8b0204e1f87d5d212 100644 (file)
@@ -120,6 +120,8 @@ enum perf_counter_sample_format {
        PERF_SAMPLE_ID                          = 1U << 6,
        PERF_SAMPLE_CPU                         = 1U << 7,
        PERF_SAMPLE_PERIOD                      = 1U << 8,
+
+       PERF_SAMPLE_MAX = 1U << 9,              /* non-ABI */
 };
 
 /*
@@ -131,17 +133,26 @@ enum perf_counter_read_format {
        PERF_FORMAT_TOTAL_TIME_ENABLED          = 1U << 0,
        PERF_FORMAT_TOTAL_TIME_RUNNING          = 1U << 1,
        PERF_FORMAT_ID                          = 1U << 2,
+
+       PERF_FORMAT_MAX = 1U << 3,              /* non-ABI */
 };
 
+#define PERF_ATTR_SIZE_VER0    64      /* sizeof first published struct */
+
 /*
  * Hardware event to monitor via a performance monitoring counter:
  */
 struct perf_counter_attr {
+
        /*
         * Major type: hardware/software/tracepoint/etc.
         */
        __u32                   type;
-       __u32                   __reserved_1;
+
+       /*
+        * Size of the attr structure, for fwd/bwd compat.
+        */
+       __u32                   size;
 
        /*
         * Type specific configuration information.
@@ -168,12 +179,12 @@ struct perf_counter_attr {
                                comm           :  1, /* include comm data     */
                                freq           :  1, /* use freq, not period  */
 
-                               __reserved_2   : 53;
+                               __reserved_1   : 53;
 
        __u32                   wakeup_events;  /* wakeup every n events */
-       __u32                   __reserved_3;
+       __u32                   __reserved_2;
 
-       __u64                   __reserved_4;
+       __u64                   __reserved_3;
 };
 
 /*
@@ -621,7 +632,8 @@ extern int perf_counter_overflow(struct perf_counter *counter, int nmi,
 static inline int is_software_counter(struct perf_counter *counter)
 {
        return (counter->attr.type != PERF_TYPE_RAW) &&
-               (counter->attr.type != PERF_TYPE_HARDWARE);
+               (counter->attr.type != PERF_TYPE_HARDWARE) &&
+               (counter->attr.type != PERF_TYPE_HW_CACHE);
 }
 
 extern void perf_swcounter_event(u32, u64, int, struct pt_regs *, u64);
index 1d4e2d289821d1d74f9e203dc966ed0ed1e5e0ef..b3f74764a5869dc579aae27792381619ce2bbf4b 100644 (file)
@@ -382,14 +382,13 @@ struct dev_pm_info {
 #ifdef CONFIG_PM_SLEEP
 extern void device_pm_lock(void);
 extern int sysdev_resume(void);
-extern void device_power_up(pm_message_t state);
-extern void device_resume(pm_message_t state);
+extern void dpm_resume_noirq(pm_message_t state);
+extern void dpm_resume_end(pm_message_t state);
 
 extern void device_pm_unlock(void);
 extern int sysdev_suspend(pm_message_t state);
-extern int device_power_down(pm_message_t state);
-extern int device_suspend(pm_message_t state);
-extern int device_prepare_suspend(pm_message_t state);
+extern int dpm_suspend_noirq(pm_message_t state);
+extern int dpm_suspend_start(pm_message_t state);
 
 extern void __suspend_report_result(const char *function, void *fn, int ret);
 
@@ -403,7 +402,7 @@ extern void __suspend_report_result(const char *function, void *fn, int ret);
 #define device_pm_lock() do {} while (0)
 #define device_pm_unlock() do {} while (0)
 
-static inline int device_suspend(pm_message_t state)
+static inline int dpm_suspend_start(pm_message_t state)
 {
        return 0;
 }
index ca3c88773028046f36a807741f2f6fa16b9c65fe..b063c7328ba5b103b444fea061d07b7f55d24d03 100644 (file)
@@ -446,6 +446,7 @@ int pnp_start_dev(struct pnp_dev *dev);
 int pnp_stop_dev(struct pnp_dev *dev);
 int pnp_activate_dev(struct pnp_dev *dev);
 int pnp_disable_dev(struct pnp_dev *dev);
+int pnp_range_reserved(resource_size_t start, resource_size_t end);
 
 /* protocol helpers */
 int pnp_is_active(struct pnp_dev *dev);
@@ -476,6 +477,7 @@ static inline int pnp_start_dev(struct pnp_dev *dev) { return -ENODEV; }
 static inline int pnp_stop_dev(struct pnp_dev *dev) { return -ENODEV; }
 static inline int pnp_activate_dev(struct pnp_dev *dev) { return -ENODEV; }
 static inline int pnp_disable_dev(struct pnp_dev *dev) { return -ENODEV; }
+static inline int pnp_range_reserved(resource_size_t start, resource_size_t end) { return 0;}
 
 /* protocol helpers */
 static inline int pnp_is_active(struct pnp_dev *dev) { return 0; }
index 48803064cedf72fb5f52f9550a7625b03b216366..219b8fb4651dd1b15d4c2fe9296c01fef9e2b1e2 100644 (file)
@@ -319,4 +319,6 @@ static inline void *kzalloc_node(size_t size, gfp_t flags, int node)
        return kmalloc_node(size, flags | __GFP_ZERO, node);
 }
 
+void __init kmem_cache_init_late(void);
+
 #endif /* _LINUX_SLAB_H */
index 0ec00b39d006471e585a11e74759ff832f6f028b..bb5368df4be84397f28ec90b134b63687fdcaed6 100644 (file)
@@ -34,4 +34,9 @@ static __always_inline void *__kmalloc(size_t size, gfp_t flags)
        return kmalloc(size, flags);
 }
 
+static inline void kmem_cache_init_late(void)
+{
+       /* Nothing to do */
+}
+
 #endif /* __LINUX_SLOB_DEF_H */
index be5d40c43bd2e7aa96264e624b074f4a884826f4..4dcbc2c71491ec9dc736b83c1eeedb03f4650a95 100644 (file)
@@ -302,4 +302,6 @@ static __always_inline void *kmalloc_node(size_t size, gfp_t flags, int node)
 }
 #endif
 
+void __init kmem_cache_init_late(void);
+
 #endif /* _LINUX_SLUB_DEF_H */
index 795032edfc46eca55687c1739b0022364dc1fb14..cd15df6c63cdc07f51d2823ccb2a7e77b070c8ee 100644 (file)
@@ -245,11 +245,6 @@ extern unsigned long get_safe_page(gfp_t gfp_mask);
 
 extern void hibernation_set_ops(struct platform_hibernation_ops *ops);
 extern int hibernate(void);
-extern int hibernate_nvs_register(unsigned long start, unsigned long size);
-extern int hibernate_nvs_alloc(void);
-extern void hibernate_nvs_free(void);
-extern void hibernate_nvs_save(void);
-extern void hibernate_nvs_restore(void);
 extern bool system_entering_hibernation(void);
 #else /* CONFIG_HIBERNATION */
 static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
@@ -258,6 +253,16 @@ static inline void swsusp_unset_page_free(struct page *p) {}
 
 static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
 static inline int hibernate(void) { return -ENOSYS; }
+static inline bool system_entering_hibernation(void) { return false; }
+#endif /* CONFIG_HIBERNATION */
+
+#ifdef CONFIG_HIBERNATION_NVS
+extern int hibernate_nvs_register(unsigned long start, unsigned long size);
+extern int hibernate_nvs_alloc(void);
+extern void hibernate_nvs_free(void);
+extern void hibernate_nvs_save(void);
+extern void hibernate_nvs_restore(void);
+#else /* CONFIG_HIBERNATION_NVS */
 static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
 {
        return 0;
@@ -266,8 +271,7 @@ static inline int hibernate_nvs_alloc(void) { return 0; }
 static inline void hibernate_nvs_free(void) {}
 static inline void hibernate_nvs_save(void) {}
 static inline void hibernate_nvs_restore(void) {}
-static inline bool system_entering_hibernation(void) { return false; }
-#endif /* CONFIG_HIBERNATION */
+#endif /* CONFIG_HIBERNATION_NVS */
 
 #ifdef CONFIG_PM_SLEEP
 void save_processor_state(void);
index c6c84ad8bd7119dea51f73ab13ee34987c5d152d..418d90f5effe72ff9921a6ea066bb526a7337127 100644 (file)
@@ -758,6 +758,6 @@ int kernel_execve(const char *filename, char *const argv[], char *const envp[]);
 
 
 asmlinkage long sys_perf_counter_open(
-               const struct perf_counter_attr __user *attr_uptr,
+               struct perf_counter_attr __user *attr_uptr,
                pid_t pid, int cpu, int group_fd, unsigned long flags);
 #endif
index 06005fa9e982e0f703a7eef56f4108378c566f24..4fca4f5440ba47c93d7f903b8395a7b98791a021 100644 (file)
 
 /**
  * virtqueue - a queue to register buffers for sending or receiving.
+ * @list: the chain of virtqueues for this device
  * @callback: the function to call when buffers are consumed (can be NULL).
+ * @name: the name of this virtqueue (mainly for debugging)
  * @vdev: the virtio device this queue was created for.
  * @vq_ops: the operations for this virtqueue (see below).
  * @priv: a pointer for the virtqueue implementation to use.
  */
-struct virtqueue
-{
+struct virtqueue {
+       struct list_head list;
        void (*callback)(struct virtqueue *vq);
+       const char *name;
        struct virtio_device *vdev;
        struct virtqueue_ops *vq_ops;
        void *priv;
@@ -76,15 +79,16 @@ struct virtqueue_ops {
  * @dev: underlying device.
  * @id: the device type identification (used to match it with a driver).
  * @config: the configuration ops for this device.
+ * @vqs: the list of virtqueues for this device.
  * @features: the features supported by both driver and device.
  * @priv: private pointer for the driver's use.
  */
-struct virtio_device
-{
+struct virtio_device {
        int index;
        struct device dev;
        struct virtio_device_id id;
        struct virtio_config_ops *config;
+       struct list_head vqs;
        /* Note that this is a Linux set_bit-style bitmap. */
        unsigned long features[1];
        void *priv;
@@ -99,8 +103,7 @@ void unregister_virtio_device(struct virtio_device *dev);
  * @id_table: the ids serviced by this driver.
  * @feature_table: an array of feature numbers supported by this device.
  * @feature_table_size: number of entries in the feature table array.
- * @probe: the function to call when a device is found.  Returns a token for
- *    remove, or PTR_ERR().
+ * @probe: the function to call when a device is found.  Returns 0 or -errno.
  * @remove: the function when a device is removed.
  * @config_changed: optional function to call when the device configuration
  *    changes; may be called in interrupt context.
index bf8ec283b232af65116af4fa338ad602f1b8d798..99f514575f6afa1fe0e6ceeb4c50c7fb4e6520a5 100644 (file)
@@ -29,6 +29,7 @@
 #define VIRTIO_F_NOTIFY_ON_EMPTY       24
 
 #ifdef __KERNEL__
+#include <linux/err.h>
 #include <linux/virtio.h>
 
 /**
  * @set_status: write the status byte
  *     vdev: the virtio_device
  *     status: the new status byte
+ * @request_vqs: request the specified number of virtqueues
+ *     vdev: the virtio_device
+ *     max_vqs: the max number of virtqueues we want
+ *      If supplied, must call before any virtqueues are instantiated.
+ *      To modify the max number of virtqueues after request_vqs has been
+ *      called, call free_vqs and then request_vqs with a new value.
+ * @free_vqs: cleanup resources allocated by request_vqs
+ *     vdev: the virtio_device
+ *      If supplied, must call after all virtqueues have been deleted.
  * @reset: reset the device
  *     vdev: the virtio device
  *     After this, status and feature negotiation must be done again
- * @find_vq: find a virtqueue and instantiate it.
+ * @find_vqs: find virtqueues and instantiate them.
  *     vdev: the virtio_device
- *     index: the 0-based virtqueue number in case there's more than one.
- *     callback: the virqtueue callback
- *     Returns the new virtqueue or ERR_PTR() (eg. -ENOENT).
- * @del_vq: free a virtqueue found by find_vq().
+ *     nvqs: the number of virtqueues to find
+ *     vqs: on success, includes new virtqueues
+ *     callbacks: array of callbacks, for each virtqueue
+ *     names: array of virtqueue names (mainly for debugging)
+ *     Returns 0 on success or error status
+ * @del_vqs: free virtqueues found by find_vqs().
  * @get_features: get the array of feature bits for this device.
  *     vdev: the virtio_device
  *     Returns the first 32 feature bits (all we currently need).
@@ -66,6 +78,7 @@
  *     This gives the final feature bits for the device: it can change
  *     the dev->feature bits if it wants.
  */
+typedef void vq_callback_t(struct virtqueue *);
 struct virtio_config_ops
 {
        void (*get)(struct virtio_device *vdev, unsigned offset,
@@ -75,10 +88,11 @@ struct virtio_config_ops
        u8 (*get_status)(struct virtio_device *vdev);
        void (*set_status)(struct virtio_device *vdev, u8 status);
        void (*reset)(struct virtio_device *vdev);
-       struct virtqueue *(*find_vq)(struct virtio_device *vdev,
-                                    unsigned index,
-                                    void (*callback)(struct virtqueue *));
-       void (*del_vq)(struct virtqueue *vq);
+       int (*find_vqs)(struct virtio_device *, unsigned nvqs,
+                       struct virtqueue *vqs[],
+                       vq_callback_t *callbacks[],
+                       const char *names[]);
+       void (*del_vqs)(struct virtio_device *);
        u32 (*get_features)(struct virtio_device *vdev);
        void (*finalize_features)(struct virtio_device *vdev);
 };
@@ -99,7 +113,9 @@ static inline bool virtio_has_feature(const struct virtio_device *vdev,
        if (__builtin_constant_p(fbit))
                BUILD_BUG_ON(fbit >= 32);
 
-       virtio_check_driver_offered_feature(vdev, fbit);
+       if (fbit < VIRTIO_TRANSPORT_F_START)
+               virtio_check_driver_offered_feature(vdev, fbit);
+
        return test_bit(fbit, vdev->features);
 }
 
@@ -126,5 +142,18 @@ static inline int virtio_config_buf(struct virtio_device *vdev,
        vdev->config->get(vdev, offset, buf, len);
        return 0;
 }
+
+static inline
+struct virtqueue *virtio_find_single_vq(struct virtio_device *vdev,
+                                       vq_callback_t *c, const char *n)
+{
+       vq_callback_t *callbacks[] = { c };
+       const char *names[] = { n };
+       struct virtqueue *vq;
+       int err = vdev->config->find_vqs(vdev, 1, &vq, callbacks, names);
+       if (err < 0)
+               return ERR_PTR(err);
+       return vq;
+}
 #endif /* __KERNEL__ */
 #endif /* _LINUX_VIRTIO_CONFIG_H */
index cd0fd5d181a6f259e8e24f7fb12f107cd35a4515..9a3d7c48c622d8c3e525ce5653fd12e98b4d9f9a 100644 (file)
 /* The bit of the ISR which indicates a device configuration change. */
 #define VIRTIO_PCI_ISR_CONFIG          0x2
 
+/* MSI-X registers: only enabled if MSI-X is enabled. */
+/* A 16-bit vector for configuration changes. */
+#define VIRTIO_MSI_CONFIG_VECTOR        20
+/* A 16-bit vector for selected queue notifications. */
+#define VIRTIO_MSI_QUEUE_VECTOR         22
+/* Vector value used to disable MSI for queue */
+#define VIRTIO_MSI_NO_VECTOR            0xffff
+
 /* The remaining space is defined by each driver as the per-driver
  * configuration space */
-#define VIRTIO_PCI_CONFIG              20
+#define VIRTIO_PCI_CONFIG(dev)         ((dev)->msix_enabled ? 24 : 20)
 
 /* Virtio ABI version, this must match exactly */
 #define VIRTIO_PCI_ABI_VERSION         0
index 71e03722fb5946fa60ab67a88a7250bee3c77b7c..693e0ec5afa67f9ec2c18d48563cccdba3261409 100644 (file)
@@ -14,6 +14,8 @@
 #define VRING_DESC_F_NEXT      1
 /* This marks a buffer as write-only (otherwise read-only). */
 #define VRING_DESC_F_WRITE     2
+/* This means the buffer contains a list of buffer descriptors. */
+#define VRING_DESC_F_INDIRECT  4
 
 /* The Host uses this in used->flags to advise the Guest: don't kick me when
  * you add a buffer.  It's unreliable, so it's simply an optimization.  Guest
@@ -24,6 +26,9 @@
  * optimization.  */
 #define VRING_AVAIL_F_NO_INTERRUPT     1
 
+/* We support indirect buffer descriptors */
+#define VIRTIO_RING_F_INDIRECT_DESC    28
+
 /* Virtio ring descriptors: 16 bytes.  These can chain together via "next". */
 struct vring_desc
 {
@@ -119,7 +124,8 @@ struct virtqueue *vring_new_virtqueue(unsigned int num,
                                      struct virtio_device *vdev,
                                      void *pages,
                                      void (*notify)(struct virtqueue *vq),
-                                     void (*callback)(struct virtqueue *vq));
+                                     void (*callback)(struct virtqueue *vq),
+                                     const char *name);
 void vring_del_virtqueue(struct virtqueue *vq);
 /* Filter out transport-specific feature bits. */
 void vring_transport_features(struct virtio_device *vdev);
index 0627a9ae6347c206f7388dd9dcd8959e52ae55c6..3d138c1fcf8abb14fe2f024c4760448e4a965f4c 100644 (file)
  * http://www.t11.org/ftp/t11/pub/fc/bb-5/08-543v1.pdf
  */
 
-/*
- * The FIP ethertype eventually goes in net/if_ether.h.
- */
-#ifndef ETH_P_FIP
-#define ETH_P_FIP      0x8914  /* FIP Ethertype */
-#endif
-
 #define FIP_DEF_PRI    128     /* default selection priority */
 #define FIP_DEF_FC_MAP 0x0efc00 /* default FCoE MAP (MAC OUI) value */
 #define FIP_DEF_FKA    8000    /* default FCF keep-alive/advert period (mS) */
index d0ed5226f8c4f02db9ad40df1c8455d83c053f92..4426f00da5ffcbf1ca9f92409bbb860479e60a18 100644 (file)
 #define ISCSI_IF_H
 
 #include <scsi/iscsi_proto.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+
+#define ISCSI_NL_GRP_ISCSID    1
+#define ISCSI_NL_GRP_UIP       2
 
 #define UEVENT_BASE                    10
 #define KEVENT_BASE                    100
@@ -50,7 +55,10 @@ enum iscsi_uevent_e {
        ISCSI_UEVENT_TGT_DSCVR          = UEVENT_BASE + 15,
        ISCSI_UEVENT_SET_HOST_PARAM     = UEVENT_BASE + 16,
        ISCSI_UEVENT_UNBIND_SESSION     = UEVENT_BASE + 17,
-       ISCSI_UEVENT_CREATE_BOUND_SESSION       = UEVENT_BASE + 18,
+       ISCSI_UEVENT_CREATE_BOUND_SESSION               = UEVENT_BASE + 18,
+       ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST  = UEVENT_BASE + 19,
+
+       ISCSI_UEVENT_PATH_UPDATE        = UEVENT_BASE + 20,
 
        /* up events */
        ISCSI_KEVENT_RECV_PDU           = KEVENT_BASE + 1,
@@ -59,6 +67,9 @@ enum iscsi_uevent_e {
        ISCSI_KEVENT_DESTROY_SESSION    = KEVENT_BASE + 4,
        ISCSI_KEVENT_UNBIND_SESSION     = KEVENT_BASE + 5,
        ISCSI_KEVENT_CREATE_SESSION     = KEVENT_BASE + 6,
+
+       ISCSI_KEVENT_PATH_REQ           = KEVENT_BASE + 7,
+       ISCSI_KEVENT_IF_DOWN            = KEVENT_BASE + 8,
 };
 
 enum iscsi_tgt_dscvr {
@@ -131,6 +142,10 @@ struct iscsi_uevent {
                struct msg_transport_connect {
                        uint32_t        non_blocking;
                } ep_connect;
+               struct msg_transport_connect_through_host {
+                       uint32_t        host_no;
+                       uint32_t        non_blocking;
+               } ep_connect_through_host;
                struct msg_transport_poll {
                        uint64_t        ep_handle;
                        uint32_t        timeout_ms;
@@ -154,6 +169,9 @@ struct iscsi_uevent {
                        uint32_t        param; /* enum iscsi_host_param */
                        uint32_t        len;
                } set_host_param;
+               struct msg_set_path {
+                       uint32_t        host_no;
+               } set_path;
        } u;
        union {
                /* messages k -> u */
@@ -187,9 +205,38 @@ struct iscsi_uevent {
                struct msg_transport_connect_ret {
                        uint64_t        handle;
                } ep_connect_ret;
+               struct msg_req_path {
+                       uint32_t        host_no;
+               } req_path;
+               struct msg_notify_if_down {
+                       uint32_t        host_no;
+               } notify_if_down;
        } r;
 } __attribute__ ((aligned (sizeof(uint64_t))));
 
+/*
+ * To keep the struct iscsi_uevent size the same for userspace code
+ * compatibility, the main structure for ISCSI_UEVENT_PATH_UPDATE and
+ * ISCSI_KEVENT_PATH_REQ is defined separately and comes after the
+ * struct iscsi_uevent in the NETLINK_ISCSI message.
+ */
+struct iscsi_path {
+       uint64_t        handle;
+       uint8_t         mac_addr[6];
+       uint8_t         mac_addr_old[6];
+       uint32_t        ip_addr_len;    /* 4 or 16 */
+       union {
+               struct in_addr  v4_addr;
+               struct in6_addr v6_addr;
+       } src;
+       union {
+               struct in_addr  v4_addr;
+               struct in6_addr v6_addr;
+       } dst;
+       uint16_t        vlan_id;
+       uint16_t        pmtu;
+} __attribute__ ((aligned (sizeof(uint64_t))));
+
 /*
  * Common error codes
  */
index 45f9cc642c46572623eaf09568332edcd17f475c..ebdd9f4cf070da669c2b80a817c9b156e51f360e 100644 (file)
@@ -679,6 +679,7 @@ struct fc_lport {
        unsigned int            e_d_tov;
        unsigned int            r_a_tov;
        u8                      max_retry_count;
+       u8                      max_rport_retry_count;
        u16                     link_speed;
        u16                     link_supported_speeds;
        u16                     lro_xid;        /* max xid for fcoe lro */
index 0289f5745fb9a687e92f46625a8bd38fa81407d0..196525cd402f6a86d09d208557f03217dd9bc08a 100644 (file)
@@ -82,9 +82,12 @@ enum {
 
 
 enum {
+       ISCSI_TASK_FREE,
        ISCSI_TASK_COMPLETED,
        ISCSI_TASK_PENDING,
        ISCSI_TASK_RUNNING,
+       ISCSI_TASK_ABRT_TMF,            /* aborted due to TMF */
+       ISCSI_TASK_ABRT_SESS_RECOV,     /* aborted due to session recovery */
 };
 
 struct iscsi_r2t_info {
@@ -181,9 +184,7 @@ struct iscsi_conn {
 
        /* xmit */
        struct list_head        mgmtqueue;      /* mgmt (control) xmit queue */
-       struct list_head        mgmt_run_list;  /* list of control tasks */
-       struct list_head        xmitqueue;      /* data-path cmd queue */
-       struct list_head        run_list;       /* list of cmds in progress */
+       struct list_head        cmdqueue;       /* data-path cmd queue */
        struct list_head        requeue;        /* tasks needing another run */
        struct work_struct      xmitwork;       /* per-conn. xmit workqueue */
        unsigned long           suspend_tx;     /* suspend Tx */
@@ -406,6 +407,7 @@ extern int __iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *,
                                char *, int);
 extern int iscsi_verify_itt(struct iscsi_conn *, itt_t);
 extern struct iscsi_task *iscsi_itt_to_ctask(struct iscsi_conn *, itt_t);
+extern struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *, itt_t);
 extern void iscsi_requeue_task(struct iscsi_task *task);
 extern void iscsi_put_task(struct iscsi_task *task);
 extern void __iscsi_get_task(struct iscsi_task *task);
index f888a6fda07302b3df0dffc731e4c04aa0985863..56e920ade3269f4895546c13bc419bad045f2400 100644 (file)
@@ -29,6 +29,7 @@ enum {
        OSD_APAGE_PARTITION_INFORMATION = OSD_APAGE_PARTITION_FIRST + 1,
        OSD_APAGE_PARTITION_QUOTAS      = OSD_APAGE_PARTITION_FIRST + 2,
        OSD_APAGE_PARTITION_TIMESTAMP   = OSD_APAGE_PARTITION_FIRST + 3,
+       OSD_APAGE_PARTITION_ATTR_ACCESS = OSD_APAGE_PARTITION_FIRST + 4,
        OSD_APAGE_PARTITION_SECURITY    = OSD_APAGE_PARTITION_FIRST + 5,
        OSD_APAGE_PARTITION_LAST        = 0x5FFFFFFF,
 
@@ -51,7 +52,9 @@ enum {
        OSD_APAGE_RESERVED_TYPE_LAST    = 0xEFFFFFFF,
 
        OSD_APAGE_COMMON_FIRST          = 0xF0000000,
-       OSD_APAGE_COMMON_LAST           = 0xFFFFFFFE,
+       OSD_APAGE_COMMON_LAST           = 0xFFFFFFFD,
+
+       OSD_APAGE_CURRENT_COMMAND       = 0xFFFFFFFE,
 
        OSD_APAGE_REQUEST_ALL           = 0xFFFFFFFF,
 };
@@ -106,10 +109,30 @@ enum {
        OSD_ATTR_RI_PRODUCT_REVISION_LEVEL   = 0x7,   /* 4        */
        OSD_ATTR_RI_PRODUCT_SERIAL_NUMBER    = 0x8,   /* variable */
        OSD_ATTR_RI_OSD_NAME                 = 0x9,   /* variable */
+       OSD_ATTR_RI_MAX_CDB_CONTINUATION_LEN = 0xA,   /* 4        */
        OSD_ATTR_RI_TOTAL_CAPACITY           = 0x80,  /* 8        */
        OSD_ATTR_RI_USED_CAPACITY            = 0x81,  /* 8        */
        OSD_ATTR_RI_NUMBER_OF_PARTITIONS     = 0xC0,  /* 8        */
        OSD_ATTR_RI_CLOCK                    = 0x100, /* 6        */
+       OARI_DEFAULT_ISOLATION_METHOD        = 0X110, /* 1        */
+       OARI_SUPPORTED_ISOLATION_METHODS     = 0X111, /* 32       */
+
+       OARI_DATA_ATOMICITY_GUARANTEE                   = 0X120,   /* 8       */
+       OARI_DATA_ATOMICITY_ALIGNMENT                   = 0X121,   /* 8       */
+       OARI_ATTRIBUTES_ATOMICITY_GUARANTEE             = 0X122,   /* 8       */
+       OARI_DATA_ATTRIBUTES_ATOMICITY_MULTIPLIER       = 0X123,   /* 1       */
+
+       OARI_MAXIMUM_SNAPSHOTS_COUNT                    = 0X1C1,    /* 0 or 4 */
+       OARI_MAXIMUM_CLONES_COUNT                       = 0X1C2,    /* 0 or 4 */
+       OARI_MAXIMUM_BRANCH_DEPTH                       = 0X1CC,    /* 0 or 4 */
+       OARI_SUPPORTED_OBJECT_DUPLICATION_METHOD_FIRST  = 0X200,    /* 0 or 4 */
+       OARI_SUPPORTED_OBJECT_DUPLICATION_METHOD_LAST   = 0X2ff,    /* 0 or 4 */
+       OARI_SUPPORTED_TIME_OF_DUPLICATION_METHOD_FIRST = 0X300,    /* 0 or 4 */
+       OARI_SUPPORTED_TIME_OF_DUPLICATION_METHOD_LAST  = 0X30F,    /* 0 or 4 */
+       OARI_SUPPORT_FOR_DUPLICATED_OBJECT_FREEZING     = 0X310,    /* 0 or 4 */
+       OARI_SUPPORT_FOR_SNAPSHOT_REFRESHING            = 0X311,    /* 0 or 1 */
+       OARI_SUPPORTED_CDB_CONTINUATION_DESC_TYPE_FIRST = 0X7000001,/* 0 or 4 */
+       OARI_SUPPORTED_CDB_CONTINUATION_DESC_TYPE_LAST  = 0X700FFFF,/* 0 or 4 */
 };
 /* Root_Information_attributes_page does not have a get_page structure */
 
@@ -120,7 +143,15 @@ enum {
        OSD_ATTR_PI_PARTITION_ID            = 0x1,     /* 8        */
        OSD_ATTR_PI_USERNAME                = 0x9,     /* variable */
        OSD_ATTR_PI_USED_CAPACITY           = 0x81,    /* 8        */
+       OSD_ATTR_PI_USED_CAPACITY_INCREMENT = 0x84,    /* 0 or 8   */
        OSD_ATTR_PI_NUMBER_OF_OBJECTS       = 0xC1,    /* 8        */
+
+       OSD_ATTR_PI_ACTUAL_DATA_SPACE                      = 0xD1, /* 0 or 8 */
+       OSD_ATTR_PI_RESERVED_DATA_SPACE                    = 0xD2, /* 0 or 8 */
+       OSD_ATTR_PI_DEFAULT_SNAPSHOT_DUPLICATION_METHOD    = 0x200,/* 0 or 4 */
+       OSD_ATTR_PI_DEFAULT_CLONE_DUPLICATION_METHOD       = 0x201,/* 0 or 4 */
+       OSD_ATTR_PI_DEFAULT_SP_TIME_OF_DUPLICATION         = 0x300,/* 0 or 4 */
+       OSD_ATTR_PI_DEFAULT_CLONE_TIME_OF_DUPLICATION      = 0x301,/* 0 or 4 */
 };
 /* Partition Information attributes page does not have a get_page structure */
 
@@ -131,6 +162,7 @@ enum {
        OSD_ATTR_CI_PARTITION_ID           = 0x1,       /* 8        */
        OSD_ATTR_CI_COLLECTION_OBJECT_ID   = 0x2,       /* 8        */
        OSD_ATTR_CI_USERNAME               = 0x9,       /* variable */
+       OSD_ATTR_CI_COLLECTION_TYPE        = 0xA,       /* 1        */
        OSD_ATTR_CI_USED_CAPACITY          = 0x81,      /* 8        */
 };
 /* Collection Information attributes page does not have a get_page structure */
@@ -144,6 +176,8 @@ enum {
        OSD_ATTR_OI_USERNAME             = 0x9,       /* variable */
        OSD_ATTR_OI_USED_CAPACITY        = 0x81,      /* 8        */
        OSD_ATTR_OI_LOGICAL_LENGTH       = 0x82,      /* 8        */
+       SD_ATTR_OI_ACTUAL_DATA_SPACE     = 0XD1,      /* 0 OR 8   */
+       SD_ATTR_OI_RESERVED_DATA_SPACE   = 0XD2,      /* 0 OR 8   */
 };
 /* Object Information attributes page does not have a get_page structure */
 
@@ -248,7 +282,18 @@ struct object_timestamps_attributes_page {
        struct osd_timestamp data_modified_time;
 }  __packed;
 
-/* 7.1.2.19 Collections attributes page */
+/* OSD2r05: 7.1.3.19 Attributes Access attributes page
+ * (OSD_APAGE_PARTITION_ATTR_ACCESS)
+ *
+ * each attribute is of the form below. Total array length is deduced
+ * from the attribute's length
+ * (See allowed_attributes_access of the struct osd_cap_object_descriptor)
+ */
+struct attributes_access_attr {
+       struct osd_attributes_list_attrid attr_list[0];
+} __packed;
+
+/* OSD2r05: 7.1.2.21 Collections attributes page */
 /* TBD */
 
 /* 7.1.2.20 Root Policy/Security attributes page (OSD_APAGE_ROOT_SECURITY) */
@@ -324,4 +369,29 @@ struct object_security_attributes_page {
        __be32 policy_access_tag;
 }  __packed;
 
+/* OSD2r05: 7.1.3.31 Current Command attributes page
+ * (OSD_APAGE_CURRENT_COMMAND)
+ */
+enum {
+       OSD_ATTR_CC_RESPONSE_INTEGRITY_CHECK_VALUE     = 0x1, /* 32  */
+       OSD_ATTR_CC_OBJECT_TYPE                        = 0x2, /* 1   */
+       OSD_ATTR_CC_PARTITION_ID                       = 0x3, /* 8   */
+       OSD_ATTR_CC_OBJECT_ID                          = 0x4, /* 8   */
+       OSD_ATTR_CC_STARTING_BYTE_ADDRESS_OF_APPEND    = 0x5, /* 8   */
+       OSD_ATTR_CC_CHANGE_IN_USED_CAPACITY            = 0x6, /* 8   */
+};
+
+/*TBD: osdv1_current_command_attributes_page */
+
+struct osdv2_current_command_attributes_page {
+       struct osd_attr_page_header hdr;  /* id=0xFFFFFFFE, size=0x44 */
+       u8 response_integrity_check_value[OSD_CRYPTO_KEYID_SIZE];
+       u8 object_type;
+       u8 reserved[3];
+       __be64 partition_id;
+       __be64 object_id;
+       __be64 starting_byte_address_of_append;
+       __be64 change_in_used_capacity;
+};
+
 #endif /*ndef __OSD_ATTRIBUTES_H__*/
index b24d9616eb469f2afbcdd02f32dbdd9cae6eae3d..02bd9f7163570512e19c0e98e17295a5f47561e8 100644 (file)
@@ -18,6 +18,7 @@
 #include "osd_types.h"
 
 #include <linux/blkdev.h>
+#include <scsi/scsi_device.h>
 
 /* Note: "NI" in comments below means "Not Implemented yet" */
 
@@ -47,6 +48,7 @@ enum osd_std_version {
  */
 struct osd_dev {
        struct scsi_device *scsi_device;
+       struct file *file;
        unsigned def_timeout;
 
 #ifdef OSD_VER1_SUPPORT
@@ -69,6 +71,10 @@ void osd_dev_fini(struct osd_dev *od);
 
 /* some hi level device operations */
 int osd_auto_detect_ver(struct osd_dev *od, void *caps);    /* GFP_KERNEL */
+static inline struct request_queue *osd_request_queue(struct osd_dev *od)
+{
+       return od->scsi_device->request_queue;
+}
 
 /* we might want to use function vector in the future */
 static inline void osd_dev_set_ver(struct osd_dev *od, enum osd_std_version v)
@@ -363,7 +369,9 @@ void osd_req_create_object(struct osd_request *or, struct osd_obj_id *);
 void osd_req_remove_object(struct osd_request *or, struct osd_obj_id *);
 
 void osd_req_write(struct osd_request *or,
-       const struct osd_obj_id *, struct bio *data_out, u64 offset);
+       const struct osd_obj_id *obj, u64 offset, struct bio *bio, u64 len);
+int osd_req_write_kern(struct osd_request *or,
+       const struct osd_obj_id *obj, u64 offset, void *buff, u64 len);
 void osd_req_append(struct osd_request *or,
        const struct osd_obj_id *, struct bio *data_out);/* NI */
 void osd_req_create_write(struct osd_request *or,
@@ -378,7 +386,9 @@ void osd_req_flush_object(struct osd_request *or,
        /*V2*/ u64 offset, /*V2*/ u64 len);
 
 void osd_req_read(struct osd_request *or,
-       const struct osd_obj_id *, struct bio *data_in, u64 offset);
+       const struct osd_obj_id *obj, u64 offset, struct bio *bio, u64 len);
+int osd_req_read_kern(struct osd_request *or,
+       const struct osd_obj_id *obj, u64 offset, void *buff, u64 len);
 
 /*
  * Root/Partition/Collection/Object Attributes commands
index 62b2ab8c69d45318ba93419184b608348928d5e2..2cc8e8b1cc19da61757676325ab3ae9cd9050e2d 100644 (file)
@@ -303,7 +303,15 @@ enum osd_service_actions {
        OSD_ACT_V2(REMOVE_MEMBER_OBJECTS,       0x21)
        OSD_ACT_V2(GET_MEMBER_ATTRIBUTES,       0x22)
        OSD_ACT_V2(SET_MEMBER_ATTRIBUTES,       0x23)
+
+       OSD_ACT_V2(CREATE_CLONE,                0x28)
+       OSD_ACT_V2(CREATE_SNAPSHOT,             0x29)
+       OSD_ACT_V2(DETACH_CLONE,                0x2A)
+       OSD_ACT_V2(REFRESH_SNAPSHOT_CLONE,      0x2B)
+       OSD_ACT_V2(RESTORE_PARTITION_FROM_SNAPSHOT, 0x2C)
+
        OSD_ACT_V2(READ_MAP,                    0x31)
+       OSD_ACT_V2(READ_MAPS_COMPARE,           0x32)
 
        OSD_ACT_V1_V2(PERFORM_SCSI_COMMAND,     0x8F7E, 0x8F7C)
        OSD_ACT_V1_V2(SCSI_TASK_MANAGEMENT,     0x8F7F, 0x8F7D)
index 457588e1119bd4cf3c429ffe6cea6aa8ded3deff..349c7f30720d8f652c79b1db0251f3a44180505a 100644 (file)
@@ -126,12 +126,14 @@ struct iscsi_transport {
                               int *index, int *age);
 
        void (*session_recovery_timedout) (struct iscsi_cls_session *session);
-       struct iscsi_endpoint *(*ep_connect) (struct sockaddr *dst_addr,
+       struct iscsi_endpoint *(*ep_connect) (struct Scsi_Host *shost,
+                                             struct sockaddr *dst_addr,
                                              int non_blocking);
        int (*ep_poll) (struct iscsi_endpoint *ep, int timeout_ms);
        void (*ep_disconnect) (struct iscsi_endpoint *ep);
        int (*tgt_dscvr) (struct Scsi_Host *shost, enum iscsi_tgt_dscvr type,
                          uint32_t enable, struct sockaddr *dst_addr);
+       int (*set_path) (struct Scsi_Host *shost, struct iscsi_path *params);
 };
 
 /*
@@ -148,6 +150,10 @@ extern void iscsi_conn_error_event(struct iscsi_cls_conn *conn,
 extern int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
                          char *data, uint32_t data_size);
 
+extern int iscsi_offload_mesg(struct Scsi_Host *shost,
+                             struct iscsi_transport *transport, uint32_t type,
+                             char *data, uint16_t data_size);
+
 struct iscsi_cls_conn {
        struct list_head conn_list;     /* item in connlist */
        void *dd_data;                  /* LLD private data */
index 6add80fc251200ef9a99e0e576a433e29f9a1b7a..82aed3f47534f3d47d53930e4869c9285de5652d 100644 (file)
@@ -255,6 +255,7 @@ typedef int __bitwise snd_pcm_subformat_t;
 #define SNDRV_PCM_INFO_HALF_DUPLEX     0x00100000      /* only half duplex */
 #define SNDRV_PCM_INFO_JOINT_DUPLEX    0x00200000      /* playback and capture stream are somewhat correlated */
 #define SNDRV_PCM_INFO_SYNC_START      0x00400000      /* pcm support some kind of sync go */
+#define SNDRV_PCM_INFO_FIFO_IN_FRAMES  0x80000000      /* internal kernel flag - FIFO size is in frames */
 
 typedef int __bitwise snd_pcm_state_t;
 #define        SNDRV_PCM_STATE_OPEN            ((__force snd_pcm_state_t) 0) /* stream is open */
index 3dea79829acc0c53673533d421aa9ca15f3b9c06..309cb9659a05907576fb933b822b100afd3e56bb 100644 (file)
@@ -300,19 +300,10 @@ int snd_card_create(int idx, const char *id,
                    struct module *module, int extra_size,
                    struct snd_card **card_ret);
 
-static inline __deprecated
-struct snd_card *snd_card_new(int idx, const char *id,
-                             struct module *module, int extra_size)
-{
-       struct snd_card *card;
-       if (snd_card_create(idx, id, module, extra_size, &card) < 0)
-               return NULL;
-       return card;
-}
-
 int snd_card_disconnect(struct snd_card *card);
 int snd_card_free(struct snd_card *card);
 int snd_card_free_when_closed(struct snd_card *card);
+void snd_card_set_id(struct snd_card *card, const char *id);
 int snd_card_register(struct snd_card *card);
 int snd_card_info_init(void);
 int snd_card_info_done(void);
diff --git a/include/sound/driver.h b/include/sound/driver.h
deleted file mode 100644 (file)
index f035943..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#warning "This file is deprecated"
index c172968916170b3e3c4e42aa1793b729f21e3670..23893523dc8ce16023cc347dcd3672f3c6be4033 100644 (file)
@@ -98,6 +98,7 @@ struct snd_pcm_ops {
 #define SNDRV_PCM_IOCTL1_INFO          1
 #define SNDRV_PCM_IOCTL1_CHANNEL_INFO  2
 #define SNDRV_PCM_IOCTL1_GSTATE                3
+#define SNDRV_PCM_IOCTL1_FIFO_SIZE     4
 
 #define SNDRV_PCM_TRIGGER_STOP         0
 #define SNDRV_PCM_TRIGGER_START                1
@@ -270,6 +271,7 @@ struct snd_pcm_runtime {
        snd_pcm_uframes_t hw_ptr_base;  /* Position at buffer restart */
        snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */
        unsigned long hw_ptr_jiffies;   /* Time when hw_ptr is updated */
+       snd_pcm_sframes_t delay;        /* extra delay; typically FIFO size */
 
        /* -- HW params -- */
        snd_pcm_access_t access;        /* access mode */
@@ -486,80 +488,6 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream);
 void snd_pcm_vma_notify_data(void *client, void *data);
 int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, struct vm_area_struct *area);
 
-#if BITS_PER_LONG >= 64
-
-static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem)
-{
-       *rem = *n % div;
-       *n /= div;
-}
-
-#elif defined(i386)
-
-static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem)
-{
-       u_int32_t low, high;
-       low = *n & 0xffffffff;
-       high = *n >> 32;
-       if (high) {
-               u_int32_t high1 = high % div;
-               high /= div;
-               asm("divl %2":"=a" (low), "=d" (*rem):"rm" (div), "a" (low), "d" (high1));
-               *n = (u_int64_t)high << 32 | low;
-       } else {
-               *n = low / div;
-               *rem = low % div;
-       }
-}
-#else
-
-static inline void divl(u_int32_t high, u_int32_t low,
-                       u_int32_t div,
-                       u_int32_t *q, u_int32_t *r)
-{
-       u_int64_t n = (u_int64_t)high << 32 | low;
-       u_int64_t d = (u_int64_t)div << 31;
-       u_int32_t q1 = 0;
-       int c = 32;
-       while (n > 0xffffffffU) {
-               q1 <<= 1;
-               if (n >= d) {
-                       n -= d;
-                       q1 |= 1;
-               }
-               d >>= 1;
-               c--;
-       }
-       q1 <<= c;
-       if (n) {
-               low = n;
-               *q = q1 | (low / div);
-               *r = low % div;
-       } else {
-               *r = 0;
-               *q = q1;
-       }
-       return;
-}
-
-static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem)
-{
-       u_int32_t low, high;
-       low = *n & 0xffffffff;
-       high = *n >> 32;
-       if (high) {
-               u_int32_t high1 = high % div;
-               u_int32_t low1 = low;
-               high /= div;
-               divl(high1, low1, div, &low, rem);
-               *n = (u_int64_t)high << 32 | low;
-       } else {
-               *n = low / div;
-               *rem = low % div;
-       }
-}
-#endif
-
 /*
  *  PCM library
  */
index 13676472ddfcca7ace85a4bc407c66d40a746b79..352d7eee9b6d912739fecbf48ec20aef0485a0d0 100644 (file)
@@ -44,24 +44,6 @@ struct snd_pcm_substream;
 #define SND_SOC_DAIFMT_CONT            (0 << 4) /* continuous clock */
 #define SND_SOC_DAIFMT_GATED           (1 << 4) /* clock is gated */
 
-/*
- * DAI Left/Right Clocks.
- *
- * Specifies whether the DAI can support different samples for similtanious
- * playback and capture. This usually requires a seperate physical frame
- * clock for playback and capture.
- */
-#define SND_SOC_DAIFMT_SYNC            (0 << 5) /* Tx FRM = Rx FRM */
-#define SND_SOC_DAIFMT_ASYNC           (1 << 5) /* Tx FRM ~ Rx FRM */
-
-/*
- * TDM
- *
- * Time Division Multiplexing. Allows PCM data to be multplexed with other
- * data on the DAI.
- */
-#define SND_SOC_DAIFMT_TDM             (1 << 6)
-
 /*
  * DAI hardware signal inversions.
  *
@@ -96,6 +78,10 @@ struct snd_pcm_substream;
 #define SND_SOC_CLOCK_IN               0
 #define SND_SOC_CLOCK_OUT              1
 
+#define SND_SOC_STD_AC97_FMTS (SNDRV_PCM_FMTBIT_S16_LE |\
+                               SNDRV_PCM_FMTBIT_S32_LE |\
+                               SNDRV_PCM_FMTBIT_S32_BE)
+
 struct snd_soc_dai_ops;
 struct snd_soc_dai;
 struct snd_ac97_bus_ops;
@@ -208,6 +194,7 @@ struct snd_soc_dai {
        /* DAI capabilities */
        struct snd_soc_pcm_stream capture;
        struct snd_soc_pcm_stream playback;
+       unsigned int symmetric_rates:1;
 
        /* DAI runtime info */
        struct snd_pcm_runtime *runtime;
@@ -219,11 +206,8 @@ struct snd_soc_dai {
        /* DAI private data */
        void *private_data;
 
-       /* parent codec/platform */
-       union {
-               struct snd_soc_codec *codec;
-               struct snd_soc_platform *platform;
-       };
+       /* parent platform */
+       struct snd_soc_platform *platform;
 
        struct list_head list;
 };
index a7def6a9a030e00671cb40ea6da2498ffca9caa2..ec8a45f9a06950f6bf1cfa0ea0d46ed3ca5200f0 100644 (file)
 #define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \
 {      .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
        .shift = wshift, .invert = winvert}
+#define SND_SOC_DAPM_DAC_E(wname, stname, wreg, wshift, winvert, \
+                          wevent, wflags)                              \
+{      .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
+       .shift = wshift, .invert = winvert, \
+       .event = wevent, .event_flags = wflags}
 #define SND_SOC_DAPM_ADC(wname, stname, wreg, wshift, winvert) \
 {      .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \
        .shift = wshift, .invert = winvert}
+#define SND_SOC_DAPM_ADC_E(wname, stname, wreg, wshift, winvert, \
+                          wevent, wflags)                              \
+{      .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \
+       .shift = wshift, .invert = winvert, \
+       .event = wevent, .event_flags = wflags}
 
-/* generic register modifier widget */
+/* generic widgets */
 #define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \
 {      .id = wid, .name = wname, .kcontrols = NULL, .num_kcontrols = 0, \
        .reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \
        .on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \
        .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
+#define SND_SOC_DAPM_SUPPLY(wname, wreg, wshift, winvert, wevent, wflags) \
+{      .id = snd_soc_dapm_supply, .name = wname, .reg = wreg,  \
+       .shift = wshift, .invert = winvert, .event = wevent, \
+       .event_flags = wflags}
 
 /* dapm kcontrol types */
 #define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \
@@ -265,8 +279,6 @@ int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
 /* dapm events */
 int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream,
        int event);
-int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
-       enum snd_soc_bias_level level);
 
 /* dapm sys fs - used by the core */
 int snd_soc_dapm_sys_add(struct device *dev);
@@ -298,6 +310,7 @@ enum snd_soc_dapm_type {
        snd_soc_dapm_vmid,                      /* codec bias/vmid - to minimise pops */
        snd_soc_dapm_pre,                       /* machine specific pre widget - exec first */
        snd_soc_dapm_post,                      /* machine specific post widget - exec last */
+       snd_soc_dapm_supply,            /* power/clock supply */
 };
 
 /*
@@ -357,6 +370,8 @@ struct snd_soc_dapm_widget {
        unsigned char suspend:1;                /* was active before suspend */
        unsigned char pmdown:1;                 /* waiting for timeout */
 
+       int (*power_check)(struct snd_soc_dapm_widget *w);
+
        /* external events */
        unsigned short event_flags;             /* flags to specify event types */
        int (*event)(struct snd_soc_dapm_widget*, struct snd_kcontrol *, int);
@@ -368,6 +383,9 @@ struct snd_soc_dapm_widget {
        /* widget input and outputs */
        struct list_head sources;
        struct list_head sinks;
+
+       /* used during DAPM updates */
+       struct list_head power_list;
 };
 
 #endif
index a40bc6f316fc668b6328374315aded850a40262d..cf6111d72b17015572a131a26c4c7913378adcd0 100644 (file)
        .info = snd_soc_info_volsw, \
        .get = xhandler_get, .put = xhandler_put, \
        .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) }
+#define SOC_DOUBLE_EXT(xname, xreg, shift_left, shift_right, xmax, xinvert,\
+        xhandler_get, xhandler_put) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+       .info = snd_soc_info_volsw, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = (unsigned long)&(struct soc_mixer_control) \
+               {.reg = xreg, .shift = shift_left, .rshift = shift_right, \
+                .max = xmax, .invert = xinvert} }
 #define SOC_SINGLE_EXT_TLV(xname, xreg, xshift, xmax, xinvert,\
         xhandler_get, xhandler_put, tlv_array) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
@@ -206,10 +214,6 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
                        struct snd_soc_jack_gpio *gpios);
 #endif
 
-/* codec IO */
-#define snd_soc_read(codec, reg) codec->read(codec, reg)
-#define snd_soc_write(codec, reg, value) codec->write(codec, reg, value)
-
 /* codec register bit access */
 int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
                                unsigned short mask, unsigned short value);
@@ -331,6 +335,7 @@ struct snd_soc_codec {
        struct module *owner;
        struct mutex mutex;
        struct device *dev;
+       struct snd_soc_device *socdev;
 
        struct list_head list;
 
@@ -364,6 +369,8 @@ struct snd_soc_codec {
        enum snd_soc_bias_level bias_level;
        enum snd_soc_bias_level suspend_bias_level;
        struct delayed_work delayed_work;
+       struct list_head up_list;
+       struct list_head down_list;
 
        /* codec DAI's */
        struct snd_soc_dai *dai;
@@ -417,6 +424,12 @@ struct snd_soc_dai_link  {
        /* codec/machine specific init - e.g. add machine controls */
        int (*init)(struct snd_soc_codec *codec);
 
+       /* Symmetry requirements */
+       unsigned int symmetric_rates:1;
+
+       /* Symmetry data - only valid if symmetry is being enforced */
+       unsigned int rate;
+
        /* DAI pcm */
        struct snd_pcm *pcm;
 };
@@ -490,6 +503,19 @@ struct soc_enum {
        void *dapm;
 };
 
+/* codec IO */
+static inline unsigned int snd_soc_read(struct snd_soc_codec *codec,
+                                       unsigned int reg)
+{
+       return codec->read(codec, reg);
+}
+
+static inline unsigned int snd_soc_write(struct snd_soc_codec *codec,
+                                        unsigned int reg, unsigned int val)
+{
+       return codec->write(codec, reg, val);
+}
+
 #include <sound/soc-dai.h>
 
 #endif
diff --git a/include/sound/wm9081.h b/include/sound/wm9081.h
new file mode 100644 (file)
index 0000000..e173ddb
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * linux/sound/wm9081.h -- Platform data for WM9081
+ *
+ * Copyright 2009 Wolfson Microelectronics. PLC.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_SND_WM_9081_H
+#define __LINUX_SND_WM_9081_H
+
+struct wm9081_retune_mobile_setting {
+       const char *name;
+       unsigned int rate;
+       u16 config[20];
+};
+
+struct wm9081_retune_mobile_config {
+       struct wm9081_retune_mobile_setting *configs;
+       int num_configs;
+};
+
+#endif
index c649657e22590e74610f05d5eb904d9534d4fd2f..d3a50967c337a95622b52478f6e6382405dd6fc2 100644 (file)
@@ -936,6 +936,8 @@ config AIO
 
 config HAVE_PERF_COUNTERS
        bool
+       help
+         See tools/perf/design.txt for details.
 
 menu "Performance Counters"
 
index 5616661eac0166e584710edbfa78386ddda0bd76..f6204f712e7c639652d06cbefb70bba4df2ae7a1 100644 (file)
@@ -539,6 +539,11 @@ void __init __weak thread_info_cache_init(void)
  */
 static void __init mm_init(void)
 {
+       /*
+        * page_cgroup requires countinous pages as memmap
+        * and it's bigger than MAX_ORDER unless SPARSEMEM.
+        */
+       page_cgroup_init_flatmem();
        mem_init();
        kmem_cache_init();
        vmalloc_init();
@@ -635,6 +640,7 @@ asmlinkage void __init start_kernel(void)
                                 "enabled early\n");
        early_boot_irqs_on();
        local_irq_enable();
+       kmem_cache_init_late();
 
        /*
         * HACK ALERT! This is early. We're enabling the console before
index 1045785412305ef99ae4445c962d14b45e0aa432..065205bdd920bfd9751d72b0d0e6a7024ecbf774 100644 (file)
@@ -45,7 +45,7 @@ void handle_bad_irq(unsigned int irq, struct irq_desc *desc)
 #if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_HARDIRQS)
 static void __init init_irq_default_affinity(void)
 {
-       alloc_bootmem_cpumask_var(&irq_default_affinity);
+       alloc_cpumask_var(&irq_default_affinity, GFP_NOWAIT);
        cpumask_setall(irq_default_affinity);
 }
 #else
index e4983770913b915e85fb2906e4f9ffd766d0694f..ae1c35201cc873d697921cb2c5da95896d89493d 100644 (file)
@@ -1448,17 +1448,17 @@ int kernel_kexec(void)
                        goto Restore_console;
                }
                suspend_console();
-               error = device_suspend(PMSG_FREEZE);
+               error = dpm_suspend_start(PMSG_FREEZE);
                if (error)
                        goto Resume_console;
-               /* At this point, device_suspend() has been called,
-                * but *not* device_power_down(). We *must*
-                * device_power_down() now.  Otherwise, drivers for
+               /* At this point, dpm_suspend_start() has been called,
+                * but *not* dpm_suspend_noirq(). We *must* call
+                * dpm_suspend_noirq() now.  Otherwise, drivers for
                 * some devices (e.g. interrupt controllers) become
                 * desynchronized with the actual state of the
                 * hardware at resume time, and evil weirdness ensues.
                 */
-               error = device_power_down(PMSG_FREEZE);
+               error = dpm_suspend_noirq(PMSG_FREEZE);
                if (error)
                        goto Resume_devices;
                error = disable_nonboot_cpus();
@@ -1486,9 +1486,9 @@ int kernel_kexec(void)
                local_irq_enable();
  Enable_cpus:
                enable_nonboot_cpus();
-               device_power_up(PMSG_RESTORE);
+               dpm_resume_noirq(PMSG_RESTORE);
  Resume_devices:
-               device_resume(PMSG_RESTORE);
+               dpm_resume_end(PMSG_RESTORE);
  Resume_console:
                resume_console();
                thaw_processes();
index 35f7de00bf0db7aad5b4a34e1311e6157764d331..e4ab36ce767222becc860650e0892399ab58b96d 100644 (file)
@@ -2455,6 +2455,7 @@ SYSCALL_DEFINE3(init_module, void __user *, umod,
        mutex_lock(&module_mutex);
        /* Drop initial reference. */
        module_put(mod);
+       trim_init_extable(mod);
        module_free(mod, mod->module_init);
        mod->module_init = NULL;
        mod->init_size = 0;
index de273ec85bd2a1a9d9ac6077ddb3078f0d8d04ef..7f6912ced2bab2c957da0ef3edc6e70625560837 100644 (file)
@@ -24,9 +24,6 @@
 #include <linux/err.h>
 #include <linux/slab.h>
 
-/* We abuse the high bits of "perm" to record whether we kmalloc'ed. */
-#define KPARAM_KMALLOCED       0x80000000
-
 #if 0
 #define DEBUGP printk
 #else
@@ -220,13 +217,13 @@ int param_set_charp(const char *val, struct kernel_param *kp)
                return -ENOSPC;
        }
 
-       if (kp->perm & KPARAM_KMALLOCED)
+       if (kp->flags & KPARAM_KMALLOCED)
                kfree(*(char **)kp->arg);
 
        /* This is a hack.  We can't need to strdup in early boot, and we
         * don't need to; this mangled commandline is preserved. */
        if (slab_is_available()) {
-               kp->perm |= KPARAM_KMALLOCED;
+               kp->flags |= KPARAM_KMALLOCED;
                *(char **)kp->arg = kstrdup(val, GFP_KERNEL);
                if (!kp->arg)
                        return -ENOMEM;
@@ -241,44 +238,63 @@ int param_get_charp(char *buffer, struct kernel_param *kp)
        return sprintf(buffer, "%s", *((char **)kp->arg));
 }
 
+/* Actually could be a bool or an int, for historical reasons. */
 int param_set_bool(const char *val, struct kernel_param *kp)
 {
+       bool v;
+
        /* No equals means "set"... */
        if (!val) val = "1";
 
        /* One of =[yYnN01] */
        switch (val[0]) {
        case 'y': case 'Y': case '1':
-               *(int *)kp->arg = 1;
-               return 0;
+               v = true;
+               break;
        case 'n': case 'N': case '0':
-               *(int *)kp->arg = 0;
-               return 0;
+               v = false;
+               break;
+       default:
+               return -EINVAL;
        }
-       return -EINVAL;
+
+       if (kp->flags & KPARAM_ISBOOL)
+               *(bool *)kp->arg = v;
+       else
+               *(int *)kp->arg = v;
+       return 0;
 }
 
 int param_get_bool(char *buffer, struct kernel_param *kp)
 {
+       bool val;
+       if (kp->flags & KPARAM_ISBOOL)
+               val = *(bool *)kp->arg;
+       else
+               val = *(int *)kp->arg;
+
        /* Y and N chosen as being relatively non-coder friendly */
-       return sprintf(buffer, "%c", (*(int *)kp->arg) ? 'Y' : 'N');
+       return sprintf(buffer, "%c", val ? 'Y' : 'N');
 }
 
+/* This one must be bool. */
 int param_set_invbool(const char *val, struct kernel_param *kp)
 {
-       int boolval, ret;
+       int ret;
+       bool boolval;
        struct kernel_param dummy;
 
        dummy.arg = &boolval;
+       dummy.flags = KPARAM_ISBOOL;
        ret = param_set_bool(val, &dummy);
        if (ret == 0)
-               *(int *)kp->arg = !boolval;
+               *(bool *)kp->arg = !boolval;
        return ret;
 }
 
 int param_get_invbool(char *buffer, struct kernel_param *kp)
 {
-       return sprintf(buffer, "%c", (*(int *)kp->arg) ? 'N' : 'Y');
+       return sprintf(buffer, "%c", (*(bool *)kp->arg) ? 'N' : 'Y');
 }
 
 /* We break the rule and mangle the string. */
@@ -591,7 +607,7 @@ void destroy_params(const struct kernel_param *params, unsigned num)
        unsigned int i;
 
        for (i = 0; i < num; i++)
-               if (params[i].perm & KPARAM_KMALLOCED)
+               if (params[i].flags & KPARAM_KMALLOCED)
                        kfree(*(char **)params[i].arg);
 }
 
index ef5d8a5b245390130f76e9284f372396dfa7ddd5..29b685f551aac6aa2f5bee8e1356755ae5627001 100644 (file)
@@ -3570,12 +3570,8 @@ perf_counter_alloc(struct perf_counter_attr *attr,
        if (attr->inherit && (attr->sample_type & PERF_SAMPLE_GROUP))
                goto done;
 
-       if (attr->type == PERF_TYPE_RAW) {
-               pmu = hw_perf_counter_init(counter);
-               goto done;
-       }
-
        switch (attr->type) {
+       case PERF_TYPE_RAW:
        case PERF_TYPE_HARDWARE:
        case PERF_TYPE_HW_CACHE:
                pmu = hw_perf_counter_init(counter);
@@ -3588,6 +3584,9 @@ perf_counter_alloc(struct perf_counter_attr *attr,
        case PERF_TYPE_TRACEPOINT:
                pmu = tp_perf_counter_init(counter);
                break;
+
+       default:
+               break;
        }
 done:
        err = 0;
@@ -3614,6 +3613,85 @@ done:
        return counter;
 }
 
+static int perf_copy_attr(struct perf_counter_attr __user *uattr,
+                         struct perf_counter_attr *attr)
+{
+       int ret;
+       u32 size;
+
+       if (!access_ok(VERIFY_WRITE, uattr, PERF_ATTR_SIZE_VER0))
+               return -EFAULT;
+
+       /*
+        * zero the full structure, so that a short copy will be nice.
+        */
+       memset(attr, 0, sizeof(*attr));
+
+       ret = get_user(size, &uattr->size);
+       if (ret)
+               return ret;
+
+       if (size > PAGE_SIZE)   /* silly large */
+               goto err_size;
+
+       if (!size)              /* abi compat */
+               size = PERF_ATTR_SIZE_VER0;
+
+       if (size < PERF_ATTR_SIZE_VER0)
+               goto err_size;
+
+       /*
+        * If we're handed a bigger struct than we know of,
+        * ensure all the unknown bits are 0.
+        */
+       if (size > sizeof(*attr)) {
+               unsigned long val;
+               unsigned long __user *addr;
+               unsigned long __user *end;
+
+               addr = PTR_ALIGN((void __user *)uattr + sizeof(*attr),
+                               sizeof(unsigned long));
+               end  = PTR_ALIGN((void __user *)uattr + size,
+                               sizeof(unsigned long));
+
+               for (; addr < end; addr += sizeof(unsigned long)) {
+                       ret = get_user(val, addr);
+                       if (ret)
+                               return ret;
+                       if (val)
+                               goto err_size;
+               }
+       }
+
+       ret = copy_from_user(attr, uattr, size);
+       if (ret)
+               return -EFAULT;
+
+       /*
+        * If the type exists, the corresponding creation will verify
+        * the attr->config.
+        */
+       if (attr->type >= PERF_TYPE_MAX)
+               return -EINVAL;
+
+       if (attr->__reserved_1 || attr->__reserved_2 || attr->__reserved_3)
+               return -EINVAL;
+
+       if (attr->sample_type & ~(PERF_SAMPLE_MAX-1))
+               return -EINVAL;
+
+       if (attr->read_format & ~(PERF_FORMAT_MAX-1))
+               return -EINVAL;
+
+out:
+       return ret;
+
+err_size:
+       put_user(sizeof(*attr), &uattr->size);
+       ret = -E2BIG;
+       goto out;
+}
+
 /**
  * sys_perf_counter_open - open a performance counter, associate it to a task/cpu
  *
@@ -3623,7 +3701,7 @@ done:
  * @group_fd:          group leader counter fd
  */
 SYSCALL_DEFINE5(perf_counter_open,
-               const struct perf_counter_attr __user *, attr_uptr,
+               struct perf_counter_attr __user *, attr_uptr,
                pid_t, pid, int, cpu, int, group_fd, unsigned long, flags)
 {
        struct perf_counter *counter, *group_leader;
@@ -3639,8 +3717,9 @@ SYSCALL_DEFINE5(perf_counter_open,
        if (flags)
                return -EINVAL;
 
-       if (copy_from_user(&attr, attr_uptr, sizeof(attr)) != 0)
-               return -EFAULT;
+       ret = perf_copy_attr(attr_uptr, &attr);
+       if (ret)
+               return ret;
 
        if (!attr.exclude_kernel) {
                if (perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN))
index 23bd4daeb96b221690ba16ade909d1dd4672093f..72067cbdb37f0839819db9280c1e42b8c250f9d8 100644 (file)
@@ -116,9 +116,13 @@ config SUSPEND_FREEZER
 
          Turning OFF this setting is NOT recommended! If in doubt, say Y.
 
+config HIBERNATION_NVS
+       bool
+
 config HIBERNATION
        bool "Hibernation (aka 'suspend to disk')"
        depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
+       select HIBERNATION_NVS if HAS_IOMEM
        ---help---
          Enable the suspend to disk (STD) functionality, which is usually
          called "hibernation" in user interfaces.  STD checkpoints the
index 720ea4f781bd4896ef8922b9e55442f447967ab5..c3b81c30e5d57627f082f66a70a7b464d63c412d 100644 (file)
@@ -6,6 +6,9 @@ endif
 obj-$(CONFIG_PM)               += main.o
 obj-$(CONFIG_PM_SLEEP)         += console.o
 obj-$(CONFIG_FREEZER)          += process.o
-obj-$(CONFIG_HIBERNATION)      += swsusp.o disk.o snapshot.o swap.o user.o
+obj-$(CONFIG_SUSPEND)          += suspend.o
+obj-$(CONFIG_PM_TEST_SUSPEND)  += suspend_test.o
+obj-$(CONFIG_HIBERNATION)      += swsusp.o hibernate.o snapshot.o swap.o user.o
+obj-$(CONFIG_HIBERNATION_NVS)  += hibernate_nvs.o
 
 obj-$(CONFIG_MAGIC_SYSRQ)      += poweroff.o
similarity index 96%
rename from kernel/power/disk.c
rename to kernel/power/hibernate.c
index 5cb080e7eebd9a388d31a12425283c6b768a6e59..81d2e746489311473bb86a5428bdc16381c95fc1 100644 (file)
@@ -1,12 +1,12 @@
 /*
- * kernel/power/disk.c - Suspend-to-disk support.
+ * kernel/power/hibernate.c - Hibernation (a.k.a suspend-to-disk) support.
  *
  * Copyright (c) 2003 Patrick Mochel
  * Copyright (c) 2003 Open Source Development Lab
  * Copyright (c) 2004 Pavel Machek <pavel@suse.cz>
+ * Copyright (c) 2009 Rafael J. Wysocki, Novell Inc.
  *
  * This file is released under the GPLv2.
- *
  */
 
 #include <linux/suspend.h>
@@ -215,13 +215,13 @@ static int create_image(int platform_mode)
        if (error)
                return error;
 
-       /* At this point, device_suspend() has been called, but *not*
-        * device_power_down(). We *must* call device_power_down() now.
+       /* At this point, dpm_suspend_start() has been called, but *not*
+        * dpm_suspend_noirq(). We *must* call dpm_suspend_noirq() now.
         * Otherwise, drivers for some devices (e.g. interrupt controllers)
         * become desynchronized with the actual state of the hardware
         * at resume time, and evil weirdness ensues.
         */
-       error = device_power_down(PMSG_FREEZE);
+       error = dpm_suspend_noirq(PMSG_FREEZE);
        if (error) {
                printk(KERN_ERR "PM: Some devices failed to power down, "
                        "aborting hibernation\n");
@@ -262,7 +262,7 @@ static int create_image(int platform_mode)
 
  Power_up:
        sysdev_resume();
-       /* NOTE:  device_power_up() is just a resume() for devices
+       /* NOTE:  dpm_resume_noirq() is just a resume() for devices
         * that suspended with irqs off ... no overall powerup.
         */
 
@@ -275,7 +275,7 @@ static int create_image(int platform_mode)
  Platform_finish:
        platform_finish(platform_mode);
 
-       device_power_up(in_suspend ?
+       dpm_resume_noirq(in_suspend ?
                (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
 
        return error;
@@ -304,7 +304,7 @@ int hibernation_snapshot(int platform_mode)
                goto Close;
 
        suspend_console();
-       error = device_suspend(PMSG_FREEZE);
+       error = dpm_suspend_start(PMSG_FREEZE);
        if (error)
                goto Recover_platform;
 
@@ -315,7 +315,7 @@ int hibernation_snapshot(int platform_mode)
        /* Control returns here after successful restore */
 
  Resume_devices:
-       device_resume(in_suspend ?
+       dpm_resume_end(in_suspend ?
                (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
        resume_console();
  Close:
@@ -339,7 +339,7 @@ static int resume_target_kernel(bool platform_mode)
 {
        int error;
 
-       error = device_power_down(PMSG_QUIESCE);
+       error = dpm_suspend_noirq(PMSG_QUIESCE);
        if (error) {
                printk(KERN_ERR "PM: Some devices failed to power down, "
                        "aborting resume\n");
@@ -394,7 +394,7 @@ static int resume_target_kernel(bool platform_mode)
  Cleanup:
        platform_restore_cleanup(platform_mode);
 
-       device_power_up(PMSG_RECOVER);
+       dpm_resume_noirq(PMSG_RECOVER);
 
        return error;
 }
@@ -414,10 +414,10 @@ int hibernation_restore(int platform_mode)
 
        pm_prepare_console();
        suspend_console();
-       error = device_suspend(PMSG_QUIESCE);
+       error = dpm_suspend_start(PMSG_QUIESCE);
        if (!error) {
                error = resume_target_kernel(platform_mode);
-               device_resume(PMSG_RECOVER);
+               dpm_resume_end(PMSG_RECOVER);
        }
        resume_console();
        pm_restore_console();
@@ -447,14 +447,14 @@ int hibernation_platform_enter(void)
 
        entering_platform_hibernation = true;
        suspend_console();
-       error = device_suspend(PMSG_HIBERNATE);
+       error = dpm_suspend_start(PMSG_HIBERNATE);
        if (error) {
                if (hibernation_ops->recover)
                        hibernation_ops->recover();
                goto Resume_devices;
        }
 
-       error = device_power_down(PMSG_HIBERNATE);
+       error = dpm_suspend_noirq(PMSG_HIBERNATE);
        if (error)
                goto Resume_devices;
 
@@ -479,11 +479,11 @@ int hibernation_platform_enter(void)
  Platofrm_finish:
        hibernation_ops->finish();
 
-       device_power_up(PMSG_RESTORE);
+       dpm_suspend_noirq(PMSG_RESTORE);
 
  Resume_devices:
        entering_platform_hibernation = false;
-       device_resume(PMSG_RESTORE);
+       dpm_resume_end(PMSG_RESTORE);
        resume_console();
 
  Close:
diff --git a/kernel/power/hibernate_nvs.c b/kernel/power/hibernate_nvs.c
new file mode 100644 (file)
index 0000000..39ac698
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * linux/kernel/power/hibernate_nvs.c - Routines for handling NVS memory
+ *
+ * Copyright (C) 2008,2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/suspend.h>
+
+/*
+ * Platforms, like ACPI, may want us to save some memory used by them during
+ * hibernation and to restore the contents of this memory during the subsequent
+ * resume.  The code below implements a mechanism allowing us to do that.
+ */
+
+struct nvs_page {
+       unsigned long phys_start;
+       unsigned int size;
+       void *kaddr;
+       void *data;
+       struct list_head node;
+};
+
+static LIST_HEAD(nvs_list);
+
+/**
+ *     hibernate_nvs_register - register platform NVS memory region to save
+ *     @start - physical address of the region
+ *     @size - size of the region
+ *
+ *     The NVS region need not be page-aligned (both ends) and we arrange
+ *     things so that the data from page-aligned addresses in this region will
+ *     be copied into separate RAM pages.
+ */
+int hibernate_nvs_register(unsigned long start, unsigned long size)
+{
+       struct nvs_page *entry, *next;
+
+       while (size > 0) {
+               unsigned int nr_bytes;
+
+               entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
+               if (!entry)
+                       goto Error;
+
+               list_add_tail(&entry->node, &nvs_list);
+               entry->phys_start = start;
+               nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
+               entry->size = (size < nr_bytes) ? size : nr_bytes;
+
+               start += entry->size;
+               size -= entry->size;
+       }
+       return 0;
+
+ Error:
+       list_for_each_entry_safe(entry, next, &nvs_list, node) {
+               list_del(&entry->node);
+               kfree(entry);
+       }
+       return -ENOMEM;
+}
+
+/**
+ *     hibernate_nvs_free - free data pages allocated for saving NVS regions
+ */
+void hibernate_nvs_free(void)
+{
+       struct nvs_page *entry;
+
+       list_for_each_entry(entry, &nvs_list, node)
+               if (entry->data) {
+                       free_page((unsigned long)entry->data);
+                       entry->data = NULL;
+                       if (entry->kaddr) {
+                               iounmap(entry->kaddr);
+                               entry->kaddr = NULL;
+                       }
+               }
+}
+
+/**
+ *     hibernate_nvs_alloc - allocate memory necessary for saving NVS regions
+ */
+int hibernate_nvs_alloc(void)
+{
+       struct nvs_page *entry;
+
+       list_for_each_entry(entry, &nvs_list, node) {
+               entry->data = (void *)__get_free_page(GFP_KERNEL);
+               if (!entry->data) {
+                       hibernate_nvs_free();
+                       return -ENOMEM;
+               }
+       }
+       return 0;
+}
+
+/**
+ *     hibernate_nvs_save - save NVS memory regions
+ */
+void hibernate_nvs_save(void)
+{
+       struct nvs_page *entry;
+
+       printk(KERN_INFO "PM: Saving platform NVS memory\n");
+
+       list_for_each_entry(entry, &nvs_list, node)
+               if (entry->data) {
+                       entry->kaddr = ioremap(entry->phys_start, entry->size);
+                       memcpy(entry->data, entry->kaddr, entry->size);
+               }
+}
+
+/**
+ *     hibernate_nvs_restore - restore NVS memory regions
+ *
+ *     This function is going to be called with interrupts disabled, so it
+ *     cannot iounmap the virtual addresses used to access the NVS region.
+ */
+void hibernate_nvs_restore(void)
+{
+       struct nvs_page *entry;
+
+       printk(KERN_INFO "PM: Restoring platform NVS memory\n");
+
+       list_for_each_entry(entry, &nvs_list, node)
+               if (entry->data)
+                       memcpy(entry->kaddr, entry->data, entry->size);
+}
index 868028280d13474811810098cfa0f1923d212b32..f710e36930cc33ecceac7f5455de6d86fedbd718 100644 (file)
@@ -8,20 +8,9 @@
  *
  */
 
-#include <linux/module.h>
-#include <linux/suspend.h>
 #include <linux/kobject.h>
 #include <linux/string.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/kmod.h>
-#include <linux/init.h>
-#include <linux/console.h>
-#include <linux/cpu.h>
 #include <linux/resume-trace.h>
-#include <linux/freezer.h>
-#include <linux/vmstat.h>
-#include <linux/syscalls.h>
 
 #include "power.h"
 
@@ -119,373 +108,6 @@ power_attr(pm_test);
 
 #endif /* CONFIG_PM_SLEEP */
 
-#ifdef CONFIG_SUSPEND
-
-static int suspend_test(int level)
-{
-#ifdef CONFIG_PM_DEBUG
-       if (pm_test_level == level) {
-               printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n");
-               mdelay(5000);
-               return 1;
-       }
-#endif /* !CONFIG_PM_DEBUG */
-       return 0;
-}
-
-#ifdef CONFIG_PM_TEST_SUSPEND
-
-/*
- * We test the system suspend code by setting an RTC wakealarm a short
- * time in the future, then suspending.  Suspending the devices won't
- * normally take long ... some systems only need a few milliseconds.
- *
- * The time it takes is system-specific though, so when we test this
- * during system bootup we allow a LOT of time.
- */
-#define TEST_SUSPEND_SECONDS   5
-
-static unsigned long suspend_test_start_time;
-
-static void suspend_test_start(void)
-{
-       /* FIXME Use better timebase than "jiffies", ideally a clocksource.
-        * What we want is a hardware counter that will work correctly even
-        * during the irqs-are-off stages of the suspend/resume cycle...
-        */
-       suspend_test_start_time = jiffies;
-}
-
-static void suspend_test_finish(const char *label)
-{
-       long nj = jiffies - suspend_test_start_time;
-       unsigned msec;
-
-       msec = jiffies_to_msecs(abs(nj));
-       pr_info("PM: %s took %d.%03d seconds\n", label,
-                       msec / 1000, msec % 1000);
-
-       /* Warning on suspend means the RTC alarm period needs to be
-        * larger -- the system was sooo slooowwww to suspend that the
-        * alarm (should have) fired before the system went to sleep!
-        *
-        * Warning on either suspend or resume also means the system
-        * has some performance issues.  The stack dump of a WARN_ON
-        * is more likely to get the right attention than a printk...
-        */
-       WARN(msec > (TEST_SUSPEND_SECONDS * 1000), "Component: %s\n", label);
-}
-
-#else
-
-static void suspend_test_start(void)
-{
-}
-
-static void suspend_test_finish(const char *label)
-{
-}
-
-#endif
-
-/* This is just an arbitrary number */
-#define FREE_PAGE_NUMBER (100)
-
-static struct platform_suspend_ops *suspend_ops;
-
-/**
- *     suspend_set_ops - Set the global suspend method table.
- *     @ops:   Pointer to ops structure.
- */
-
-void suspend_set_ops(struct platform_suspend_ops *ops)
-{
-       mutex_lock(&pm_mutex);
-       suspend_ops = ops;
-       mutex_unlock(&pm_mutex);
-}
-
-/**
- * suspend_valid_only_mem - generic memory-only valid callback
- *
- * Platform drivers that implement mem suspend only and only need
- * to check for that in their .valid callback can use this instead
- * of rolling their own .valid callback.
- */
-int suspend_valid_only_mem(suspend_state_t state)
-{
-       return state == PM_SUSPEND_MEM;
-}
-
-/**
- *     suspend_prepare - Do prep work before entering low-power state.
- *
- *     This is common code that is called for each state that we're entering.
- *     Run suspend notifiers, allocate a console and stop all processes.
- */
-static int suspend_prepare(void)
-{
-       int error;
-       unsigned int free_pages;
-
-       if (!suspend_ops || !suspend_ops->enter)
-               return -EPERM;
-
-       pm_prepare_console();
-
-       error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
-       if (error)
-               goto Finish;
-
-       error = usermodehelper_disable();
-       if (error)
-               goto Finish;
-
-       if (suspend_freeze_processes()) {
-               error = -EAGAIN;
-               goto Thaw;
-       }
-
-       free_pages = global_page_state(NR_FREE_PAGES);
-       if (free_pages < FREE_PAGE_NUMBER) {
-               pr_debug("PM: free some memory\n");
-               shrink_all_memory(FREE_PAGE_NUMBER - free_pages);
-               if (nr_free_pages() < FREE_PAGE_NUMBER) {
-                       error = -ENOMEM;
-                       printk(KERN_ERR "PM: No enough memory\n");
-               }
-       }
-       if (!error)
-               return 0;
-
- Thaw:
-       suspend_thaw_processes();
-       usermodehelper_enable();
- Finish:
-       pm_notifier_call_chain(PM_POST_SUSPEND);
-       pm_restore_console();
-       return error;
-}
-
-/* default implementation */
-void __attribute__ ((weak)) arch_suspend_disable_irqs(void)
-{
-       local_irq_disable();
-}
-
-/* default implementation */
-void __attribute__ ((weak)) arch_suspend_enable_irqs(void)
-{
-       local_irq_enable();
-}
-
-/**
- *     suspend_enter - enter the desired system sleep state.
- *     @state:         state to enter
- *
- *     This function should be called after devices have been suspended.
- */
-static int suspend_enter(suspend_state_t state)
-{
-       int error;
-
-       if (suspend_ops->prepare) {
-               error = suspend_ops->prepare();
-               if (error)
-                       return error;
-       }
-
-       error = device_power_down(PMSG_SUSPEND);
-       if (error) {
-               printk(KERN_ERR "PM: Some devices failed to power down\n");
-               goto Platfrom_finish;
-       }
-
-       if (suspend_ops->prepare_late) {
-               error = suspend_ops->prepare_late();
-               if (error)
-                       goto Power_up_devices;
-       }
-
-       if (suspend_test(TEST_PLATFORM))
-               goto Platform_wake;
-
-       error = disable_nonboot_cpus();
-       if (error || suspend_test(TEST_CPUS))
-               goto Enable_cpus;
-
-       arch_suspend_disable_irqs();
-       BUG_ON(!irqs_disabled());
-
-       error = sysdev_suspend(PMSG_SUSPEND);
-       if (!error) {
-               if (!suspend_test(TEST_CORE))
-                       error = suspend_ops->enter(state);
-               sysdev_resume();
-       }
-
-       arch_suspend_enable_irqs();
-       BUG_ON(irqs_disabled());
-
- Enable_cpus:
-       enable_nonboot_cpus();
-
- Platform_wake:
-       if (suspend_ops->wake)
-               suspend_ops->wake();
-
- Power_up_devices:
-       device_power_up(PMSG_RESUME);
-
- Platfrom_finish:
-       if (suspend_ops->finish)
-               suspend_ops->finish();
-
-       return error;
-}
-
-/**
- *     suspend_devices_and_enter - suspend devices and enter the desired system
- *                                 sleep state.
- *     @state:           state to enter
- */
-int suspend_devices_and_enter(suspend_state_t state)
-{
-       int error;
-
-       if (!suspend_ops)
-               return -ENOSYS;
-
-       if (suspend_ops->begin) {
-               error = suspend_ops->begin(state);
-               if (error)
-                       goto Close;
-       }
-       suspend_console();
-       suspend_test_start();
-       error = device_suspend(PMSG_SUSPEND);
-       if (error) {
-               printk(KERN_ERR "PM: Some devices failed to suspend\n");
-               goto Recover_platform;
-       }
-       suspend_test_finish("suspend devices");
-       if (suspend_test(TEST_DEVICES))
-               goto Recover_platform;
-
-       suspend_enter(state);
-
- Resume_devices:
-       suspend_test_start();
-       device_resume(PMSG_RESUME);
-       suspend_test_finish("resume devices");
-       resume_console();
- Close:
-       if (suspend_ops->end)
-               suspend_ops->end();
-       return error;
-
- Recover_platform:
-       if (suspend_ops->recover)
-               suspend_ops->recover();
-       goto Resume_devices;
-}
-
-/**
- *     suspend_finish - Do final work before exiting suspend sequence.
- *
- *     Call platform code to clean up, restart processes, and free the 
- *     console that we've allocated. This is not called for suspend-to-disk.
- */
-static void suspend_finish(void)
-{
-       suspend_thaw_processes();
-       usermodehelper_enable();
-       pm_notifier_call_chain(PM_POST_SUSPEND);
-       pm_restore_console();
-}
-
-
-
-
-static const char * const pm_states[PM_SUSPEND_MAX] = {
-       [PM_SUSPEND_STANDBY]    = "standby",
-       [PM_SUSPEND_MEM]        = "mem",
-};
-
-static inline int valid_state(suspend_state_t state)
-{
-       /* All states need lowlevel support and need to be valid
-        * to the lowlevel implementation, no valid callback
-        * implies that none are valid. */
-       if (!suspend_ops || !suspend_ops->valid || !suspend_ops->valid(state))
-               return 0;
-       return 1;
-}
-
-
-/**
- *     enter_state - Do common work of entering low-power state.
- *     @state:         pm_state structure for state we're entering.
- *
- *     Make sure we're the only ones trying to enter a sleep state. Fail
- *     if someone has beat us to it, since we don't want anything weird to
- *     happen when we wake up.
- *     Then, do the setup for suspend, enter the state, and cleaup (after
- *     we've woken up).
- */
-static int enter_state(suspend_state_t state)
-{
-       int error;
-
-       if (!valid_state(state))
-               return -ENODEV;
-
-       if (!mutex_trylock(&pm_mutex))
-               return -EBUSY;
-
-       printk(KERN_INFO "PM: Syncing filesystems ... ");
-       sys_sync();
-       printk("done.\n");
-
-       pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
-       error = suspend_prepare();
-       if (error)
-               goto Unlock;
-
-       if (suspend_test(TEST_FREEZER))
-               goto Finish;
-
-       pr_debug("PM: Entering %s sleep\n", pm_states[state]);
-       error = suspend_devices_and_enter(state);
-
- Finish:
-       pr_debug("PM: Finishing wakeup.\n");
-       suspend_finish();
- Unlock:
-       mutex_unlock(&pm_mutex);
-       return error;
-}
-
-
-/**
- *     pm_suspend - Externally visible function for suspending system.
- *     @state:         Enumerated value of state to enter.
- *
- *     Determine whether or not value is within range, get state 
- *     structure, and enter (above).
- */
-
-int pm_suspend(suspend_state_t state)
-{
-       if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)
-               return enter_state(state);
-       return -EINVAL;
-}
-
-EXPORT_SYMBOL(pm_suspend);
-
-#endif /* CONFIG_SUSPEND */
-
 struct kobject *power_kobj;
 
 /**
@@ -498,7 +120,6 @@ struct kobject *power_kobj;
  *     store() accepts one of those strings, translates it into the 
  *     proper enumerated value, and initiates a suspend transition.
  */
-
 static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
                          char *buf)
 {
@@ -596,7 +217,6 @@ static struct attribute_group attr_group = {
        .attrs = g,
 };
 
-
 static int __init pm_init(void)
 {
        power_kobj = kobject_create_and_add("power", NULL);
@@ -606,144 +226,3 @@ static int __init pm_init(void)
 }
 
 core_initcall(pm_init);
-
-
-#ifdef CONFIG_PM_TEST_SUSPEND
-
-#include <linux/rtc.h>
-
-/*
- * To test system suspend, we need a hands-off mechanism to resume the
- * system.  RTCs wake alarms are a common self-contained mechanism.
- */
-
-static void __init test_wakealarm(struct rtc_device *rtc, suspend_state_t state)
-{
-       static char err_readtime[] __initdata =
-               KERN_ERR "PM: can't read %s time, err %d\n";
-       static char err_wakealarm [] __initdata =
-               KERN_ERR "PM: can't set %s wakealarm, err %d\n";
-       static char err_suspend[] __initdata =
-               KERN_ERR "PM: suspend test failed, error %d\n";
-       static char info_test[] __initdata =
-               KERN_INFO "PM: test RTC wakeup from '%s' suspend\n";
-
-       unsigned long           now;
-       struct rtc_wkalrm       alm;
-       int                     status;
-
-       /* this may fail if the RTC hasn't been initialized */
-       status = rtc_read_time(rtc, &alm.time);
-       if (status < 0) {
-               printk(err_readtime, dev_name(&rtc->dev), status);
-               return;
-       }
-       rtc_tm_to_time(&alm.time, &now);
-
-       memset(&alm, 0, sizeof alm);
-       rtc_time_to_tm(now + TEST_SUSPEND_SECONDS, &alm.time);
-       alm.enabled = true;
-
-       status = rtc_set_alarm(rtc, &alm);
-       if (status < 0) {
-               printk(err_wakealarm, dev_name(&rtc->dev), status);
-               return;
-       }
-
-       if (state == PM_SUSPEND_MEM) {
-               printk(info_test, pm_states[state]);
-               status = pm_suspend(state);
-               if (status == -ENODEV)
-                       state = PM_SUSPEND_STANDBY;
-       }
-       if (state == PM_SUSPEND_STANDBY) {
-               printk(info_test, pm_states[state]);
-               status = pm_suspend(state);
-       }
-       if (status < 0)
-               printk(err_suspend, status);
-
-       /* Some platforms can't detect that the alarm triggered the
-        * wakeup, or (accordingly) disable it after it afterwards.
-        * It's supposed to give oneshot behavior; cope.
-        */
-       alm.enabled = false;
-       rtc_set_alarm(rtc, &alm);
-}
-
-static int __init has_wakealarm(struct device *dev, void *name_ptr)
-{
-       struct rtc_device *candidate = to_rtc_device(dev);
-
-       if (!candidate->ops->set_alarm)
-               return 0;
-       if (!device_may_wakeup(candidate->dev.parent))
-               return 0;
-
-       *(const char **)name_ptr = dev_name(dev);
-       return 1;
-}
-
-/*
- * Kernel options like "test_suspend=mem" force suspend/resume sanity tests
- * at startup time.  They're normally disabled, for faster boot and because
- * we can't know which states really work on this particular system.
- */
-static suspend_state_t test_state __initdata = PM_SUSPEND_ON;
-
-static char warn_bad_state[] __initdata =
-       KERN_WARNING "PM: can't test '%s' suspend state\n";
-
-static int __init setup_test_suspend(char *value)
-{
-       unsigned i;
-
-       /* "=mem" ==> "mem" */
-       value++;
-       for (i = 0; i < PM_SUSPEND_MAX; i++) {
-               if (!pm_states[i])
-                       continue;
-               if (strcmp(pm_states[i], value) != 0)
-                       continue;
-               test_state = (__force suspend_state_t) i;
-               return 0;
-       }
-       printk(warn_bad_state, value);
-       return 0;
-}
-__setup("test_suspend", setup_test_suspend);
-
-static int __init test_suspend(void)
-{
-       static char             warn_no_rtc[] __initdata =
-               KERN_WARNING "PM: no wakealarm-capable RTC driver is ready\n";
-
-       char                    *pony = NULL;
-       struct rtc_device       *rtc = NULL;
-
-       /* PM is initialized by now; is that state testable? */
-       if (test_state == PM_SUSPEND_ON)
-               goto done;
-       if (!valid_state(test_state)) {
-               printk(warn_bad_state, pm_states[test_state]);
-               goto done;
-       }
-
-       /* RTCs have initialized by now too ... can we use one? */
-       class_find_device(rtc_class, NULL, &pony, has_wakealarm);
-       if (pony)
-               rtc = rtc_class_open(pony);
-       if (!rtc) {
-               printk(warn_no_rtc);
-               goto done;
-       }
-
-       /* go for it */
-       test_wakealarm(rtc, test_state);
-       rtc_class_close(rtc);
-done:
-       return 0;
-}
-late_initcall(test_suspend);
-
-#endif /* CONFIG_PM_TEST_SUSPEND */
index 46b5ec7a3afb243050d04816990780966bab69bc..26d5a26f82e35fe722f5d895e2814f4341ca1ea4 100644 (file)
@@ -45,7 +45,7 @@ static inline char *check_image_kernel(struct swsusp_info *info)
  */
 #define SPARE_PAGES    ((1024 * 1024) >> PAGE_SHIFT)
 
-/* kernel/power/disk.c */
+/* kernel/power/hibernate.c */
 extern int hibernation_snapshot(int platform_mode);
 extern int hibernation_restore(int platform_mode);
 extern int hibernation_platform_enter(void);
@@ -74,7 +74,7 @@ extern asmlinkage int swsusp_arch_resume(void);
 
 extern int create_basic_memory_bitmaps(void);
 extern void free_basic_memory_bitmaps(void);
-extern unsigned int count_data_pages(void);
+extern int swsusp_shrink_memory(void);
 
 /**
  *     Auxiliary structure used for reading the snapshot image data and
@@ -147,9 +147,8 @@ extern int swsusp_swap_in_use(void);
  */
 #define SF_PLATFORM_MODE       1
 
-/* kernel/power/disk.c */
+/* kernel/power/hibernate.c */
 extern int swsusp_check(void);
-extern int swsusp_shrink_memory(void);
 extern void swsusp_free(void);
 extern int swsusp_read(unsigned int *flags_p);
 extern int swsusp_write(unsigned int flags);
@@ -161,22 +160,36 @@ extern void swsusp_show_speed(struct timeval *, struct timeval *,
                                unsigned int, char *);
 
 #ifdef CONFIG_SUSPEND
-/* kernel/power/main.c */
+/* kernel/power/suspend.c */
+extern const char *const pm_states[];
+
+extern bool valid_state(suspend_state_t state);
 extern int suspend_devices_and_enter(suspend_state_t state);
+extern int enter_state(suspend_state_t state);
 #else /* !CONFIG_SUSPEND */
 static inline int suspend_devices_and_enter(suspend_state_t state)
 {
        return -ENOSYS;
 }
+static inline int enter_state(suspend_state_t state) { return -ENOSYS; }
+static inline bool valid_state(suspend_state_t state) { return false; }
 #endif /* !CONFIG_SUSPEND */
 
+#ifdef CONFIG_PM_TEST_SUSPEND
+/* kernel/power/suspend_test.c */
+extern void suspend_test_start(void);
+extern void suspend_test_finish(const char *label);
+#else /* !CONFIG_PM_TEST_SUSPEND */
+static inline void suspend_test_start(void) {}
+static inline void suspend_test_finish(const char *label) {}
+#endif /* !CONFIG_PM_TEST_SUSPEND */
+
 #ifdef CONFIG_PM_SLEEP
 /* kernel/power/main.c */
 extern int pm_notifier_call_chain(unsigned long val);
 #endif
 
 #ifdef CONFIG_HIGHMEM
-unsigned int count_highmem_pages(void);
 int restore_highmem(void);
 #else
 static inline unsigned int count_highmem_pages(void) { return 0; }
index 33e2e4a819f97014a6e70e5f06d52e246ba783b3..523a451b45d37404f3ca1c8e0b59d9c31f45b49a 100644 (file)
@@ -39,6 +39,14 @@ static int swsusp_page_is_free(struct page *);
 static void swsusp_set_page_forbidden(struct page *);
 static void swsusp_unset_page_forbidden(struct page *);
 
+/*
+ * Preferred image size in bytes (tunable via /sys/power/image_size).
+ * When it is set to N, swsusp will do its best to ensure the image
+ * size will not exceed N bytes, but if that is impossible, it will
+ * try to create the smallest image possible.
+ */
+unsigned long image_size = 500 * 1024 * 1024;
+
 /* List of PBEs needed for restoring the pages that were allocated before
  * the suspend and included in the suspend image, but have also been
  * allocated by the "resume" kernel, so their contents cannot be written
@@ -840,7 +848,7 @@ static struct page *saveable_highmem_page(struct zone *zone, unsigned long pfn)
  *     pages.
  */
 
-unsigned int count_highmem_pages(void)
+static unsigned int count_highmem_pages(void)
 {
        struct zone *zone;
        unsigned int n = 0;
@@ -902,7 +910,7 @@ static struct page *saveable_page(struct zone *zone, unsigned long pfn)
  *     pages.
  */
 
-unsigned int count_data_pages(void)
+static unsigned int count_data_pages(void)
 {
        struct zone *zone;
        unsigned long pfn, max_zone_pfn;
@@ -1058,6 +1066,74 @@ void swsusp_free(void)
        buffer = NULL;
 }
 
+/**
+ *     swsusp_shrink_memory -  Try to free as much memory as needed
+ *
+ *     ... but do not OOM-kill anyone
+ *
+ *     Notice: all userland should be stopped before it is called, or
+ *     livelock is possible.
+ */
+
+#define SHRINK_BITE    10000
+static inline unsigned long __shrink_memory(long tmp)
+{
+       if (tmp > SHRINK_BITE)
+               tmp = SHRINK_BITE;
+       return shrink_all_memory(tmp);
+}
+
+int swsusp_shrink_memory(void)
+{
+       long tmp;
+       struct zone *zone;
+       unsigned long pages = 0;
+       unsigned int i = 0;
+       char *p = "-\\|/";
+       struct timeval start, stop;
+
+       printk(KERN_INFO "PM: Shrinking memory...  ");
+       do_gettimeofday(&start);
+       do {
+               long size, highmem_size;
+
+               highmem_size = count_highmem_pages();
+               size = count_data_pages() + PAGES_FOR_IO + SPARE_PAGES;
+               tmp = size;
+               size += highmem_size;
+               for_each_populated_zone(zone) {
+                       tmp += snapshot_additional_pages(zone);
+                       if (is_highmem(zone)) {
+                               highmem_size -=
+                                       zone_page_state(zone, NR_FREE_PAGES);
+                       } else {
+                               tmp -= zone_page_state(zone, NR_FREE_PAGES);
+                               tmp += zone->lowmem_reserve[ZONE_NORMAL];
+                       }
+               }
+
+               if (highmem_size < 0)
+                       highmem_size = 0;
+
+               tmp += highmem_size;
+               if (tmp > 0) {
+                       tmp = __shrink_memory(tmp);
+                       if (!tmp)
+                               return -ENOMEM;
+                       pages += tmp;
+               } else if (size > image_size / PAGE_SIZE) {
+                       tmp = __shrink_memory(size - (image_size / PAGE_SIZE));
+                       pages += tmp;
+               }
+               printk("\b%c", p[i++%4]);
+       } while (tmp > 0);
+       do_gettimeofday(&stop);
+       printk("\bdone (%lu pages freed)\n", pages);
+       swsusp_show_speed(&start, &stop, pages, "Freed");
+
+       return 0;
+}
+
 #ifdef CONFIG_HIGHMEM
 /**
   *    count_pages_for_highmem - compute the number of non-highmem pages
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
new file mode 100644 (file)
index 0000000..6f10dfc
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * kernel/power/suspend.c - Suspend to RAM and standby functionality.
+ *
+ * Copyright (c) 2003 Patrick Mochel
+ * Copyright (c) 2003 Open Source Development Lab
+ * Copyright (c) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/cpu.h>
+#include <linux/syscalls.h>
+
+#include "power.h"
+
+const char *const pm_states[PM_SUSPEND_MAX] = {
+       [PM_SUSPEND_STANDBY]    = "standby",
+       [PM_SUSPEND_MEM]        = "mem",
+};
+
+static struct platform_suspend_ops *suspend_ops;
+
+/**
+ *     suspend_set_ops - Set the global suspend method table.
+ *     @ops:   Pointer to ops structure.
+ */
+void suspend_set_ops(struct platform_suspend_ops *ops)
+{
+       mutex_lock(&pm_mutex);
+       suspend_ops = ops;
+       mutex_unlock(&pm_mutex);
+}
+
+bool valid_state(suspend_state_t state)
+{
+       /*
+        * All states need lowlevel support and need to be valid to the lowlevel
+        * implementation, no valid callback implies that none are valid.
+        */
+       return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
+}
+
+/**
+ * suspend_valid_only_mem - generic memory-only valid callback
+ *
+ * Platform drivers that implement mem suspend only and only need
+ * to check for that in their .valid callback can use this instead
+ * of rolling their own .valid callback.
+ */
+int suspend_valid_only_mem(suspend_state_t state)
+{
+       return state == PM_SUSPEND_MEM;
+}
+
+static int suspend_test(int level)
+{
+#ifdef CONFIG_PM_DEBUG
+       if (pm_test_level == level) {
+               printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n");
+               mdelay(5000);
+               return 1;
+       }
+#endif /* !CONFIG_PM_DEBUG */
+       return 0;
+}
+
+/**
+ *     suspend_prepare - Do prep work before entering low-power state.
+ *
+ *     This is common code that is called for each state that we're entering.
+ *     Run suspend notifiers, allocate a console and stop all processes.
+ */
+static int suspend_prepare(void)
+{
+       int error;
+
+       if (!suspend_ops || !suspend_ops->enter)
+               return -EPERM;
+
+       pm_prepare_console();
+
+       error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
+       if (error)
+               goto Finish;
+
+       error = usermodehelper_disable();
+       if (error)
+               goto Finish;
+
+       error = suspend_freeze_processes();
+       if (!error)
+               return 0;
+
+       suspend_thaw_processes();
+       usermodehelper_enable();
+ Finish:
+       pm_notifier_call_chain(PM_POST_SUSPEND);
+       pm_restore_console();
+       return error;
+}
+
+/* default implementation */
+void __attribute__ ((weak)) arch_suspend_disable_irqs(void)
+{
+       local_irq_disable();
+}
+
+/* default implementation */
+void __attribute__ ((weak)) arch_suspend_enable_irqs(void)
+{
+       local_irq_enable();
+}
+
+/**
+ *     suspend_enter - enter the desired system sleep state.
+ *     @state:         state to enter
+ *
+ *     This function should be called after devices have been suspended.
+ */
+static int suspend_enter(suspend_state_t state)
+{
+       int error;
+
+       if (suspend_ops->prepare) {
+               error = suspend_ops->prepare();
+               if (error)
+                       return error;
+       }
+
+       error = dpm_suspend_noirq(PMSG_SUSPEND);
+       if (error) {
+               printk(KERN_ERR "PM: Some devices failed to power down\n");
+               goto Platfrom_finish;
+       }
+
+       if (suspend_ops->prepare_late) {
+               error = suspend_ops->prepare_late();
+               if (error)
+                       goto Power_up_devices;
+       }
+
+       if (suspend_test(TEST_PLATFORM))
+               goto Platform_wake;
+
+       error = disable_nonboot_cpus();
+       if (error || suspend_test(TEST_CPUS))
+               goto Enable_cpus;
+
+       arch_suspend_disable_irqs();
+       BUG_ON(!irqs_disabled());
+
+       error = sysdev_suspend(PMSG_SUSPEND);
+       if (!error) {
+               if (!suspend_test(TEST_CORE))
+                       error = suspend_ops->enter(state);
+               sysdev_resume();
+       }
+
+       arch_suspend_enable_irqs();
+       BUG_ON(irqs_disabled());
+
+ Enable_cpus:
+       enable_nonboot_cpus();
+
+ Platform_wake:
+       if (suspend_ops->wake)
+               suspend_ops->wake();
+
+ Power_up_devices:
+       dpm_resume_noirq(PMSG_RESUME);
+
+ Platfrom_finish:
+       if (suspend_ops->finish)
+               suspend_ops->finish();
+
+       return error;
+}
+
+/**
+ *     suspend_devices_and_enter - suspend devices and enter the desired system
+ *                                 sleep state.
+ *     @state:           state to enter
+ */
+int suspend_devices_and_enter(suspend_state_t state)
+{
+       int error;
+
+       if (!suspend_ops)
+               return -ENOSYS;
+
+       if (suspend_ops->begin) {
+               error = suspend_ops->begin(state);
+               if (error)
+                       goto Close;
+       }
+       suspend_console();
+       suspend_test_start();
+       error = dpm_suspend_start(PMSG_SUSPEND);
+       if (error) {
+               printk(KERN_ERR "PM: Some devices failed to suspend\n");
+               goto Recover_platform;
+       }
+       suspend_test_finish("suspend devices");
+       if (suspend_test(TEST_DEVICES))
+               goto Recover_platform;
+
+       suspend_enter(state);
+
+ Resume_devices:
+       suspend_test_start();
+       dpm_resume_end(PMSG_RESUME);
+       suspend_test_finish("resume devices");
+       resume_console();
+ Close:
+       if (suspend_ops->end)
+               suspend_ops->end();
+       return error;
+
+ Recover_platform:
+       if (suspend_ops->recover)
+               suspend_ops->recover();
+       goto Resume_devices;
+}
+
+/**
+ *     suspend_finish - Do final work before exiting suspend sequence.
+ *
+ *     Call platform code to clean up, restart processes, and free the
+ *     console that we've allocated. This is not called for suspend-to-disk.
+ */
+static void suspend_finish(void)
+{
+       suspend_thaw_processes();
+       usermodehelper_enable();
+       pm_notifier_call_chain(PM_POST_SUSPEND);
+       pm_restore_console();
+}
+
+/**
+ *     enter_state - Do common work of entering low-power state.
+ *     @state:         pm_state structure for state we're entering.
+ *
+ *     Make sure we're the only ones trying to enter a sleep state. Fail
+ *     if someone has beat us to it, since we don't want anything weird to
+ *     happen when we wake up.
+ *     Then, do the setup for suspend, enter the state, and cleaup (after
+ *     we've woken up).
+ */
+int enter_state(suspend_state_t state)
+{
+       int error;
+
+       if (!valid_state(state))
+               return -ENODEV;
+
+       if (!mutex_trylock(&pm_mutex))
+               return -EBUSY;
+
+       printk(KERN_INFO "PM: Syncing filesystems ... ");
+       sys_sync();
+       printk("done.\n");
+
+       pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
+       error = suspend_prepare();
+       if (error)
+               goto Unlock;
+
+       if (suspend_test(TEST_FREEZER))
+               goto Finish;
+
+       pr_debug("PM: Entering %s sleep\n", pm_states[state]);
+       error = suspend_devices_and_enter(state);
+
+ Finish:
+       pr_debug("PM: Finishing wakeup.\n");
+       suspend_finish();
+ Unlock:
+       mutex_unlock(&pm_mutex);
+       return error;
+}
+
+/**
+ *     pm_suspend - Externally visible function for suspending system.
+ *     @state:         Enumerated value of state to enter.
+ *
+ *     Determine whether or not value is within range, get state
+ *     structure, and enter (above).
+ */
+int pm_suspend(suspend_state_t state)
+{
+       if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)
+               return enter_state(state);
+       return -EINVAL;
+}
+EXPORT_SYMBOL(pm_suspend);
diff --git a/kernel/power/suspend_test.c b/kernel/power/suspend_test.c
new file mode 100644 (file)
index 0000000..17d8bb1
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * kernel/power/suspend_test.c - Suspend to RAM and standby test facility.
+ *
+ * Copyright (c) 2009 Pavel Machek <pavel@ucw.cz>
+ *
+ * This file is released under the GPLv2.
+ */
+
+#include <linux/init.h>
+#include <linux/rtc.h>
+
+#include "power.h"
+
+/*
+ * We test the system suspend code by setting an RTC wakealarm a short
+ * time in the future, then suspending.  Suspending the devices won't
+ * normally take long ... some systems only need a few milliseconds.
+ *
+ * The time it takes is system-specific though, so when we test this
+ * during system bootup we allow a LOT of time.
+ */
+#define TEST_SUSPEND_SECONDS   5
+
+static unsigned long suspend_test_start_time;
+
+void suspend_test_start(void)
+{
+       /* FIXME Use better timebase than "jiffies", ideally a clocksource.
+        * What we want is a hardware counter that will work correctly even
+        * during the irqs-are-off stages of the suspend/resume cycle...
+        */
+       suspend_test_start_time = jiffies;
+}
+
+void suspend_test_finish(const char *label)
+{
+       long nj = jiffies - suspend_test_start_time;
+       unsigned msec;
+
+       msec = jiffies_to_msecs(abs(nj));
+       pr_info("PM: %s took %d.%03d seconds\n", label,
+                       msec / 1000, msec % 1000);
+
+       /* Warning on suspend means the RTC alarm period needs to be
+        * larger -- the system was sooo slooowwww to suspend that the
+        * alarm (should have) fired before the system went to sleep!
+        *
+        * Warning on either suspend or resume also means the system
+        * has some performance issues.  The stack dump of a WARN_ON
+        * is more likely to get the right attention than a printk...
+        */
+       WARN(msec > (TEST_SUSPEND_SECONDS * 1000), "Component: %s\n", label);
+}
+
+/*
+ * To test system suspend, we need a hands-off mechanism to resume the
+ * system.  RTCs wake alarms are a common self-contained mechanism.
+ */
+
+static void __init test_wakealarm(struct rtc_device *rtc, suspend_state_t state)
+{
+       static char err_readtime[] __initdata =
+               KERN_ERR "PM: can't read %s time, err %d\n";
+       static char err_wakealarm [] __initdata =
+               KERN_ERR "PM: can't set %s wakealarm, err %d\n";
+       static char err_suspend[] __initdata =
+               KERN_ERR "PM: suspend test failed, error %d\n";
+       static char info_test[] __initdata =
+               KERN_INFO "PM: test RTC wakeup from '%s' suspend\n";
+
+       unsigned long           now;
+       struct rtc_wkalrm       alm;
+       int                     status;
+
+       /* this may fail if the RTC hasn't been initialized */
+       status = rtc_read_time(rtc, &alm.time);
+       if (status < 0) {
+               printk(err_readtime, dev_name(&rtc->dev), status);
+               return;
+       }
+       rtc_tm_to_time(&alm.time, &now);
+
+       memset(&alm, 0, sizeof alm);
+       rtc_time_to_tm(now + TEST_SUSPEND_SECONDS, &alm.time);
+       alm.enabled = true;
+
+       status = rtc_set_alarm(rtc, &alm);
+       if (status < 0) {
+               printk(err_wakealarm, dev_name(&rtc->dev), status);
+               return;
+       }
+
+       if (state == PM_SUSPEND_MEM) {
+               printk(info_test, pm_states[state]);
+               status = pm_suspend(state);
+               if (status == -ENODEV)
+                       state = PM_SUSPEND_STANDBY;
+       }
+       if (state == PM_SUSPEND_STANDBY) {
+               printk(info_test, pm_states[state]);
+               status = pm_suspend(state);
+       }
+       if (status < 0)
+               printk(err_suspend, status);
+
+       /* Some platforms can't detect that the alarm triggered the
+        * wakeup, or (accordingly) disable it after it afterwards.
+        * It's supposed to give oneshot behavior; cope.
+        */
+       alm.enabled = false;
+       rtc_set_alarm(rtc, &alm);
+}
+
+static int __init has_wakealarm(struct device *dev, void *name_ptr)
+{
+       struct rtc_device *candidate = to_rtc_device(dev);
+
+       if (!candidate->ops->set_alarm)
+               return 0;
+       if (!device_may_wakeup(candidate->dev.parent))
+               return 0;
+
+       *(const char **)name_ptr = dev_name(dev);
+       return 1;
+}
+
+/*
+ * Kernel options like "test_suspend=mem" force suspend/resume sanity tests
+ * at startup time.  They're normally disabled, for faster boot and because
+ * we can't know which states really work on this particular system.
+ */
+static suspend_state_t test_state __initdata = PM_SUSPEND_ON;
+
+static char warn_bad_state[] __initdata =
+       KERN_WARNING "PM: can't test '%s' suspend state\n";
+
+static int __init setup_test_suspend(char *value)
+{
+       unsigned i;
+
+       /* "=mem" ==> "mem" */
+       value++;
+       for (i = 0; i < PM_SUSPEND_MAX; i++) {
+               if (!pm_states[i])
+                       continue;
+               if (strcmp(pm_states[i], value) != 0)
+                       continue;
+               test_state = (__force suspend_state_t) i;
+               return 0;
+       }
+       printk(warn_bad_state, value);
+       return 0;
+}
+__setup("test_suspend", setup_test_suspend);
+
+static int __init test_suspend(void)
+{
+       static char             warn_no_rtc[] __initdata =
+               KERN_WARNING "PM: no wakealarm-capable RTC driver is ready\n";
+
+       char                    *pony = NULL;
+       struct rtc_device       *rtc = NULL;
+
+       /* PM is initialized by now; is that state testable? */
+       if (test_state == PM_SUSPEND_ON)
+               goto done;
+       if (!valid_state(test_state)) {
+               printk(warn_bad_state, pm_states[test_state]);
+               goto done;
+       }
+
+       /* RTCs have initialized by now too ... can we use one? */
+       class_find_device(rtc_class, NULL, &pony, has_wakealarm);
+       if (pony)
+               rtc = rtc_class_open(pony);
+       if (!rtc) {
+               printk(warn_no_rtc);
+               goto done;
+       }
+
+       /* go for it */
+       test_wakealarm(rtc, test_state);
+       rtc_class_close(rtc);
+done:
+       return 0;
+}
+late_initcall(test_suspend);
index 78c35047586dbaf6ad4766a88e73062a56fde543..6a07f4dbf2f8b16d165a050852dbaf4a5d8f8351 100644 (file)
 
 #include "power.h"
 
-/*
- * Preferred image size in bytes (tunable via /sys/power/image_size).
- * When it is set to N, swsusp will do its best to ensure the image
- * size will not exceed N bytes, but if that is impossible, it will
- * try to create the smallest image possible.
- */
-unsigned long image_size = 500 * 1024 * 1024;
-
 int in_suspend __nosavedata = 0;
 
 /**
@@ -194,193 +186,3 @@ void swsusp_show_speed(struct timeval *start, struct timeval *stop,
                        centisecs / 100, centisecs % 100,
                        kps / 1000, (kps % 1000) / 10);
 }
-
-/**
- *     swsusp_shrink_memory -  Try to free as much memory as needed
- *
- *     ... but do not OOM-kill anyone
- *
- *     Notice: all userland should be stopped before it is called, or
- *     livelock is possible.
- */
-
-#define SHRINK_BITE    10000
-static inline unsigned long __shrink_memory(long tmp)
-{
-       if (tmp > SHRINK_BITE)
-               tmp = SHRINK_BITE;
-       return shrink_all_memory(tmp);
-}
-
-int swsusp_shrink_memory(void)
-{
-       long tmp;
-       struct zone *zone;
-       unsigned long pages = 0;
-       unsigned int i = 0;
-       char *p = "-\\|/";
-       struct timeval start, stop;
-
-       printk(KERN_INFO "PM: Shrinking memory...  ");
-       do_gettimeofday(&start);
-       do {
-               long size, highmem_size;
-
-               highmem_size = count_highmem_pages();
-               size = count_data_pages() + PAGES_FOR_IO + SPARE_PAGES;
-               tmp = size;
-               size += highmem_size;
-               for_each_populated_zone(zone) {
-                       tmp += snapshot_additional_pages(zone);
-                       if (is_highmem(zone)) {
-                               highmem_size -=
-                                       zone_page_state(zone, NR_FREE_PAGES);
-                       } else {
-                               tmp -= zone_page_state(zone, NR_FREE_PAGES);
-                               tmp += zone->lowmem_reserve[ZONE_NORMAL];
-                       }
-               }
-
-               if (highmem_size < 0)
-                       highmem_size = 0;
-
-               tmp += highmem_size;
-               if (tmp > 0) {
-                       tmp = __shrink_memory(tmp);
-                       if (!tmp)
-                               return -ENOMEM;
-                       pages += tmp;
-               } else if (size > image_size / PAGE_SIZE) {
-                       tmp = __shrink_memory(size - (image_size / PAGE_SIZE));
-                       pages += tmp;
-               }
-               printk("\b%c", p[i++%4]);
-       } while (tmp > 0);
-       do_gettimeofday(&stop);
-       printk("\bdone (%lu pages freed)\n", pages);
-       swsusp_show_speed(&start, &stop, pages, "Freed");
-
-       return 0;
-}
-
-/*
- * Platforms, like ACPI, may want us to save some memory used by them during
- * hibernation and to restore the contents of this memory during the subsequent
- * resume.  The code below implements a mechanism allowing us to do that.
- */
-
-struct nvs_page {
-       unsigned long phys_start;
-       unsigned int size;
-       void *kaddr;
-       void *data;
-       struct list_head node;
-};
-
-static LIST_HEAD(nvs_list);
-
-/**
- *     hibernate_nvs_register - register platform NVS memory region to save
- *     @start - physical address of the region
- *     @size - size of the region
- *
- *     The NVS region need not be page-aligned (both ends) and we arrange
- *     things so that the data from page-aligned addresses in this region will
- *     be copied into separate RAM pages.
- */
-int hibernate_nvs_register(unsigned long start, unsigned long size)
-{
-       struct nvs_page *entry, *next;
-
-       while (size > 0) {
-               unsigned int nr_bytes;
-
-               entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
-               if (!entry)
-                       goto Error;
-
-               list_add_tail(&entry->node, &nvs_list);
-               entry->phys_start = start;
-               nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
-               entry->size = (size < nr_bytes) ? size : nr_bytes;
-
-               start += entry->size;
-               size -= entry->size;
-       }
-       return 0;
-
- Error:
-       list_for_each_entry_safe(entry, next, &nvs_list, node) {
-               list_del(&entry->node);
-               kfree(entry);
-       }
-       return -ENOMEM;
-}
-
-/**
- *     hibernate_nvs_free - free data pages allocated for saving NVS regions
- */
-void hibernate_nvs_free(void)
-{
-       struct nvs_page *entry;
-
-       list_for_each_entry(entry, &nvs_list, node)
-               if (entry->data) {
-                       free_page((unsigned long)entry->data);
-                       entry->data = NULL;
-                       if (entry->kaddr) {
-                               iounmap(entry->kaddr);
-                               entry->kaddr = NULL;
-                       }
-               }
-}
-
-/**
- *     hibernate_nvs_alloc - allocate memory necessary for saving NVS regions
- */
-int hibernate_nvs_alloc(void)
-{
-       struct nvs_page *entry;
-
-       list_for_each_entry(entry, &nvs_list, node) {
-               entry->data = (void *)__get_free_page(GFP_KERNEL);
-               if (!entry->data) {
-                       hibernate_nvs_free();
-                       return -ENOMEM;
-               }
-       }
-       return 0;
-}
-
-/**
- *     hibernate_nvs_save - save NVS memory regions
- */
-void hibernate_nvs_save(void)
-{
-       struct nvs_page *entry;
-
-       printk(KERN_INFO "PM: Saving platform NVS memory\n");
-
-       list_for_each_entry(entry, &nvs_list, node)
-               if (entry->data) {
-                       entry->kaddr = ioremap(entry->phys_start, entry->size);
-                       memcpy(entry->data, entry->kaddr, entry->size);
-               }
-}
-
-/**
- *     hibernate_nvs_restore - restore NVS memory regions
- *
- *     This function is going to be called with interrupts disabled, so it
- *     cannot iounmap the virtual addresses used to access the NVS region.
- */
-void hibernate_nvs_restore(void)
-{
-       struct nvs_page *entry;
-
-       printk(KERN_INFO "PM: Restoring platform NVS memory\n");
-
-       list_for_each_entry(entry, &nvs_list, node)
-               if (entry->data)
-                       memcpy(entry->kaddr, entry->data, entry->size);
-}
index f04aa9664504025ad16e01f1c2c8c8da6a787545..8ec9d13140be832cd88d349248c1cd155fd8da61 100644 (file)
@@ -2192,6 +2192,7 @@ void kick_process(struct task_struct *p)
                smp_send_reschedule(cpu);
        preempt_enable();
 }
+EXPORT_SYMBOL_GPL(kick_process);
 
 /*
  * Return a low guess at the load of a migration-source cpu weighted
index 1f6edefebffe3f7fd94d0348b97d0dd6d80a9dc1..34c5c0e6222ecf289112358c44751badd60569ab 100644 (file)
@@ -93,6 +93,8 @@ obj-$(CONFIG_NLATTR) += nlattr.o
 
 obj-$(CONFIG_DMA_API_DEBUG) += dma-debug.o
 
+obj-$(CONFIG_GENERIC_CSUM) += checksum.o
+
 hostprogs-y    := gen_crc32table
 clean-files    := crc32table.h
 
diff --git a/lib/checksum.c b/lib/checksum.c
new file mode 100644 (file)
index 0000000..12e5a1c
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ *
+ * INET                An implementation of the TCP/IP protocol suite for the LINUX
+ *             operating system.  INET is implemented using the  BSD Socket
+ *             interface as the means of communication with the user level.
+ *
+ *             IP/TCP/UDP checksumming routines
+ *
+ * Authors:    Jorge Cwik, <jorge@laser.satlink.net>
+ *             Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ *             Tom May, <ftom@netcom.com>
+ *             Andreas Schwab, <schwab@issan.informatik.uni-dortmund.de>
+ *             Lots of code moved from tcp.c and ip.c; see those files
+ *             for more names.
+ *
+ * 03/02/96    Jes Sorensen, Andreas Schwab, Roman Hodek:
+ *             Fixed some nasty bugs, causing some horrible crashes.
+ *             A: At some points, the sum (%0) was used as
+ *             length-counter instead of the length counter
+ *             (%1). Thanks to Roman Hodek for pointing this out.
+ *             B: GCC seems to mess up if one uses too many
+ *             data-registers to hold input values and one tries to
+ *             specify d0 and d1 as scratch registers. Letting gcc
+ *             choose these registers itself solves the problem.
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ */
+
+/* Revised by Kenneth Albanowski for m68knommu. Basic problem: unaligned access
+ kills, so most of the assembly has to go. */
+
+#include <linux/module.h>
+#include <net/checksum.h>
+
+#include <asm/byteorder.h>
+
+static inline unsigned short from32to16(unsigned long x)
+{
+       /* add up 16-bit and 16-bit for 16+c bit */
+       x = (x & 0xffff) + (x >> 16);
+       /* add up carry.. */
+       x = (x & 0xffff) + (x >> 16);
+       return x;
+}
+
+static unsigned int do_csum(const unsigned char *buff, int len)
+{
+       int odd, count;
+       unsigned long result = 0;
+
+       if (len <= 0)
+               goto out;
+       odd = 1 & (unsigned long) buff;
+       if (odd) {
+               result = *buff;
+               len--;
+               buff++;
+       }
+       count = len >> 1;               /* nr of 16-bit words.. */
+       if (count) {
+               if (2 & (unsigned long) buff) {
+                       result += *(unsigned short *) buff;
+                       count--;
+                       len -= 2;
+                       buff += 2;
+               }
+               count >>= 1;            /* nr of 32-bit words.. */
+               if (count) {
+                       unsigned long carry = 0;
+                       do {
+                               unsigned long w = *(unsigned long *) buff;
+                               count--;
+                               buff += 4;
+                               result += carry;
+                               result += w;
+                               carry = (w > result);
+                       } while (count);
+                       result += carry;
+                       result = (result & 0xffff) + (result >> 16);
+               }
+               if (len & 2) {
+                       result += *(unsigned short *) buff;
+                       buff += 2;
+               }
+       }
+       if (len & 1)
+               result += (*buff << 8);
+       result = from32to16(result);
+       if (odd)
+               result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
+out:
+       return result;
+}
+
+/*
+ *     This is a version of ip_compute_csum() optimized for IP headers,
+ *     which always checksum on 4 octet boundaries.
+ */
+__sum16 ip_fast_csum(const void *iph, unsigned int ihl)
+{
+       return (__force __sum16)~do_csum(iph, ihl*4);
+}
+EXPORT_SYMBOL(ip_fast_csum);
+
+/*
+ * computes the checksum of a memory block at buff, length len,
+ * and adds in "sum" (32-bit)
+ *
+ * returns a 32-bit number suitable for feeding into itself
+ * or csum_tcpudp_magic
+ *
+ * this function must be called with even lengths, except
+ * for the last fragment, which may be odd
+ *
+ * it's best to have buff aligned on a 32-bit boundary
+ */
+__wsum csum_partial(const void *buff, int len, __wsum wsum)
+{
+       unsigned int sum = (__force unsigned int)wsum;
+       unsigned int result = do_csum(buff, len);
+
+       /* add in old sum, and carry.. */
+       result += sum;
+       if (sum > result)
+               result += 1;
+       return (__force __wsum)result;
+}
+EXPORT_SYMBOL(csum_partial);
+
+/*
+ * this routine is used for miscellaneous IP-like checksums, mainly
+ * in icmp.c
+ */
+__sum16 ip_compute_csum(const void *buff, int len)
+{
+       return (__force __sum16)~do_csum(buff, len);
+}
+EXPORT_SYMBOL(ip_compute_csum);
+
+/*
+ * copy from fs while checksumming, otherwise like csum_partial
+ */
+__wsum
+csum_partial_copy_from_user(const void __user *src, void *dst, int len,
+                                               __wsum sum, int *csum_err)
+{
+       int missing;
+
+       missing = __copy_from_user(dst, src, len);
+       if (missing) {
+               memset(dst + len - missing, 0, missing);
+               *csum_err = -EFAULT;
+       } else
+               *csum_err = 0;
+
+       return csum_partial(dst, len, sum);
+}
+EXPORT_SYMBOL(csum_partial_copy_from_user);
+
+/*
+ * copy from ds while checksumming, otherwise like csum_partial
+ */
+__wsum
+csum_partial_copy(const void *src, void *dst, int len, __wsum sum)
+{
+       memcpy(dst, src, len);
+       return csum_partial(dst, len, sum);
+}
+EXPORT_SYMBOL(csum_partial_copy);
+
+#ifndef csum_tcpudp_nofold
+__wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
+                       unsigned short len,
+                       unsigned short proto,
+                       __wsum sum)
+{
+       unsigned long long s = (__force u32)sum;
+
+       s += (__force u32)saddr;
+       s += (__force u32)daddr;
+#ifdef __BIG_ENDIAN
+       s += proto + len;
+#else
+       s += (proto + len) << 8;
+#endif
+       s += (s >> 32);
+       return (__force __wsum)s;
+}
+EXPORT_SYMBOL(csum_tcpudp_nofold);
+#endif
index 179c0874559569bf2626cbfc70bfcfef09474097..4cac81ec225e09af4cfd69c36d63a574a8418110 100644 (file)
@@ -39,7 +39,26 @@ void sort_extable(struct exception_table_entry *start,
        sort(start, finish - start, sizeof(struct exception_table_entry),
             cmp_ex, NULL);
 }
-#endif
+
+#ifdef CONFIG_MODULES
+/*
+ * If the exception table is sorted, any referring to the module init
+ * will be at the beginning or the end.
+ */
+void trim_init_extable(struct module *m)
+{
+       /*trim the beginning*/
+       while (m->num_exentries && within_module_init(m->extable[0].insn, m)) {
+               m->extable++;
+               m->num_exentries--;
+       }
+       /*trim the end*/
+       while (m->num_exentries &&
+               within_module_init(m->extable[m->num_exentries-1].insn, m))
+               m->num_exentries--;
+}
+#endif /* CONFIG_MODULES */
+#endif /* !ARCH_HAS_SORT_EXTABLE */
 
 #ifndef ARCH_HAS_SEARCH_EXTABLE
 /*
index 3dd4a909a1de8d0c04147c8e08c647127a386c98..11a8a10a3909e973800888da1b600338eb1d8b99 100644 (file)
@@ -47,8 +47,6 @@ static int __init alloc_node_page_cgroup(int nid)
        struct page_cgroup *base, *pc;
        unsigned long table_size;
        unsigned long start_pfn, nr_pages, index;
-       struct page *page;
-       unsigned int order;
 
        start_pfn = NODE_DATA(nid)->node_start_pfn;
        nr_pages = NODE_DATA(nid)->node_spanned_pages;
@@ -57,13 +55,11 @@ static int __init alloc_node_page_cgroup(int nid)
                return 0;
 
        table_size = sizeof(struct page_cgroup) * nr_pages;
-       order = get_order(table_size);
-       page = alloc_pages_node(nid, GFP_NOWAIT | __GFP_ZERO, order);
-       if (!page)
-               page = alloc_pages_node(-1, GFP_NOWAIT | __GFP_ZERO, order);
-       if (!page)
+
+       base = __alloc_bootmem_node_nopanic(NODE_DATA(nid),
+                       table_size, PAGE_SIZE, __pa(MAX_DMA_ADDRESS));
+       if (!base)
                return -ENOMEM;
-       base = page_address(page);
        for (index = 0; index < nr_pages; index++) {
                pc = base + index;
                __init_page_cgroup(pc, start_pfn + index);
@@ -73,7 +69,7 @@ static int __init alloc_node_page_cgroup(int nid)
        return 0;
 }
 
-void __init page_cgroup_init(void)
+void __init page_cgroup_init_flatmem(void)
 {
 
        int nid, fail;
@@ -117,16 +113,11 @@ static int __init_refok init_section_page_cgroup(unsigned long pfn)
        if (!section->page_cgroup) {
                nid = page_to_nid(pfn_to_page(pfn));
                table_size = sizeof(struct page_cgroup) * PAGES_PER_SECTION;
-               if (slab_is_available()) {
-                       base = kmalloc_node(table_size,
-                                       GFP_KERNEL | __GFP_NOWARN, nid);
-                       if (!base)
-                               base = vmalloc_node(table_size, nid);
-               } else {
-                       base = __alloc_bootmem_node_nopanic(NODE_DATA(nid),
-                               table_size,
-                               PAGE_SIZE, __pa(MAX_DMA_ADDRESS));
-               }
+               VM_BUG_ON(!slab_is_available());
+               base = kmalloc_node(table_size,
+                               GFP_KERNEL | __GFP_NOWARN, nid);
+               if (!base)
+                       base = vmalloc_node(table_size, nid);
        } else {
                /*
                 * We don't have to allocate page_cgroup again, but
index f46b65d124e5fabc39ba94653ff5fcf33bab89a5..18e3164de09a9ce3b5c69b9fd53b6533b39e9486 100644 (file)
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -303,6 +303,12 @@ struct kmem_list3 {
        int free_touched;               /* updated without locking */
 };
 
+/*
+ * The slab allocator is initialized with interrupts disabled. Therefore, make
+ * sure early boot allocations don't accidentally enable interrupts.
+ */
+static gfp_t slab_gfp_mask __read_mostly = SLAB_GFP_BOOT_MASK;
+
 /*
  * Need this for bootstrapping a per node allocator.
  */
@@ -753,6 +759,7 @@ static enum {
        NONE,
        PARTIAL_AC,
        PARTIAL_L3,
+       EARLY,
        FULL
 } g_cpucache_up;
 
@@ -761,7 +768,7 @@ static enum {
  */
 int slab_is_available(void)
 {
-       return g_cpucache_up == FULL;
+       return g_cpucache_up >= EARLY;
 }
 
 static DEFINE_PER_CPU(struct delayed_work, reap_work);
@@ -1625,19 +1632,27 @@ void __init kmem_cache_init(void)
                }
        }
 
-       /* 6) resize the head arrays to their final sizes */
-       {
-               struct kmem_cache *cachep;
-               mutex_lock(&cache_chain_mutex);
-               list_for_each_entry(cachep, &cache_chain, next)
-                       if (enable_cpucache(cachep, GFP_NOWAIT))
-                               BUG();
-               mutex_unlock(&cache_chain_mutex);
-       }
+       g_cpucache_up = EARLY;
 
        /* Annotate slab for lockdep -- annotate the malloc caches */
        init_lock_keys();
+}
+
+void __init kmem_cache_init_late(void)
+{
+       struct kmem_cache *cachep;
+
+       /*
+        * Interrupts are enabled now so all GFP allocations are safe.
+        */
+       slab_gfp_mask = __GFP_BITS_MASK;
 
+       /* 6) resize the head arrays to their final sizes */
+       mutex_lock(&cache_chain_mutex);
+       list_for_each_entry(cachep, &cache_chain, next)
+               if (enable_cpucache(cachep, GFP_NOWAIT))
+                       BUG();
+       mutex_unlock(&cache_chain_mutex);
 
        /* Done! */
        g_cpucache_up = FULL;
@@ -2102,7 +2117,7 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp)
                        for_each_online_node(node) {
                                cachep->nodelists[node] =
                                    kmalloc_node(sizeof(struct kmem_list3),
-                                               GFP_KERNEL, node);
+                                               gfp, node);
                                BUG_ON(!cachep->nodelists[node]);
                                kmem_list3_init(cachep->nodelists[node]);
                        }
@@ -3354,6 +3369,8 @@ __cache_alloc_node(struct kmem_cache *cachep, gfp_t flags, int nodeid,
        unsigned long save_flags;
        void *ptr;
 
+       flags &= slab_gfp_mask;
+
        lockdep_trace_alloc(flags);
 
        if (slab_should_failslab(cachep, flags))
@@ -3434,6 +3451,8 @@ __cache_alloc(struct kmem_cache *cachep, gfp_t flags, void *caller)
        unsigned long save_flags;
        void *objp;
 
+       flags &= slab_gfp_mask;
+
        lockdep_trace_alloc(flags);
 
        if (slab_should_failslab(cachep, flags))
index 3964d3ce4c1588414dcc03b043b672a5c4769964..30354bfeb43d5b093669fb0ef62b213e0c3991f2 100644 (file)
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -178,6 +178,12 @@ static enum {
        SYSFS           /* Sysfs up */
 } slab_state = DOWN;
 
+/*
+ * The slab allocator is initialized with interrupts disabled. Therefore, make
+ * sure early boot allocations don't accidentally enable interrupts.
+ */
+static gfp_t slab_gfp_mask __read_mostly = SLAB_GFP_BOOT_MASK;
+
 /* A list of all slab caches on the system */
 static DECLARE_RWSEM(slub_lock);
 static LIST_HEAD(slab_caches);
@@ -1595,6 +1601,8 @@ static __always_inline void *slab_alloc(struct kmem_cache *s,
        unsigned long flags;
        unsigned int objsize;
 
+       gfpflags &= slab_gfp_mask;
+
        lockdep_trace_alloc(gfpflags);
        might_sleep_if(gfpflags & __GFP_WAIT);
 
@@ -3104,6 +3112,14 @@ void __init kmem_cache_init(void)
                nr_cpu_ids, nr_node_ids);
 }
 
+void __init kmem_cache_init_late(void)
+{
+       /*
+        * Interrupts are enabled now so all GFP allocations are safe.
+        */
+       slab_gfp_mask = __GFP_BITS_MASK;
+}
+
 /*
  * Find a mergeable slab cache
  */
index d254306562cda93af097d60ed024a71479c9da4d..95c08a8cc2ba4fb015f71b605271f9b8804c68fb 100644 (file)
@@ -2056,7 +2056,7 @@ unsigned long global_lru_pages(void)
                + global_page_state(NR_INACTIVE_FILE);
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_HIBERNATION
 /*
  * Helper function for shrink_all_memory().  Tries to reclaim 'nr_pages' pages
  * from LRU lists system-wide, for given pass and priority.
@@ -2196,7 +2196,7 @@ out:
 
        return sc.nr_reclaimed;
 }
-#endif
+#endif /* CONFIG_HIBERNATION */
 
 /* It's optimal to keep kswapds on the same CPUs as their memory, but
    not required for correctness.  So if the last cpu in a node goes
index bb8579a141a86c584db61993a86104316ba4cb43..a49484e67e1ddf2b1dcd2ae1ed0ee71a8872521f 100644 (file)
@@ -246,7 +246,7 @@ static int p9_virtio_probe(struct virtio_device *vdev)
        chan->vdev = vdev;
 
        /* We expect one virtqueue, for requests. */
-       chan->vq = vdev->config->find_vq(vdev, 0, req_done);
+       chan->vq = virtio_find_single_vq(vdev, req_done, "requests");
        if (IS_ERR(chan->vq)) {
                err = PTR_ERR(chan->vq);
                goto out_free_vq;
@@ -261,7 +261,7 @@ static int p9_virtio_probe(struct virtio_device *vdev)
        return 0;
 
 out_free_vq:
-       vdev->config->del_vq(chan->vq);
+       vdev->config->del_vqs(vdev);
 fail:
        mutex_lock(&virtio_9p_lock);
        chan_index--;
@@ -332,7 +332,7 @@ static void p9_virtio_remove(struct virtio_device *vdev)
        BUG_ON(chan->inuse);
 
        if (chan->initialized) {
-               vdev->config->del_vq(chan->vq);
+               vdev->config->del_vqs(vdev);
                chan->initialized = false;
        }
 }
index 60d00d1c4eee2051d0bb9d274ef95a9832962153..66ad375612f27f0aa2ce6902806e9ee59d7b2706 100755 (executable)
@@ -14,6 +14,57 @@ cat << EOF
 #include <asm/types.h>
 #include <asm/unistd.h>
 
+/* *at */
+#define __IGNORE_open          /* openat */
+#define __IGNORE_link          /* linkat */
+#define __IGNORE_unlink                /* unlinkat */
+#define __IGNORE_mknod         /* mknodat */
+#define __IGNORE_chmod         /* fchmodat */
+#define __IGNORE_chown         /* fchownat */
+#define __IGNORE_mkdir         /* mkdirat */
+#define __IGNORE_rmdir         /* unlinkat */
+#define __IGNORE_lchown                /* fchownat */
+#define __IGNORE_access                /* faccessat */
+#define __IGNORE_rename                /* renameat */
+#define __IGNORE_readlink      /* readlinkat */
+#define __IGNORE_symlink       /* symlinkat */
+#define __IGNORE_utimes                /* futimesat */
+#if BITS_PER_LONG == 64
+#define __IGNORE_stat          /* fstatat */
+#define __IGNORE_lstat         /* fstatat */
+#else
+#define __IGNORE_stat64                /* fstatat64 */
+#define __IGNORE_lstat64       /* fstatat64 */
+#endif
+
+/* CLOEXEC flag */
+#define __IGNORE_pipe          /* pipe2 */
+#define __IGNORE_dup2          /* dup3 */
+#define __IGNORE_epoll_create  /* epoll_create1 */
+#define __IGNORE_inotify_init  /* inotify_init1 */
+#define __IGNORE_eventfd       /* eventfd2 */
+#define __IGNORE_signalfd      /* signalfd4 */
+
+/* MMU */
+#ifndef CONFIG_MMU
+#define __IGNORE_madvise
+#define __IGNORE_mbind
+#define __IGNORE_mincore
+#define __IGNORE_mlock
+#define __IGNORE_mlockall
+#define __IGNORE_munlock
+#define __IGNORE_munlockall
+#define __IGNORE_mprotect
+#define __IGNORE_msync
+#define __IGNORE_migrate_pages
+#define __IGNORE_move_pages
+#define __IGNORE_remap_file_pages
+#define __IGNORE_get_mempolicy
+#define __IGNORE_set_mempolicy
+#define __IGNORE_swapoff
+#define __IGNORE_swapon
+#endif
+
 /* System calls for 32-bit kernels only */
 #if BITS_PER_LONG == 64
 #define __IGNORE_sendfile64
@@ -27,6 +78,22 @@ cat << EOF
 #define __IGNORE_fstatat64
 #define __IGNORE_fstatfs64
 #define __IGNORE_statfs64
+#define __IGNORE_llseek
+#define __IGNORE_mmap2
+#else
+#define __IGNORE_sendfile
+#define __IGNORE_ftruncate
+#define __IGNORE_truncate
+#define __IGNORE_stat
+#define __IGNORE_lstat
+#define __IGNORE_fstat
+#define __IGNORE_fcntl
+#define __IGNORE_fadvise64
+#define __IGNORE_newfstatat
+#define __IGNORE_fstatfs
+#define __IGNORE_statfs
+#define __IGNORE_lseek
+#define __IGNORE_mmap
 #endif
 
 /* i386-specific or historical system calls */
@@ -44,7 +111,6 @@ cat << EOF
 #define __IGNORE_idle
 #define __IGNORE_modify_ldt
 #define __IGNORE_ugetrlimit
-#define __IGNORE_mmap2
 #define __IGNORE_vm86
 #define __IGNORE_vm86old
 #define __IGNORE_set_thread_area
@@ -55,7 +121,6 @@ cat << EOF
 #define __IGNORE_oldlstat
 #define __IGNORE_oldolduname
 #define __IGNORE_olduname
-#define __IGNORE_umount2
 #define __IGNORE_umount
 #define __IGNORE_waitpid
 #define __IGNORE_stime
@@ -75,9 +140,12 @@ cat << EOF
 #define __IGNORE__llseek
 #define __IGNORE__newselect
 #define __IGNORE_create_module
-#define __IGNORE_delete_module
 #define __IGNORE_query_module
 #define __IGNORE_get_kernel_syms
+#define __IGNORE_sysfs
+#define __IGNORE_uselib
+#define __IGNORE__sysctl
+
 /* ... including the "new" 32-bit uid syscalls */
 #define __IGNORE_lchown32
 #define __IGNORE_getuid32
@@ -99,6 +167,24 @@ cat << EOF
 #define __IGNORE_setfsuid32
 #define __IGNORE_setfsgid32
 
+/* these can be expressed using other calls */
+#define __IGNORE_alarm         /* setitimer */
+#define __IGNORE_creat         /* open */
+#define __IGNORE_fork          /* clone */
+#define __IGNORE_futimesat     /* utimensat */
+#define __IGNORE_getpgrp       /* getpgid */
+#define __IGNORE_getdents      /* getdents64 */
+#define __IGNORE_pause         /* sigsuspend */
+#define __IGNORE_poll          /* ppoll */
+#define __IGNORE_select                /* pselect6 */
+#define __IGNORE_epoll_wait    /* epoll_pwait */
+#define __IGNORE_time          /* gettimeofday */
+#define __IGNORE_uname         /* newuname */
+#define __IGNORE_ustat         /* statfs */
+#define __IGNORE_utime         /* utimes */
+#define __IGNORE_vfork         /* clone */
+#define __IGNORE_wait4         /* waitid */
+
 /* sync_file_range had a stupid ABI. Allow sync_file_range2 instead */
 #ifdef __NR_sync_file_range2
 #define __IGNORE_sync_file_range
index a3344285ccf4cbd17a66c8fd8118041e3ab5d025..40e0045876ee4fa954039dc1bed7c88a1db1d5d2 100644 (file)
@@ -641,7 +641,7 @@ static int do_virtio_entry(const char *filename, struct virtio_device_id *id,
        id->vendor = TO_NATIVE(id->vendor);
 
        strcpy(alias, "virtio:");
-       ADD(alias, "d", 1, id->device);
+       ADD(alias, "d", id->device != VIRTIO_DEV_ANY_ID, id->device);
        ADD(alias, "v", id->vendor != VIRTIO_DEV_ANY_ID, id->vendor);
 
        add_wildcard(alias);
index fbf5c933baa4aa1487e9f2559210ac61f262be07..586965f9605fd55d4a6cf9c8f804f01d373647bb 100644 (file)
@@ -1037,7 +1037,7 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
        }
        ldev->selfptr_headphone.ptr = ldev;
        ldev->selfptr_lineout.ptr = ldev;
-       sdev->ofdev.dev.driver_data = ldev;
+       dev_set_drvdata(&sdev->ofdev.dev, ldev);
        list_add(&ldev->list, &layouts_list);
        layouts_list_items++;
 
@@ -1081,7 +1081,7 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
 
 static int aoa_fabric_layout_remove(struct soundbus_dev *sdev)
 {
-       struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
+       struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
        int i;
 
        for (i=0; i<MAX_CODECS_PER_BUS; i++) {
@@ -1114,7 +1114,7 @@ static int aoa_fabric_layout_remove(struct soundbus_dev *sdev)
 #ifdef CONFIG_PM
 static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t state)
 {
-       struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
+       struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
 
        if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
                ldev->gpio.methods->all_amps_off(&ldev->gpio);
@@ -1124,7 +1124,7 @@ static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t sta
 
 static int aoa_fabric_layout_resume(struct soundbus_dev *sdev)
 {
-       struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
+       struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
 
        if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
                ldev->gpio.methods->all_amps_restore(&ldev->gpio);
index 418c84c99d6946d8cb4982569ada0542c1327dfa..4e3b819d49930f07b7ce641c1de0a32b878f8bcd 100644 (file)
@@ -358,14 +358,14 @@ static int i2sbus_probe(struct macio_dev* dev, const struct of_device_id *match)
                return -ENODEV;
        }
 
-       dev->ofdev.dev.driver_data = control;
+       dev_set_drvdata(&dev->ofdev.dev, control);
 
        return 0;
 }
 
 static int i2sbus_remove(struct macio_dev* dev)
 {
-       struct i2sbus_control *control = dev->ofdev.dev.driver_data;
+       struct i2sbus_control *control = dev_get_drvdata(&dev->ofdev.dev);
        struct i2sbus_dev *i2sdev, *tmp;
 
        list_for_each_entry_safe(i2sdev, tmp, &control->list, item)
@@ -377,7 +377,7 @@ static int i2sbus_remove(struct macio_dev* dev)
 #ifdef CONFIG_PM
 static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state)
 {
-       struct i2sbus_control *control = dev->ofdev.dev.driver_data;
+       struct i2sbus_control *control = dev_get_drvdata(&dev->ofdev.dev);
        struct codec_info_item *cii;
        struct i2sbus_dev* i2sdev;
        int err, ret = 0;
@@ -407,7 +407,7 @@ static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state)
 
 static int i2sbus_resume(struct macio_dev* dev)
 {
-       struct i2sbus_control *control = dev->ofdev.dev.driver_data;
+       struct i2sbus_control *control = dev_get_drvdata(&dev->ofdev.dev);
        struct codec_info_item *cii;
        struct i2sbus_dev* i2sdev;
        int err, ret = 0;
index 7bbdda041a991a991f264b09959025dd2f298295..6061fb5f4e1c14a8d1cc4b38ec3babe68f17a92d 100644 (file)
@@ -205,3 +205,5 @@ config SND_PCM_XRUN_DEBUG
 
 config SND_VMASTER
        bool
+
+source "sound/core/seq/Kconfig"
index fd56afe846ed6321d71e328147a5659423e2bce2..d5d40d78c409e6b865f0b420683ddd0c3f41d324 100644 (file)
@@ -152,15 +152,8 @@ int snd_card_create(int idx, const char *xid,
        card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
        if (!card)
                return -ENOMEM;
-       if (xid) {
-               if (!snd_info_check_reserved_words(xid)) {
-                       snd_printk(KERN_ERR
-                                  "given id string '%s' is reserved.\n", xid);
-                       err = -EBUSY;
-                       goto __error;
-               }
+       if (xid)
                strlcpy(card->id, xid, sizeof(card->id));
-       }
        err = 0;
        mutex_lock(&snd_card_mutex);
        if (idx < 0) {
@@ -483,22 +476,28 @@ int snd_card_free(struct snd_card *card)
 
 EXPORT_SYMBOL(snd_card_free);
 
-static void choose_default_id(struct snd_card *card)
+static void snd_card_set_id_no_lock(struct snd_card *card, const char *nid)
 {
        int i, len, idx_flag = 0, loops = SNDRV_CARDS;
-       char *id, *spos;
+       const char *spos, *src;
+       char *id;
        
-       id = spos = card->shortname;    
-       while (*id != '\0') {
-               if (*id == ' ')
-                       spos = id + 1;
-               id++;
+       if (nid == NULL) {
+               id = card->shortname;
+               spos = src = id;
+               while (*id != '\0') {
+                       if (*id == ' ')
+                               spos = id + 1;
+                       id++;
+               }
+       } else {
+               spos = src = nid;
        }
        id = card->id;
        while (*spos != '\0' && !isalnum(*spos))
                spos++;
        if (isdigit(*spos))
-               *id++ = isalpha(card->shortname[0]) ? card->shortname[0] : 'D';
+               *id++ = isalpha(src[0]) ? src[0] : 'D';
        while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) {
                if (isalnum(*spos))
                        *id++ = *spos;
@@ -513,7 +512,7 @@ static void choose_default_id(struct snd_card *card)
 
        while (1) {
                if (loops-- == 0) {
-                       snd_printk(KERN_ERR "unable to choose default card id (%s)\n", id);
+                       snd_printk(KERN_ERR "unable to set card id (%s)\n", id);
                        strcpy(card->id, card->proc_root->name);
                        return;
                }
@@ -539,14 +538,33 @@ static void choose_default_id(struct snd_card *card)
                        spos = id + len - 2;
                        if ((size_t)len <= sizeof(card->id) - 2)
                                spos++;
-                       *spos++ = '_';
-                       *spos++ = '1';
-                       *spos++ = '\0';
+                       *(char *)spos++ = '_';
+                       *(char *)spos++ = '1';
+                       *(char *)spos++ = '\0';
                        idx_flag++;
                }
        }
 }
 
+/**
+ *  snd_card_set_id - set card identification name
+ *  @card: soundcard structure
+ *  @nid: new identification string
+ *
+ *  This function sets the card identification and checks for name
+ *  collisions.
+ */
+void snd_card_set_id(struct snd_card *card, const char *nid)
+{
+       /* check if user specified own card->id */
+       if (card->id[0] != '\0')
+               return;
+       mutex_lock(&snd_card_mutex);
+       snd_card_set_id_no_lock(card, nid);
+       mutex_unlock(&snd_card_mutex);
+}
+EXPORT_SYMBOL(snd_card_set_id);
+
 #ifndef CONFIG_SYSFS_DEPRECATED
 static ssize_t
 card_id_show_attr(struct device *dev,
@@ -640,8 +658,7 @@ int snd_card_register(struct snd_card *card)
                mutex_unlock(&snd_card_mutex);
                return 0;
        }
-       if (card->id[0] == '\0')
-               choose_default_id(card);
+       snd_card_set_id_no_lock(card, card->id[0] == '\0' ? NULL : card->id);
        snd_cards[card->number] = card;
        mutex_unlock(&snd_card_mutex);
        init_info_for_card(card);
index d54d1a05fe65af20009da1330b3e659d032e6217..f705eec7372a32aad6187651cc393c69c2ce7e78 100644 (file)
@@ -63,7 +63,7 @@ static int snd_jack_dev_register(struct snd_device *device)
 
        /* Default to the sound card device. */
        if (!jack->input_dev->dev.parent)
-               jack->input_dev->dev.parent = card->dev;
+               jack->input_dev->dev.parent = snd_card_get_device_link(card);
 
        err = input_register_device(jack->input_dev);
        if (err == 0)
index dda000b9684cb07469e86fa4b7d324091c5db1a1..dbe406b82591058dd515686d8d13eb800418b416 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/time.h>
 #include <linux/vmalloc.h>
 #include <linux/moduleparam.h>
+#include <linux/math64.h>
 #include <linux/string.h>
 #include <sound/core.h>
 #include <sound/minors.h>
@@ -617,9 +618,7 @@ static long snd_pcm_oss_bytes(struct snd_pcm_substream *substream, long frames)
 #else
        {
                u64 bsize = (u64)runtime->oss.buffer_bytes * (u64)bytes;
-               u32 rem;
-               div64_32(&bsize, buffer_size, &rem);
-               return (long)bsize;
+               return div_u64(bsize, buffer_size);
        }
 #endif
 }
index d659995ac3ac7be2f20e488c71b7b3fe405a879e..333e4dd29450c7e9e823ebec46b7af4bcb6c62fc 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <linux/slab.h>
 #include <linux/time.h>
+#include <linux/math64.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/info.h>
@@ -126,24 +127,37 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
 }
 
 #ifdef CONFIG_SND_PCM_XRUN_DEBUG
-#define xrun_debug(substream)  ((substream)->pstr->xrun_debug)
+#define xrun_debug(substream, mask)    ((substream)->pstr->xrun_debug & (mask))
 #else
-#define xrun_debug(substream)  0
+#define xrun_debug(substream, mask)    0
 #endif
 
-#define dump_stack_on_xrun(substream) do {     \
-               if (xrun_debug(substream) > 1)  \
-                       dump_stack();           \
+#define dump_stack_on_xrun(substream) do {             \
+               if (xrun_debug(substream, 2))           \
+                       dump_stack();                   \
        } while (0)
 
+static void pcm_debug_name(struct snd_pcm_substream *substream,
+                          char *name, size_t len)
+{
+       snprintf(name, len, "pcmC%dD%d%c:%d",
+                substream->pcm->card->number,
+                substream->pcm->device,
+                substream->stream ? 'c' : 'p',
+                substream->number);
+}
+
 static void xrun(struct snd_pcm_substream *substream)
 {
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+               snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
        snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
-       if (xrun_debug(substream)) {
-               snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n",
-                          substream->pcm->card->number,
-                          substream->pcm->device,
-                          substream->stream ? 'c' : 'p');
+       if (xrun_debug(substream, 1)) {
+               char name[16];
+               pcm_debug_name(substream, name, sizeof(name));
+               snd_printd(KERN_DEBUG "XRUN: %s\n", name);
                dump_stack_on_xrun(substream);
        }
 }
@@ -154,16 +168,16 @@ snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream,
 {
        snd_pcm_uframes_t pos;
 
-       if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
-               snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
        pos = substream->ops->pointer(substream);
        if (pos == SNDRV_PCM_POS_XRUN)
                return pos; /* XRUN */
        if (pos >= runtime->buffer_size) {
                if (printk_ratelimit()) {
-                       snd_printd(KERN_ERR  "BUG: stream = %i, pos = 0x%lx, "
+                       char name[16];
+                       pcm_debug_name(substream, name, sizeof(name));
+                       snd_printd(KERN_ERR  "BUG: %s, pos = 0x%lx, "
                                   "buffer size = 0x%lx, period size = 0x%lx\n",
-                                  substream->stream, pos, runtime->buffer_size,
+                                  name, pos, runtime->buffer_size,
                                   runtime->period_size);
                }
                pos = 0;
@@ -197,7 +211,7 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream,
 
 #define hw_ptr_error(substream, fmt, args...)                          \
        do {                                                            \
-               if (xrun_debug(substream)) {                            \
+               if (xrun_debug(substream, 1)) {                         \
                        if (printk_ratelimit()) {                       \
                                snd_printd("PCM: " fmt, ##args);        \
                        }                                               \
@@ -251,7 +265,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
        }
 
        /* Do jiffies check only in xrun_debug mode */
-       if (!xrun_debug(substream))
+       if (!xrun_debug(substream, 4))
                goto no_jiffies_check;
 
        /* Skip the jiffies check for hardwares with BATCH flag.
@@ -261,6 +275,9 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
        if (runtime->hw.info & SNDRV_PCM_INFO_BATCH)
                goto no_jiffies_check;
        hdelta = new_hw_ptr - old_hw_ptr;
+       if (hdelta < runtime->delay)
+               goto no_jiffies_check;
+       hdelta -= runtime->delay;
        jdelta = jiffies - runtime->hw_ptr_jiffies;
        if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) {
                delta = jdelta /
@@ -294,14 +311,20 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
                hw_ptr_interrupt =
                        new_hw_ptr - new_hw_ptr % runtime->period_size;
        }
+       runtime->hw_ptr_interrupt = hw_ptr_interrupt;
+
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
            runtime->silence_size > 0)
                snd_pcm_playback_silence(substream, new_hw_ptr);
 
+       if (runtime->status->hw_ptr == new_hw_ptr)
+               return 0;
+
        runtime->hw_ptr_base = hw_base;
        runtime->status->hw_ptr = new_hw_ptr;
        runtime->hw_ptr_jiffies = jiffies;
-       runtime->hw_ptr_interrupt = hw_ptr_interrupt;
+       if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+               snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
 
        return snd_pcm_update_hw_ptr_post(substream, runtime);
 }
@@ -342,8 +365,12 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
                new_hw_ptr = hw_base + pos;
        }
        /* Do jiffies check only in xrun_debug mode */
-       if (xrun_debug(substream) &&
-           ((delta * HZ) / runtime->rate) > jdelta + HZ/100) {
+       if (!xrun_debug(substream, 4))
+               goto no_jiffies_check;
+       if (delta < runtime->delay)
+               goto no_jiffies_check;
+       delta -= runtime->delay;
+       if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) {
                hw_ptr_error(substream,
                             "hw_ptr skipping! "
                             "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n",
@@ -352,13 +379,19 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
                             ((delta * HZ) / runtime->rate));
                return 0;
        }
+ no_jiffies_check:
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
            runtime->silence_size > 0)
                snd_pcm_playback_silence(substream, new_hw_ptr);
 
+       if (runtime->status->hw_ptr == new_hw_ptr)
+               return 0;
+
        runtime->hw_ptr_base = hw_base;
        runtime->status->hw_ptr = new_hw_ptr;
        runtime->hw_ptr_jiffies = jiffies;
+       if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+               snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
 
        return snd_pcm_update_hw_ptr_post(substream, runtime);
 }
@@ -452,7 +485,7 @@ static inline unsigned int muldiv32(unsigned int a, unsigned int b,
                *r = 0;
                return UINT_MAX;
        }
-       div64_32(&n, c, r);
+       n = div_u64_rem(n, c, r);
        if (n >= UINT_MAX) {
                *r = 0;
                return UINT_MAX;
@@ -1524,6 +1557,23 @@ static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream,
        return 0;
 }
 
+static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream,
+                                      void *arg)
+{
+       struct snd_pcm_hw_params *params = arg;
+       snd_pcm_format_t format;
+       int channels, width;
+
+       params->fifo_size = substream->runtime->hw.fifo_size;
+       if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_FIFO_IN_FRAMES)) {
+               format = params_format(params);
+               channels = params_channels(params);
+               width = snd_pcm_format_physical_width(format);
+               params->fifo_size /= width * channels;
+       }
+       return 0;
+}
+
 /**
  * snd_pcm_lib_ioctl - a generic PCM ioctl callback
  * @substream: the pcm substream instance
@@ -1545,6 +1595,8 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
                return snd_pcm_lib_ioctl_reset(substream, arg);
        case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
                return snd_pcm_lib_ioctl_channel_info(substream, arg);
+       case SNDRV_PCM_IOCTL1_FIFO_SIZE:
+               return snd_pcm_lib_ioctl_fifo_size(substream, arg);
        }
        return -ENXIO;
 }
index b5da656d1ececa2e41b13dc55ea57b662bcc1b2e..84da3ba17c86911b0d48799772bd0c0960c90693 100644 (file)
@@ -312,9 +312,18 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
 
        hw = &substream->runtime->hw;
        if (!params->info)
-               params->info = hw->info;
-       if (!params->fifo_size)
-               params->fifo_size = hw->fifo_size;
+               params->info = hw->info & ~SNDRV_PCM_INFO_FIFO_IN_FRAMES;
+       if (!params->fifo_size) {
+               if (snd_mask_min(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT]) ==
+                   snd_mask_max(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT]) &&
+                    snd_mask_min(&params->masks[SNDRV_PCM_HW_PARAM_CHANNELS]) ==
+                    snd_mask_max(&params->masks[SNDRV_PCM_HW_PARAM_CHANNELS])) {
+                       changed = substream->ops->ioctl(substream,
+                                       SNDRV_PCM_IOCTL1_FIFO_SIZE, params);
+                       if (params < 0)
+                               return changed;
+               }
+       }
        params->rmask = 0;
        return 0;
 }
@@ -587,14 +596,15 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                status->avail = snd_pcm_playback_avail(runtime);
                if (runtime->status->state == SNDRV_PCM_STATE_RUNNING ||
-                   runtime->status->state == SNDRV_PCM_STATE_DRAINING)
+                   runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
                        status->delay = runtime->buffer_size - status->avail;
-               else
+                       status->delay += runtime->delay;
+               } else
                        status->delay = 0;
        } else {
                status->avail = snd_pcm_capture_avail(runtime);
                if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
-                       status->delay = status->avail;
+                       status->delay = status->avail + runtime->delay;
                else
                        status->delay = 0;
        }
@@ -2410,6 +2420,7 @@ static int snd_pcm_delay(struct snd_pcm_substream *substream,
                        n = snd_pcm_playback_hw_avail(runtime);
                else
                        n = snd_pcm_capture_avail(runtime);
+               n += runtime->delay;
                break;
        case SNDRV_PCM_STATE_XRUN:
                err = -EPIPE;
diff --git a/sound/core/seq/Kconfig b/sound/core/seq/Kconfig
new file mode 100644 (file)
index 0000000..b851fd8
--- /dev/null
@@ -0,0 +1,16 @@
+# define SND_XXX_SEQ to min(SND_SEQUENCER,SND_XXX)
+
+config SND_RAWMIDI_SEQ
+       def_tristate SND_SEQUENCER && SND_RAWMIDI
+
+config SND_OPL3_LIB_SEQ
+       def_tristate SND_SEQUENCER && SND_OPL3_LIB
+
+config SND_OPL4_LIB_SEQ
+       def_tristate SND_SEQUENCER && SND_OPL4_LIB
+
+config SND_SBAWE_SEQ
+       def_tristate SND_SEQUENCER && SND_SBAWE
+
+config SND_EMU10K1_SEQ
+       def_tristate SND_SEQUENCER && SND_EMU10K1
index 069593717fbab0c0deb733c52e883c5b6c638988..1bcb360330e521f37b82bca91452b7cbc0db8803 100644 (file)
@@ -17,14 +17,6 @@ snd-seq-midi-event-objs := seq_midi_event.o
 snd-seq-dummy-objs := seq_dummy.o
 snd-seq-virmidi-objs := seq_virmidi.o
 
-#
-# this function returns:
-#   "m" - CONFIG_SND_SEQUENCER is m
-#   <empty string> - CONFIG_SND_SEQUENCER is undefined
-#   otherwise parameter #1 value
-#
-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
-
 obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o snd-seq-device.o
 ifeq ($(CONFIG_SND_SEQUENCER_OSS),y)
 obj-$(CONFIG_SND_SEQUENCER) += snd-seq-midi-event.o
@@ -33,8 +25,8 @@ obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o
-obj-$(call sequencer,$(CONFIG_SND_RAWMIDI)) += snd-seq-midi.o snd-seq-midi-event.o
-obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o
-obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o
-obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-seq-midi-emul.o snd-seq-virmidi.o
-obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-seq-midi-emul.o snd-seq-virmidi.o
+obj-$(CONFIG_SND_RAWMIDI_SEQ) += snd-seq-midi.o snd-seq-midi-event.o
+obj-$(CONFIG_SND_OPL3_LIB_SEQ) += snd-seq-midi-event.o snd-seq-midi-emul.o
+obj-$(CONFIG_SND_OPL4_LIB_SEQ) += snd-seq-midi-event.o snd-seq-midi-emul.o
+obj-$(CONFIG_SND_SBAWE_SEQ) += snd-seq-midi-emul.o snd-seq-virmidi.o
+obj-$(CONFIG_SND_EMU10K1_SEQ) += snd-seq-midi-emul.o snd-seq-virmidi.o
index 19767a6a5c54a9297cb36a2a38293d82a4ec876e..7f2c2a10c4e5c96607c577ffc3c2d8f584a280b9 100644 (file)
@@ -7,14 +7,6 @@ snd-opl3-lib-objs := opl3_lib.o opl3_synth.o
 snd-opl3-synth-y := opl3_seq.o opl3_midi.o opl3_drums.o
 snd-opl3-synth-$(CONFIG_SND_SEQUENCER_OSS) += opl3_oss.o
 
-#
-# this function returns:
-#   "m" - CONFIG_SND_SEQUENCER is m
-#   <empty string> - CONFIG_SND_SEQUENCER is undefined
-#   otherwise parameter #1 value
-#
-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
-
 obj-$(CONFIG_SND_OPL3_LIB) += snd-opl3-lib.o
 obj-$(CONFIG_SND_OPL4_LIB) += snd-opl3-lib.o
-obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-opl3-synth.o
+obj-$(CONFIG_SND_OPL3_LIB_SEQ) += snd-opl3-synth.o
index d178b39ffa6095416b47aa5d57b1390fd006b4a0..b94009b0b19f994d1ee3c56323fbeb27d15bbae9 100644 (file)
@@ -6,13 +6,5 @@
 snd-opl4-lib-objs := opl4_lib.o opl4_mixer.o opl4_proc.o
 snd-opl4-synth-objs := opl4_seq.o opl4_synth.o yrw801.o
 
-#
-# this function returns:
-#   "m" - CONFIG_SND_SEQUENCER is m
-#   <empty string> - CONFIG_SND_SEQUENCER is undefined
-#   otherwise parameter #1 value
-#
-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
-
 obj-$(CONFIG_SND_OPL4_LIB) += snd-opl4-lib.o
-obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-opl4-synth.o
+obj-$(CONFIG_SND_OPL4_LIB_SEQ) += snd-opl4-synth.o
index c6942a4de99b3d5afebe9ec0961c961388a10c96..51a7e3777e17750639faf186befa3a4b4d751520 100644 (file)
@@ -177,15 +177,18 @@ config SND_ES18XX
          will be called snd-es18xx.
 
 config SND_SC6000
-       tristate "Gallant SC-6000, Audio Excel DSP 16"
+       tristate "Gallant SC-6000/6600/7000 and Audio Excel DSP 16"
        depends on HAS_IOPORT
        select SND_WSS_LIB
        select SND_OPL3_LIB
        select SND_MPU401_UART
        help
-         Say Y here to include support for Gallant SC-6000 card and clones:
+         Say Y here to include support for Gallant SC-6000, SC-6600, SC-7000
+         cards and clones:
          Audio Excel DSP 16 and Zoltrix AV302.
 
+         These cards are based on CompuMedia ASC-9308 or ASC-9408 chips.
+
          To compile this driver as a module, choose M here: the module
          will be called snd-sc6000.
 
index 442b081cafb755f90ed2181d73cbeea9fe5a2279..07df201ed8fa3d3999074b4415d582539bbe2b1f 100644 (file)
@@ -193,7 +193,7 @@ static int __devexit snd_es1688_remove(struct device *dev, unsigned int n)
 static struct isa_driver snd_es1688_driver = {
        .match          = snd_es1688_match,
        .probe          = snd_es1688_probe,
-       .remove         = snd_es1688_remove,
+       .remove         = __devexit_p(snd_es1688_remove),
 #if 0  /* FIXME */
        .suspend        = snd_es1688_suspend,
        .resume         = snd_es1688_resume,
index 180a8dea6bd94cbec45ba6bcae82282cfe7119fb..65e4b18581a60a93de84efad535836588774a5ec 100644 (file)
@@ -348,7 +348,7 @@ static int __devexit snd_gusextreme_remove(struct device *dev, unsigned int n)
 static struct isa_driver snd_gusextreme_driver = {
        .match          = snd_gusextreme_match,
        .probe          = snd_gusextreme_probe,
-       .remove         = snd_gusextreme_remove,
+       .remove         = __devexit_p(snd_gusextreme_remove),
 #if 0  /* FIXME */
        .suspend        = snd_gusextreme_suspend,
        .resume         = snd_gusextreme_resume,
index 1098a56b2f4b878898aca659d3096eafa6fd3edc..faeffceb01b796c86bbd339dfa940e507f024142 100644 (file)
@@ -13,14 +13,6 @@ snd-sbawe-objs := sbawe.o emu8000.o
 snd-emu8000-synth-objs := emu8000_synth.o emu8000_callback.o emu8000_patch.o emu8000_pcm.o
 snd-es968-objs := es968.o
 
-#
-# this function returns:
-#   "m" - CONFIG_SND_SEQUENCER is m
-#   <empty string> - CONFIG_SND_SEQUENCER is undefined
-#   otherwise parameter #1 value
-#
-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
-
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_SB_COMMON) += snd-sb-common.o
 obj-$(CONFIG_SND_SB16_DSP) += snd-sb16-dsp.o
@@ -33,4 +25,4 @@ ifeq ($(CONFIG_SND_SB16_CSP),y)
   obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o
   obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o
 endif
-obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-emu8000-synth.o
+obj-$(CONFIG_SND_SBAWE_SEQ) += snd-emu8000-synth.o
index 782010608ef425e51f26e56d4a41335d6459dd9e..9a8bbf6dd62aeaecc1f51ee28d1b66db3d819f4c 100644 (file)
@@ -2,6 +2,8 @@
  *  Driver for Gallant SC-6000 soundcard. This card is also known as
  *  Audio Excel DSP 16 or Zoltrix AV302.
  *  These cards use CompuMedia ASC-9308 chip + AD1848 codec.
+ *  SC-6600 and SC-7000 cards are also supported. They are based on
+ *  CompuMedia ASC-9408 chip and CS4231 codec.
  *
  *  Copyright (C) 2007 Krzysztof Helt <krzysztof.h1@wp.pl>
  *
@@ -54,6 +56,7 @@ static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
                                                /* 0x300, 0x310, 0x320, 0x330 */
 static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;   /* 5, 7, 9, 10, 0 */
 static int dma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;       /* 0, 1, 3 */
+static bool joystick[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = false };
 
 module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "Index value for sc-6000 based soundcard.");
@@ -73,6 +76,8 @@ module_param_array(mpu_irq, int, NULL, 0444);
 MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for sc-6000 driver.");
 module_param_array(dma, int, NULL, 0444);
 MODULE_PARM_DESC(dma, "DMA # for sc-6000 driver.");
+module_param_array(joystick, bool, NULL, 0444);
+MODULE_PARM_DESC(joystick, "Enable gameport.");
 
 /*
  * Commands of SC6000's DSP (SBPRO+special).
@@ -191,7 +196,7 @@ static __devinit unsigned char sc6000_mpu_irq_to_softcfg(int mpu_irq)
        return val;
 }
 
-static __devinit int sc6000_wait_data(char __iomem *vport)
+static int sc6000_wait_data(char __iomem *vport)
 {
        int loop = 1000;
        unsigned char val = 0;
@@ -206,7 +211,7 @@ static __devinit int sc6000_wait_data(char __iomem *vport)
        return -EAGAIN;
 }
 
-static __devinit int sc6000_read(char __iomem *vport)
+static int sc6000_read(char __iomem *vport)
 {
        if (sc6000_wait_data(vport))
                return -EBUSY;
@@ -215,7 +220,7 @@ static __devinit int sc6000_read(char __iomem *vport)
 
 }
 
-static __devinit int sc6000_write(char __iomem *vport, int cmd)
+static int sc6000_write(char __iomem *vport, int cmd)
 {
        unsigned char val;
        int loop = 500000;
@@ -276,8 +281,33 @@ static int __devinit sc6000_dsp_reset(char __iomem *vport)
 }
 
 /* detection and initialization */
-static int __devinit sc6000_cfg_write(char __iomem *vport,
-                                     unsigned char softcfg)
+static int __devinit sc6000_hw_cfg_write(char __iomem *vport, const int *cfg)
+{
+       if (sc6000_write(vport, COMMAND_6C) < 0) {
+               snd_printk(KERN_WARNING "CMD 0x%x: failed!\n", COMMAND_6C);
+               return -EIO;
+       }
+       if (sc6000_write(vport, COMMAND_5C) < 0) {
+               snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_5C);
+               return -EIO;
+       }
+       if (sc6000_write(vport, cfg[0]) < 0) {
+               snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[0]);
+               return -EIO;
+       }
+       if (sc6000_write(vport, cfg[1]) < 0) {
+               snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[1]);
+               return -EIO;
+       }
+       if (sc6000_write(vport, COMMAND_C5) < 0) {
+               snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_C5);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int sc6000_cfg_write(char __iomem *vport, unsigned char softcfg)
 {
 
        if (sc6000_write(vport, WRITE_MDIRQ_CFG)) {
@@ -291,7 +321,7 @@ static int __devinit sc6000_cfg_write(char __iomem *vport,
        return 0;
 }
 
-static int __devinit sc6000_setup_board(char __iomem *vport, int config)
+static int sc6000_setup_board(char __iomem *vport, int config)
 {
        int loop = 10;
 
@@ -334,16 +364,39 @@ static int __devinit sc6000_init_mss(char __iomem *vport, int config,
        return 0;
 }
 
-static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma,
-                                       char __iomem *vmss_port, int mpu_irq)
+static void __devinit sc6000_hw_cfg_encode(char __iomem *vport, int *cfg,
+                                          long xport, long xmpu,
+                                          long xmss_port, int joystick)
+{
+       cfg[0] = 0;
+       cfg[1] = 0;
+       if (xport == 0x240)
+               cfg[0] |= 1;
+       if (xmpu != SNDRV_AUTO_PORT) {
+               cfg[0] |= (xmpu & 0x30) >> 2;
+               cfg[1] |= 0x20;
+       }
+       if (xmss_port == 0xe80)
+               cfg[0] |= 0x10;
+       cfg[0] |= 0x40;         /* always set */
+       if (!joystick)
+               cfg[0] |= 0x02;
+       cfg[1] |= 0x80;         /* enable WSS system */
+       cfg[1] &= ~0x40;        /* disable IDE */
+       snd_printd("hw cfg %x, %x\n", cfg[0], cfg[1]);
+}
+
+static int __devinit sc6000_init_board(char __iomem *vport,
+                                       char __iomem *vmss_port, int dev)
 {
        char answer[15];
        char version[2];
-       int mss_config = sc6000_irq_to_softcfg(irq) |
-                        sc6000_dma_to_softcfg(dma);
+       int mss_config = sc6000_irq_to_softcfg(irq[dev]) |
+                        sc6000_dma_to_softcfg(dma[dev]);
        int config = mss_config |
-                    sc6000_mpu_irq_to_softcfg(mpu_irq);
+                    sc6000_mpu_irq_to_softcfg(mpu_irq[dev]);
        int err;
+       int old = 0;
 
        err = sc6000_dsp_reset(vport);
        if (err < 0) {
@@ -360,7 +413,6 @@ static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma,
        /*
         * My SC-6000 card return "SC-6000" in DSPCopyright, so
         * if we have something different, we have to be warned.
-        * Mine returns "SC-6000A " - KH
         */
        if (strncmp("SC-6000", answer, 7))
                snd_printk(KERN_WARNING "Warning: non SC-6000 audio card!\n");
@@ -372,13 +424,32 @@ static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma,
        printk(KERN_INFO PFX "Detected model: %s, DSP version %d.%d\n",
                answer, version[0], version[1]);
 
-       /*
-        * 0x0A == (IRQ 7, DMA 1, MIRQ 0)
-        */
-       err = sc6000_cfg_write(vport, 0x0a);
+       /* set configuration */
+       sc6000_write(vport, COMMAND_5C);
+       if (sc6000_read(vport) < 0)
+               old = 1;
+
+       if (!old) {
+               int cfg[2];
+               sc6000_hw_cfg_encode(vport, &cfg[0], port[dev], mpu_port[dev],
+                                    mss_port[dev], joystick[dev]);
+               if (sc6000_hw_cfg_write(vport, cfg) < 0) {
+                       snd_printk(KERN_ERR "sc6000_hw_cfg_write: failed!\n");
+                       return -EIO;
+               }
+       }
+       err = sc6000_setup_board(vport, config);
        if (err < 0) {
-               snd_printk(KERN_ERR "sc6000_cfg_write: failed!\n");
-               return -EFAULT;
+               snd_printk(KERN_ERR "sc6000_setup_board: failed!\n");
+               return -ENODEV;
+       }
+
+       sc6000_dsp_reset(vport);
+
+       if (!old) {
+               sc6000_write(vport, COMMAND_60);
+               sc6000_write(vport, 0x02);
+               sc6000_dsp_reset(vport);
        }
 
        err = sc6000_setup_board(vport, config);
@@ -386,10 +457,9 @@ static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma,
                snd_printk(KERN_ERR "sc6000_setup_board: failed!\n");
                return -ENODEV;
        }
-
        err = sc6000_init_mss(vport, config, vmss_port, mss_config);
        if (err < 0) {
-               snd_printk(KERN_ERR "Can not initialize "
+               snd_printk(KERN_ERR "Cannot initialize "
                           "Microsoft Sound System mode.\n");
                return -ENODEV;
        }
@@ -485,14 +555,16 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
        struct snd_card *card;
        struct snd_wss *chip;
        struct snd_opl3 *opl3;
-       char __iomem *vport;
+       char __iomem **vport;
        char __iomem *vmss_port;
 
 
-       err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+       err = snd_card_create(index[dev], id[dev], THIS_MODULE, sizeof(vport),
+                               &card);
        if (err < 0)
                return err;
 
+       vport = card->private_data;
        if (xirq == SNDRV_AUTO_IRQ) {
                xirq = snd_legacy_find_free_irq(possible_irqs);
                if (xirq < 0) {
@@ -517,8 +589,8 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
                err = -EBUSY;
                goto err_exit;
        }
-       vport = devm_ioport_map(devptr, port[dev], 0x10);
-       if (!vport) {
+       *vport = devm_ioport_map(devptr, port[dev], 0x10);
+       if (*vport == NULL) {
                snd_printk(KERN_ERR PFX
                           "I/O port cannot be iomaped.\n");
                err = -EBUSY;
@@ -533,7 +605,7 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
                goto err_unmap1;
        }
        vmss_port = devm_ioport_map(devptr, mss_port[dev], 4);
-       if (!vport) {
+       if (!vmss_port) {
                snd_printk(KERN_ERR PFX
                           "MSS port I/O cannot be iomaped.\n");
                err = -EBUSY;
@@ -544,7 +616,7 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
                   port[dev], xirq, xdma,
                   mpu_irq[dev] == SNDRV_AUTO_IRQ ? 0 : mpu_irq[dev]);
 
-       err = sc6000_init_board(vport, xirq, xdma, vmss_port, mpu_irq[dev]);
+       err = sc6000_init_board(*vport, vmss_port, dev);
        if (err < 0)
                goto err_unmap2;
 
@@ -552,7 +624,6 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
                             WSS_HW_DETECT, 0, &chip);
        if (err < 0)
                goto err_unmap2;
-       card->private_data = chip;
 
        err = snd_wss_pcm(chip, 0, NULL);
        if (err < 0) {
@@ -608,6 +679,7 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
        return 0;
 
 err_unmap2:
+       sc6000_setup_board(*vport, 0);
        release_region(mss_port[dev], 4);
 err_unmap1:
        release_region(port[dev], 0x10);
@@ -618,11 +690,17 @@ err_exit:
 
 static int __devexit snd_sc6000_remove(struct device *devptr, unsigned int dev)
 {
+       struct snd_card *card = dev_get_drvdata(devptr);
+       char __iomem **vport = card->private_data;
+
+       if (sc6000_setup_board(*vport, 0) < 0)
+               snd_printk(KERN_WARNING "sc6000_setup_board failed on exit!\n");
+
        release_region(port[dev], 0x10);
        release_region(mss_port[dev], 4);
 
-       snd_card_free(dev_get_drvdata(devptr));
        dev_set_drvdata(devptr, NULL);
+       snd_card_free(card);
        return 0;
 }
 
index 66f3b48ceafcdcbedb7e4700a4ae8a341283cd3f..e497525bc11bcf50612c071bdfc3c5ba186bacf1 100644 (file)
@@ -619,8 +619,7 @@ static int snd_sgio2audio_pcm_hw_params(struct snd_pcm_substream *substream,
 /* hw_free callback */
 static int snd_sgio2audio_pcm_hw_free(struct snd_pcm_substream *substream)
 {
-       if (substream->runtime->dma_area)
-               vfree(substream->runtime->dma_area);
+       vfree(substream->runtime->dma_area);
        substream->runtime->dma_area = NULL;
        return 0;
 }
index 6055fd6d3b38ee90ba6d19895363ef33db38f275..e924492df21dc1b469f7b41020ca890737fa9857 100644 (file)
@@ -935,7 +935,7 @@ snd_harmony_create(struct snd_card *card,
        h->iobase = ioremap_nocache(padev->hpa.start, HARMONY_SIZE);
        if (h->iobase == NULL) {
                printk(KERN_ERR PFX "unable to remap hpa 0x%lx\n",
-                      padev->hpa.start);
+                      (unsigned long)padev->hpa.start);
                err = -EBUSY;
                goto free_and_ret;
        }
@@ -1020,7 +1020,7 @@ static struct parisc_driver snd_harmony_driver = {
        .name = "harmony",
        .id_table = snd_harmony_devtable,
        .probe = snd_harmony_probe,
-       .remove = snd_harmony_remove,
+       .remove = __devexit_p(snd_harmony_remove),
 };
 
 static int __init 
index 93422e3a3f0cc2f642a5a30cfd09ee89cbb55a43..748f6b7d90b7715729d131e831f0299d0d8384fa 100644 (file)
@@ -275,6 +275,16 @@ config SND_CS5535AUDIO
          To compile this driver as a module, choose M here: the module
          will be called snd-cs5535audio.
 
+config SND_CTXFI
+       tristate "Creative Sound Blaster X-Fi"
+       select SND_PCM
+       help
+         If you want to use soundcards based on Creative Sound Blastr X-Fi
+         boards with 20k1 or 20k2 chips, say Y here.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-ctxfi.
+
 config SND_DARLA20
        tristate "(Echoaudio) Darla20"
        select FW_LOADER
@@ -532,6 +542,9 @@ config SND_HDSP
          To compile this driver as a module, choose M here: the module
          will be called snd-hdsp.
 
+comment "Don't forget to add built-in firmwares for HDSP driver"
+       depends on SND_HDSP=y
+
 config SND_HDSPM
        tristate "RME Hammerfall DSP MADI"
        select SND_HWDEP
@@ -622,6 +635,16 @@ config SND_KORG1212
          To compile this driver as a module, choose M here: the module
          will be called snd-korg1212.
 
+config SND_LX6464ES
+       tristate "Digigram LX6464ES"
+       select SND_PCM
+       help
+         Say Y here to include support for Digigram LX6464ES boards.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-lx6464es.
+
+
 config SND_MAESTRO3
        tristate "ESS Allegro/Maestro3"
        select SND_AC97_CODEC
@@ -764,8 +787,8 @@ config SND_VIRTUOSO
        select SND_OXYGEN_LIB
        help
          Say Y here to include support for sound cards based on the
-         Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, and
-         Essence STX.
+         Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X,
+         Essence ST (Deluxe), and Essence STX.
          Support for the HDAV1.3 (Deluxe) is very experimental.
 
          To compile this driver as a module, choose M here: the module
index 65b25d221cd2795135f265dcbb05d47f1dfc8a8f..ecfc609d2b9f427b04330bc31f0acbd35a0126ac 100644 (file)
@@ -59,9 +59,11 @@ obj-$(CONFIG_SND) += \
        ali5451/ \
        au88x0/ \
        aw2/ \
+       ctxfi/ \
        ca0106/ \
        cs46xx/ \
        cs5535audio/ \
+       lx6464es/ \
        echoaudio/ \
        emu10k1/ \
        hda/ \
index 3906f5afe27a3a9b27c9f0bf90e36108664d4925..23f49f356e0f0375a784de99c05b832c2bc66ad4 100644 (file)
@@ -1255,8 +1255,8 @@ static int inline vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma)
        int temp;
 
        temp = hwread(vortex->mmio, VORTEX_ADBDMA_STAT + (adbdma << 2));
-       temp = (dma->period_virt * dma->period_bytes) + (temp & POS_MASK);
-       return (temp);
+       temp = (dma->period_virt * dma->period_bytes) + (temp & (dma->period_bytes - 1));
+       return temp;
 }
 
 static void vortex_adbdma_startfifo(vortex_t * vortex, int adbdma)
@@ -1504,8 +1504,7 @@ static int inline vortex_wtdma_getlinearpos(vortex_t * vortex, int wtdma)
        int temp;
 
        temp = hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2));
-       //temp = (temp & POS_MASK) + (((temp>>WT_SUBBUF_SHIFT) & WT_SUBBUF_MASK)*(dma->cfg0&POS_MASK));
-       temp = (temp & POS_MASK) + ((dma->period_virt) * (dma->period_bytes));
+       temp = (dma->period_virt * dma->period_bytes) + (temp & (dma->period_bytes - 1));
        return temp;
 }
 
@@ -2441,7 +2440,8 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
                spin_lock(&vortex->lock);
                for (i = 0; i < NR_ADB; i++) {
                        if (vortex->dma_adb[i].fifo_status == FIFO_START) {
-                               if (vortex_adbdma_bufshift(vortex, i)) ;
+                               if (!vortex_adbdma_bufshift(vortex, i))
+                                       continue;
                                spin_unlock(&vortex->lock);
                                snd_pcm_period_elapsed(vortex->dma_adb[i].
                                                       substream);
index ce3f2e90f4d7545ac350b5a00fdb29bc0ef5d31a..24585c6c6d019ea7e1a1d2405f942aec8f86c1f3 100644 (file)
@@ -810,6 +810,8 @@ static struct pci_device_id snd_bt87x_ids[] = {
        BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x107d, 0x6606, GENERIC),
        /* Voodoo TV 200 */
        BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x121a, 0x3000, GENERIC),
+       /* Askey Computer Corp. MagicTView'99 */
+       BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x144f, 0x3000, GENERIC),
        /* AVerMedia Studio No. 103, 203, ...? */
        BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x1461, 0x0003, AVPHONE98),
        /* Prolink PixelView PV-M4900 */
index bfac30f7929f3094b0f95c343f2659cd0381a4a0..57b992a5c05724c6b67e18668464e592d8b82660 100644 (file)
@@ -1319,7 +1319,6 @@ static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
         }
 
        pcm->info_flags = 0;
-       pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
        strcpy(pcm->name, "CA0106");
 
        for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; 
index c111efe61c3cc224208828552e1148c5ad689bb7..c8c6f437f5b394d2a37abf335a6b931a3e537ffb 100644 (file)
@@ -739,7 +739,7 @@ static int __devinit rename_ctl(struct snd_card *card, const char *src, const ch
        } while (0)
 
 static __devinitdata
-DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 50, 1);
+DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 25, 1);
 
 static char *slave_vols[] __devinitdata = {
        "Analog Front Playback Volume",
@@ -841,6 +841,9 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
                                              snd_ca0106_master_db_scale);
        if (!vmaster)
                return -ENOMEM;
+       err = snd_ctl_add(card, vmaster);
+       if (err < 0)
+               return err;
        add_slaves(card, vmaster, slave_vols);
 
        if (emu->details->spi_dac == 1) {
@@ -848,8 +851,13 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
                                                      NULL);
                if (!vmaster)
                        return -ENOMEM;
+               err = snd_ctl_add(card, vmaster);
+               if (err < 0)
+                       return err;
                add_slaves(card, vmaster, slave_sws);
        }
+
+       strcpy(card->mixername, "CA0106");
         return 0;
 }
 
diff --git a/sound/pci/ctxfi/Makefile b/sound/pci/ctxfi/Makefile
new file mode 100644 (file)
index 0000000..15075f8
--- /dev/null
@@ -0,0 +1,5 @@
+snd-ctxfi-objs := xfi.o ctatc.o ctvmem.o ctpcm.o ctmixer.o ctresource.o \
+       ctsrc.o ctamixer.o ctdaio.o ctimap.o cthardware.o cttimer.o \
+       cthw20k2.o cthw20k1.o
+
+obj-$(CONFIG_SND_CTXFI) += snd-ctxfi.o
diff --git a/sound/pci/ctxfi/ct20k1reg.h b/sound/pci/ctxfi/ct20k1reg.h
new file mode 100644 (file)
index 0000000..f2e34e3
--- /dev/null
@@ -0,0 +1,636 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ */
+
+#ifndef CT20K1REG_H
+#define CT20k1REG_H
+
+/* 20k1 registers */
+#define        DSPXRAM_START                   0x000000
+#define        DSPXRAM_END                     0x013FFC
+#define        DSPAXRAM_START                  0x020000
+#define        DSPAXRAM_END                    0x023FFC
+#define        DSPYRAM_START                   0x040000
+#define        DSPYRAM_END                     0x04FFFC
+#define        DSPAYRAM_START                  0x020000
+#define        DSPAYRAM_END                    0x063FFC
+#define        DSPMICRO_START                  0x080000
+#define        DSPMICRO_END                    0x0B3FFC
+#define        DSP0IO_START                    0x100000
+#define        DSP0IO_END                      0x101FFC
+#define        AUDIORINGIPDSP0_START           0x100000
+#define        AUDIORINGIPDSP0_END             0x1003FC
+#define        AUDIORINGOPDSP0_START           0x100400
+#define        AUDIORINGOPDSP0_END             0x1007FC
+#define        AUDPARARINGIODSP0_START         0x100800
+#define        AUDPARARINGIODSP0_END           0x100BFC
+#define        DSP0LOCALHWREG_START            0x100C00
+#define        DSP0LOCALHWREG_END              0x100C3C
+#define        DSP0XYRAMAGINDEX_START          0x100C40
+#define        DSP0XYRAMAGINDEX_END            0x100C5C
+#define        DSP0XYRAMAGMDFR_START           0x100C60
+#define        DSP0XYRAMAGMDFR_END             0x100C7C
+#define        DSP0INTCONTLVEC_START           0x100C80
+#define        DSP0INTCONTLVEC_END             0x100CD8
+#define        INTCONTLGLOBALREG_START         0x100D1C
+#define        INTCONTLGLOBALREG_END           0x100D3C
+#define                HOSTINTFPORTADDRCONTDSP0        0x100D40
+#define                HOSTINTFPORTDATADSP0            0x100D44
+#define                TIME0PERENBDSP0                 0x100D60
+#define                TIME0COUNTERDSP0                0x100D64
+#define                TIME1PERENBDSP0                 0x100D68
+#define                TIME1COUNTERDSP0                0x100D6C
+#define                TIME2PERENBDSP0                 0x100D70
+#define                TIME2COUNTERDSP0                0x100D74
+#define                TIME3PERENBDSP0                 0x100D78
+#define                TIME3COUNTERDSP0                0x100D7C
+#define        XRAMINDOPERREFNOUP_STARTDSP0    0x100D80
+#define        XRAMINDOPERREFNOUP_ENDDSP0      0x100D9C
+#define        XRAMINDOPERREFUP_STARTDSP0      0x100DA0
+#define        XRAMINDOPERREFUP_ENDDSP0        0x100DBC
+#define        YRAMINDOPERREFNOUP_STARTDSP0    0x100DC0
+#define        YRAMINDOPERREFNOUP_ENDDSP0      0x100DDC
+#define        YRAMINDOPERREFUP_STARTDSP0      0x100DE0
+#define        YRAMINDOPERREFUP_ENDDSP0        0x100DFC
+#define        DSP0CONDCODE                    0x100E00
+#define        DSP0STACKFLAG                   0x100E04
+#define        DSP0PROGCOUNTSTACKPTREG         0x100E08
+#define        DSP0PROGCOUNTSTACKDATAREG       0x100E0C
+#define        DSP0CURLOOPADDRREG              0x100E10
+#define        DSP0CURLOOPCOUNT                0x100E14
+#define        DSP0TOPLOOPCOUNTSTACK           0x100E18
+#define        DSP0TOPLOOPADDRSTACK            0x100E1C
+#define        DSP0LOOPSTACKPTR                0x100E20
+#define        DSP0STASSTACKDATAREG            0x100E24
+#define        DSP0STASSTACKPTR                0x100E28
+#define        DSP0PROGCOUNT                   0x100E2C
+#define        GLOBDSPDEBGREG                  0x100E30
+#define        GLOBDSPBREPTRREG                0x100E30
+#define        DSP0XYRAMBASE_START             0x100EA0
+#define        DSP0XYRAMBASE_END               0x100EBC
+#define        DSP0XYRAMLENG_START             0x100EC0
+#define        DSP0XYRAMLENG_END               0x100EDC
+#define                SEMAPHOREREGDSP0                0x100EE0
+#define                DSP0INTCONTMASKREG              0x100EE4
+#define                DSP0INTCONTPENDREG              0x100EE8
+#define                DSP0INTCONTSERVINT              0x100EEC
+#define                DSPINTCONTEXTINTMODREG          0x100EEC
+#define                GPIODSP0                        0x100EFC
+#define        DMADSPBASEADDRREG_STARTDSP0     0x100F00
+#define        DMADSPBASEADDRREG_ENDDSP0       0x100F1C
+#define        DMAHOSTBASEADDRREG_STARTDSP0    0x100F20
+#define        DMAHOSTBASEADDRREG_ENDDSP0      0x100F3C
+#define        DMADSPCURADDRREG_STARTDSP0      0x100F40
+#define        DMADSPCURADDRREG_ENDDSP0        0x100F5C
+#define        DMAHOSTCURADDRREG_STARTDSP0     0x100F60
+#define        DMAHOSTCURADDRREG_ENDDSP0       0x100F7C
+#define        DMATANXCOUNTREG_STARTDSP0       0x100F80
+#define        DMATANXCOUNTREG_ENDDSP0         0x100F9C
+#define        DMATIMEBUGREG_STARTDSP0         0x100FA0
+#define        DMATIMEBUGREG_ENDDSP0           0x100FAC
+#define        DMACNTLMODFREG_STARTDSP0        0x100FA0
+#define        DMACNTLMODFREG_ENDDSP0          0x100FAC
+
+#define        DMAGLOBSTATSREGDSP0             0x100FEC
+#define        DSP0XGPRAM_START                0x101000
+#define        DSP0XGPRAM_END                  0x1017FC
+#define        DSP0YGPRAM_START                0x101800
+#define        DSP0YGPRAM_END                  0x101FFC
+
+
+
+
+#define        AUDIORINGIPDSP1_START           0x102000
+#define        AUDIORINGIPDSP1_END             0x1023FC
+#define        AUDIORINGOPDSP1_START           0x102400
+#define        AUDIORINGOPDSP1_END             0x1027FC
+#define        AUDPARARINGIODSP1_START         0x102800
+#define        AUDPARARINGIODSP1_END           0x102BFC
+#define        DSP1LOCALHWREG_START            0x102C00
+#define        DSP1LOCALHWREG_END              0x102C3C
+#define        DSP1XYRAMAGINDEX_START          0x102C40
+#define        DSP1XYRAMAGINDEX_END            0x102C5C
+#define        DSP1XYRAMAGMDFR_START           0x102C60
+#define        DSP1XYRAMAGMDFR_END             0x102C7C
+#define        DSP1INTCONTLVEC_START           0x102C80
+#define        DSP1INTCONTLVEC_END             0x102CD8
+#define                HOSTINTFPORTADDRCONTDSP1        0x102D40
+#define                HOSTINTFPORTDATADSP1            0x102D44
+#define                TIME0PERENBDSP1                 0x102D60
+#define                TIME0COUNTERDSP1                0x102D64
+#define                TIME1PERENBDSP1                 0x102D68
+#define                TIME1COUNTERDSP1                0x102D6C
+#define                TIME2PERENBDSP1                 0x102D70
+#define                TIME2COUNTERDSP1                0x102D74
+#define                TIME3PERENBDSP1                 0x102D78
+#define                TIME3COUNTERDSP1                0x102D7C
+#define        XRAMINDOPERREFNOUP_STARTDSP1    0x102D80
+#define        XRAMINDOPERREFNOUP_ENDDSP1      0x102D9C
+#define        XRAMINDOPERREFUP_STARTDSP1      0x102DA0
+#define        XRAMINDOPERREFUP_ENDDSP1        0x102DBC
+#define        YRAMINDOPERREFNOUP_STARTDSP1    0x102DC0
+#define        YRAMINDOPERREFNOUP_ENDDSP1      0x102DDC
+#define        YRAMINDOPERREFUP_STARTDSP1      0x102DE0
+#define        YRAMINDOPERREFUP_ENDDSP1        0x102DFC
+
+#define        DSP1CONDCODE                    0x102E00
+#define        DSP1STACKFLAG                   0x102E04
+#define        DSP1PROGCOUNTSTACKPTREG         0x102E08
+#define        DSP1PROGCOUNTSTACKDATAREG       0x102E0C
+#define        DSP1CURLOOPADDRREG              0x102E10
+#define        DSP1CURLOOPCOUNT                0x102E14
+#define        DSP1TOPLOOPCOUNTSTACK           0x102E18
+#define        DSP1TOPLOOPADDRSTACK            0x102E1C
+#define        DSP1LOOPSTACKPTR                0x102E20
+#define        DSP1STASSTACKDATAREG            0x102E24
+#define        DSP1STASSTACKPTR                0x102E28
+#define        DSP1PROGCOUNT                   0x102E2C
+#define        DSP1XYRAMBASE_START             0x102EA0
+#define        DSP1XYRAMBASE_END               0x102EBC
+#define        DSP1XYRAMLENG_START             0x102EC0
+#define        DSP1XYRAMLENG_END               0x102EDC
+#define                SEMAPHOREREGDSP1                0x102EE0
+#define                DSP1INTCONTMASKREG              0x102EE4
+#define                DSP1INTCONTPENDREG              0x102EE8
+#define                DSP1INTCONTSERVINT              0x102EEC
+#define                GPIODSP1                        0x102EFC
+#define        DMADSPBASEADDRREG_STARTDSP1     0x102F00
+#define        DMADSPBASEADDRREG_ENDDSP1       0x102F1C
+#define        DMAHOSTBASEADDRREG_STARTDSP1    0x102F20
+#define        DMAHOSTBASEADDRREG_ENDDSP1      0x102F3C
+#define        DMADSPCURADDRREG_STARTDSP1      0x102F40
+#define        DMADSPCURADDRREG_ENDDSP1        0x102F5C
+#define        DMAHOSTCURADDRREG_STARTDSP1     0x102F60
+#define        DMAHOSTCURADDRREG_ENDDSP1       0x102F7C
+#define        DMATANXCOUNTREG_STARTDSP1       0x102F80
+#define        DMATANXCOUNTREG_ENDDSP1         0x102F9C
+#define        DMATIMEBUGREG_STARTDSP1         0x102FA0
+#define        DMATIMEBUGREG_ENDDSP1           0x102FAC
+#define        DMACNTLMODFREG_STARTDSP1        0x102FA0
+#define        DMACNTLMODFREG_ENDDSP1          0x102FAC
+
+#define        DMAGLOBSTATSREGDSP1             0x102FEC
+#define        DSP1XGPRAM_START                0x103000
+#define        DSP1XGPRAM_END                  0x1033FC
+#define        DSP1YGPRAM_START                0x103400
+#define        DSP1YGPRAM_END                  0x1037FC
+
+
+
+#define        AUDIORINGIPDSP2_START           0x104000
+#define        AUDIORINGIPDSP2_END             0x1043FC
+#define        AUDIORINGOPDSP2_START           0x104400
+#define        AUDIORINGOPDSP2_END             0x1047FC
+#define        AUDPARARINGIODSP2_START         0x104800
+#define        AUDPARARINGIODSP2_END           0x104BFC
+#define        DSP2LOCALHWREG_START            0x104C00
+#define        DSP2LOCALHWREG_END              0x104C3C
+#define        DSP2XYRAMAGINDEX_START          0x104C40
+#define        DSP2XYRAMAGINDEX_END            0x104C5C
+#define        DSP2XYRAMAGMDFR_START           0x104C60
+#define        DSP2XYRAMAGMDFR_END             0x104C7C
+#define        DSP2INTCONTLVEC_START           0x104C80
+#define        DSP2INTCONTLVEC_END             0x104CD8
+#define                HOSTINTFPORTADDRCONTDSP2        0x104D40
+#define                HOSTINTFPORTDATADSP2            0x104D44
+#define                TIME0PERENBDSP2                 0x104D60
+#define                TIME0COUNTERDSP2                0x104D64
+#define                TIME1PERENBDSP2                 0x104D68
+#define                TIME1COUNTERDSP2                0x104D6C
+#define                TIME2PERENBDSP2                 0x104D70
+#define                TIME2COUNTERDSP2                0x104D74
+#define                TIME3PERENBDSP2                 0x104D78
+#define                TIME3COUNTERDSP2                0x104D7C
+#define        XRAMINDOPERREFNOUP_STARTDSP2    0x104D80
+#define        XRAMINDOPERREFNOUP_ENDDSP2      0x104D9C
+#define        XRAMINDOPERREFUP_STARTDSP2      0x104DA0
+#define        XRAMINDOPERREFUP_ENDDSP2        0x104DBC
+#define        YRAMINDOPERREFNOUP_STARTDSP2    0x104DC0
+#define        YRAMINDOPERREFNOUP_ENDDSP2      0x104DDC
+#define        YRAMINDOPERREFUP_STARTDSP2      0x104DE0
+#define        YRAMINDOPERREFUP_ENDDSP2        0x104DFC
+#define        DSP2CONDCODE                    0x104E00
+#define        DSP2STACKFLAG                   0x104E04
+#define        DSP2PROGCOUNTSTACKPTREG         0x104E08
+#define        DSP2PROGCOUNTSTACKDATAREG       0x104E0C
+#define        DSP2CURLOOPADDRREG              0x104E10
+#define        DSP2CURLOOPCOUNT                0x104E14
+#define        DSP2TOPLOOPCOUNTSTACK           0x104E18
+#define        DSP2TOPLOOPADDRSTACK            0x104E1C
+#define        DSP2LOOPSTACKPTR                0x104E20
+#define        DSP2STASSTACKDATAREG            0x104E24
+#define        DSP2STASSTACKPTR                0x104E28
+#define        DSP2PROGCOUNT                   0x104E2C
+#define        DSP2XYRAMBASE_START             0x104EA0
+#define        DSP2XYRAMBASE_END               0x104EBC
+#define        DSP2XYRAMLENG_START             0x104EC0
+#define        DSP2XYRAMLENG_END               0x104EDC
+#define                SEMAPHOREREGDSP2                0x104EE0
+#define                DSP2INTCONTMASKREG              0x104EE4
+#define                DSP2INTCONTPENDREG              0x104EE8
+#define                DSP2INTCONTSERVINT              0x104EEC
+#define                GPIODSP2                        0x104EFC
+#define        DMADSPBASEADDRREG_STARTDSP2     0x104F00
+#define        DMADSPBASEADDRREG_ENDDSP2       0x104F1C
+#define        DMAHOSTBASEADDRREG_STARTDSP2    0x104F20
+#define        DMAHOSTBASEADDRREG_ENDDSP2      0x104F3C
+#define        DMADSPCURADDRREG_STARTDSP2      0x104F40
+#define        DMADSPCURADDRREG_ENDDSP2        0x104F5C
+#define        DMAHOSTCURADDRREG_STARTDSP2     0x104F60
+#define        DMAHOSTCURADDRREG_ENDDSP2       0x104F7C
+#define        DMATANXCOUNTREG_STARTDSP2       0x104F80
+#define        DMATANXCOUNTREG_ENDDSP2         0x104F9C
+#define        DMATIMEBUGREG_STARTDSP2         0x104FA0
+#define        DMATIMEBUGREG_ENDDSP2           0x104FAC
+#define        DMACNTLMODFREG_STARTDSP2        0x104FA0
+#define        DMACNTLMODFREG_ENDDSP2          0x104FAC
+
+#define        DMAGLOBSTATSREGDSP2             0x104FEC
+#define        DSP2XGPRAM_START                0x105000
+#define        DSP2XGPRAM_END                  0x1051FC
+#define        DSP2YGPRAM_START                0x105800
+#define        DSP2YGPRAM_END                  0x1059FC
+
+
+
+#define        AUDIORINGIPDSP3_START           0x106000
+#define        AUDIORINGIPDSP3_END             0x1063FC
+#define        AUDIORINGOPDSP3_START           0x106400
+#define        AUDIORINGOPDSP3_END             0x1067FC
+#define        AUDPARARINGIODSP3_START         0x106800
+#define        AUDPARARINGIODSP3_END           0x106BFC
+#define        DSP3LOCALHWREG_START            0x106C00
+#define        DSP3LOCALHWREG_END              0x106C3C
+#define        DSP3XYRAMAGINDEX_START          0x106C40
+#define        DSP3XYRAMAGINDEX_END            0x106C5C
+#define        DSP3XYRAMAGMDFR_START           0x106C60
+#define        DSP3XYRAMAGMDFR_END             0x106C7C
+#define        DSP3INTCONTLVEC_START           0x106C80
+#define        DSP3INTCONTLVEC_END             0x106CD8
+#define                HOSTINTFPORTADDRCONTDSP3        0x106D40
+#define                HOSTINTFPORTDATADSP3            0x106D44
+#define                TIME0PERENBDSP3                 0x106D60
+#define                TIME0COUNTERDSP3                0x106D64
+#define                TIME1PERENBDSP3                 0x106D68
+#define                TIME1COUNTERDSP3                0x106D6C
+#define                TIME2PERENBDSP3                 0x106D70
+#define                TIME2COUNTERDSP3                0x106D74
+#define                TIME3PERENBDSP3                 0x106D78
+#define                TIME3COUNTERDSP3                0x106D7C
+#define        XRAMINDOPERREFNOUP_STARTDSP3    0x106D80
+#define        XRAMINDOPERREFNOUP_ENDDSP3      0x106D9C
+#define        XRAMINDOPERREFUP_STARTDSP3      0x106DA0
+#define        XRAMINDOPERREFUP_ENDDSP3        0x106DBC
+#define        YRAMINDOPERREFNOUP_STARTDSP3    0x106DC0
+#define        YRAMINDOPERREFNOUP_ENDDSP3      0x106DDC
+#define        YRAMINDOPERREFUP_STARTDSP3      0x106DE0
+#define        YRAMINDOPERREFUP_ENDDSP3        0x100DFC
+
+#define        DSP3CONDCODE                    0x106E00
+#define        DSP3STACKFLAG                   0x106E04
+#define        DSP3PROGCOUNTSTACKPTREG         0x106E08
+#define        DSP3PROGCOUNTSTACKDATAREG       0x106E0C
+#define        DSP3CURLOOPADDRREG              0x106E10
+#define        DSP3CURLOOPCOUNT                0x106E14
+#define        DSP3TOPLOOPCOUNTSTACK           0x106E18
+#define        DSP3TOPLOOPADDRSTACK            0x106E1C
+#define        DSP3LOOPSTACKPTR                0x106E20
+#define        DSP3STASSTACKDATAREG            0x106E24
+#define        DSP3STASSTACKPTR                0x106E28
+#define        DSP3PROGCOUNT                   0x106E2C
+#define        DSP3XYRAMBASE_START             0x106EA0
+#define        DSP3XYRAMBASE_END               0x106EBC
+#define        DSP3XYRAMLENG_START             0x106EC0
+#define        DSP3XYRAMLENG_END               0x106EDC
+#define                SEMAPHOREREGDSP3                0x106EE0
+#define                DSP3INTCONTMASKREG              0x106EE4
+#define                DSP3INTCONTPENDREG              0x106EE8
+#define                DSP3INTCONTSERVINT              0x106EEC
+#define                GPIODSP3                        0x106EFC
+#define        DMADSPBASEADDRREG_STARTDSP3     0x106F00
+#define        DMADSPBASEADDRREG_ENDDSP3       0x106F1C
+#define        DMAHOSTBASEADDRREG_STARTDSP3    0x106F20
+#define        DMAHOSTBASEADDRREG_ENDDSP3      0x106F3C
+#define        DMADSPCURADDRREG_STARTDSP3      0x106F40
+#define        DMADSPCURADDRREG_ENDDSP3        0x106F5C
+#define        DMAHOSTCURADDRREG_STARTDSP3     0x106F60
+#define        DMAHOSTCURADDRREG_ENDDSP3       0x106F7C
+#define        DMATANXCOUNTREG_STARTDSP3       0x106F80
+#define        DMATANXCOUNTREG_ENDDSP3         0x106F9C
+#define        DMATIMEBUGREG_STARTDSP3         0x106FA0
+#define        DMATIMEBUGREG_ENDDSP3           0x106FAC
+#define        DMACNTLMODFREG_STARTDSP3        0x106FA0
+#define        DMACNTLMODFREG_ENDDSP3          0x106FAC
+
+#define        DMAGLOBSTATSREGDSP3             0x106FEC
+#define        DSP3XGPRAM_START                0x107000
+#define        DSP3XGPRAM_END                  0x1071FC
+#define        DSP3YGPRAM_START                0x107800
+#define        DSP3YGPRAM_END                  0x1079FC
+
+/* end of DSP reg definitions */
+
+#define        DSPAIMAP_START                  0x108000
+#define        DSPAIMAP_END                    0x1083FC
+#define        DSPPIMAP_START                  0x108400
+#define        DSPPIMAP_END                    0x1087FC
+#define        DSPPOMAP_START                  0x108800
+#define        DSPPOMAP_END                    0x108BFC
+#define        DSPPOCTL                        0x108C00
+#define        TKCTL_START                     0x110000
+#define        TKCTL_END                       0x110FFC
+#define        TKCC_START                      0x111000
+#define        TKCC_END                        0x111FFC
+#define        TKIMAP_START                    0x112000
+#define        TKIMAP_END                      0x112FFC
+#define                TKDCTR16                        0x113000
+#define                TKPB16                          0x113004
+#define                TKBS16                          0x113008
+#define                TKDCTR32                        0x11300C
+#define                TKPB32                          0x113010
+#define                TKBS32                          0x113014
+#define                ICDCTR16                        0x113018
+#define                ITBS16                          0x11301C
+#define                ICDCTR32                        0x113020
+#define                ITBS32                          0x113024
+#define                ITSTART                         0x113028
+#define                TKSQ                            0x11302C
+
+#define                TKSCCTL_START                   0x114000
+#define                TKSCCTL_END                     0x11403C
+#define                TKSCADR_START                   0x114100
+#define                TKSCADR_END                     0x11413C
+#define                TKSCDATAX_START                 0x114800
+#define                TKSCDATAX_END                   0x1149FC
+#define                TKPCDATAX_START                 0x120000
+#define                TKPCDATAX_END                   0x12FFFC
+
+#define                MALSA                           0x130000
+#define                MAPPHA                          0x130004
+#define                MAPPLA                          0x130008
+#define                MALSB                           0x130010
+#define                MAPPHB                          0x130014
+#define                MAPPLB                          0x130018
+
+#define        TANSPORTMAPABREGS_START         0x130020
+#define        TANSPORTMAPABREGS_END           0x13A2FC
+
+#define                PTPAHX                          0x13B000
+#define                PTPALX                          0x13B004
+
+#define                TANSPPAGETABLEPHYADDR015_START  0x13B008
+#define                TANSPPAGETABLEPHYADDR015_END    0x13B07C
+#define                TRNQADRX_START                  0x13B100
+#define                TRNQADRX_END                    0x13B13C
+#define                TRNQTIMX_START                  0x13B200
+#define                TRNQTIMX_END                    0x13B23C
+#define                TRNQAPARMX_START                0x13B300
+#define                TRNQAPARMX_END                  0x13B33C
+
+#define                TRNQCNT                         0x13B400
+#define                TRNCTL                          0x13B404
+#define                TRNIS                           0x13B408
+#define                TRNCURTS                        0x13B40C
+
+#define                AMOP_START                      0x140000
+#define                AMOPLO                          0x140000
+#define                AMOPHI                          0x140004
+#define                AMOP_END                        0x147FFC
+#define                PMOP_START                      0x148000
+#define                PMOPLO                          0x148000
+#define                PMOPHI                          0x148004
+#define                PMOP_END                        0x14FFFC
+#define                PCURR_START                     0x150000
+#define                PCURR_END                       0x153FFC
+#define                PTRAG_START                     0x154000
+#define                PTRAG_END                       0x157FFC
+#define                PSR_START                       0x158000
+#define                PSR_END                         0x15BFFC
+
+#define                PFSTAT4SEG_START                0x160000
+#define                PFSTAT4SEG_END                  0x160BFC
+#define                PFSTAT2SEG_START                0x160C00
+#define                PFSTAT2SEG_END                  0x1617FC
+#define                PFTARG4SEG_START                0x164000
+#define                PFTARG4SEG_END                  0x164BFC
+#define                PFTARG2SEG_START                0x164C00
+#define                PFTARG2SEG_END                  0x1657FC
+#define                PFSR4SEG_START                  0x168000
+#define                PFSR4SEG_END                    0x168BFC
+#define                PFSR2SEG_START                  0x168C00
+#define                PFSR2SEG_END                    0x1697FC
+#define                PCURRMS4SEG_START               0x16C000
+#define                PCURRMS4SEG_END                 0x16CCFC
+#define                PCURRMS2SEG_START               0x16CC00
+#define                PCURRMS2SEG_END                 0x16D7FC
+#define                PTARGMS4SEG_START               0x170000
+#define                PTARGMS4SEG_END                 0x172FFC
+#define                PTARGMS2SEG_START               0x173000
+#define                PTARGMS2SEG_END                 0x1747FC
+#define                PSRMS4SEG_START                 0x170000
+#define                PSRMS4SEG_END                   0x172FFC
+#define                PSRMS2SEG_START                 0x173000
+#define                PSRMS2SEG_END                   0x1747FC
+
+#define                PRING_LO_START                  0x190000
+#define                PRING_LO_END                    0x193FFC
+#define                PRING_HI_START                  0x194000
+#define                PRING_HI_END                    0x197FFC
+#define                PRING_LO_HI_START               0x198000
+#define                PRING_LO_HI                     0x198000
+#define                PRING_LO_HI_END                 0x19BFFC
+
+#define                PINTFIFO                        0x1A0000
+#define                SRCCTL                          0x1B0000
+#define                SRCCCR                          0x1B0004
+#define                SRCIMAP                         0x1B0008
+#define                SRCODDC                         0x1B000C
+#define                SRCCA                           0x1B0010
+#define                SRCCF                           0x1B0014
+#define                SRCSA                           0x1B0018
+#define                SRCLA                           0x1B001C
+#define                SRCCTLSWR                       0x1B0020
+
+/* SRC HERE */
+#define                SRCALBA                         0x1B002C
+#define                SRCMCTL                         0x1B012C
+#define                SRCCERR                         0x1B022C
+#define                SRCITB                          0x1B032C
+#define                SRCIPM                          0x1B082C
+#define                SRCIP                           0x1B102C
+#define                SRCENBSTAT                      0x1B202C
+#define                SRCENBLO                        0x1B212C
+#define                SRCENBHI                        0x1B222C
+#define                SRCENBS                         0x1B232C
+#define                SRCENB                          0x1B282C
+#define                SRCENB07                        0x1B282C
+#define                SRCENBS07                       0x1B302C
+
+#define                SRCDN0Z                         0x1B0030
+#define                SRCDN0Z0                        0x1B0030
+#define                SRCDN0Z1                        0x1B0034
+#define                SRCDN0Z2                        0x1B0038
+#define                SRCDN0Z3                        0x1B003C
+#define                SRCDN1Z                         0x1B0040
+#define                SRCDN1Z0                        0x1B0040
+#define                SRCDN1Z1                        0x1B0044
+#define                SRCDN1Z2                        0x1B0048
+#define                SRCDN1Z3                        0x1B004C
+#define                SRCDN1Z4                        0x1B0050
+#define                SRCDN1Z5                        0x1B0054
+#define                SRCDN1Z6                        0x1B0058
+#define                SRCDN1Z7                        0x1B005C
+#define                SRCUPZ                          0x1B0060
+#define                SRCUPZ0                         0x1B0060
+#define                SRCUPZ1                         0x1B0064
+#define                SRCUPZ2                         0x1B0068
+#define                SRCUPZ3                         0x1B006C
+#define                SRCUPZ4                         0x1B0070
+#define                SRCUPZ5                         0x1B0074
+#define                SRCUPZ6                         0x1B0078
+#define                SRCUPZ7                         0x1B007C
+#define                SRCCD0                          0x1B0080
+#define                SRCCD1                          0x1B0084
+#define                SRCCD2                          0x1B0088
+#define                SRCCD3                          0x1B008C
+#define                SRCCD4                          0x1B0090
+#define                SRCCD5                          0x1B0094
+#define                SRCCD6                          0x1B0098
+#define                SRCCD7                          0x1B009C
+#define                SRCCD8                          0x1B00A0
+#define                SRCCD9                          0x1B00A4
+#define                SRCCDA                          0x1B00A8
+#define                SRCCDB                          0x1B00AC
+#define                SRCCDC                          0x1B00B0
+#define                SRCCDD                          0x1B00B4
+#define                SRCCDE                          0x1B00B8
+#define                SRCCDF                          0x1B00BC
+#define                SRCCD10                         0x1B00C0
+#define                SRCCD11                         0x1B00C4
+#define                SRCCD12                         0x1B00C8
+#define                SRCCD13                         0x1B00CC
+#define                SRCCD14                         0x1B00D0
+#define                SRCCD15                         0x1B00D4
+#define                SRCCD16                         0x1B00D8
+#define                SRCCD17                         0x1B00DC
+#define                SRCCD18                         0x1B00E0
+#define                SRCCD19                         0x1B00E4
+#define                SRCCD1A                         0x1B00E8
+#define                SRCCD1B                         0x1B00EC
+#define                SRCCD1C                         0x1B00F0
+#define                SRCCD1D                         0x1B00F4
+#define                SRCCD1E                         0x1B00F8
+#define                SRCCD1F                         0x1B00FC
+
+#define                SRCCONTRBLOCK_START             0x1B0100
+#define                SRCCONTRBLOCK_END               0x1BFFFC
+#define                FILTOP_START    0x1C0000
+#define                FILTOP_END      0x1C05FC
+#define                FILTIMAP_START  0x1C0800
+#define                FILTIMAP_END    0x1C0DFC
+#define                FILTZ1_START    0x1C1000
+#define                FILTZ1_END      0x1C15FC
+#define                FILTZ2_START    0x1C1800
+#define                FILTZ2_END      0x1C1DFC
+#define                DAOIMAP_START   0x1C5000
+#define                DAOIMAP         0x1C5000
+#define                DAOIMAP_END     0x1C5124
+
+#define                AC97D           0x1C5400
+#define                AC97A           0x1C5404
+#define                AC97CTL         0x1C5408
+#define                I2SCTL          0x1C5420
+
+#define                SPOS            0x1C5440
+#define                SPOSA           0x1C5440
+#define                SPOSB           0x1C5444
+#define                SPOSC           0x1C5448
+#define                SPOSD           0x1C544C
+
+#define                SPISA           0x1C5450
+#define                SPISB           0x1C5454
+#define                SPISC           0x1C5458
+#define                SPISD           0x1C545C
+
+#define                SPFSCTL         0x1C5460
+
+#define                SPFS0           0x1C5468
+#define                SPFS1           0x1C546C
+#define                SPFS2           0x1C5470
+#define                SPFS3           0x1C5474
+#define                SPFS4           0x1C5478
+#define                SPFS5           0x1C547C
+
+#define                SPOCTL          0x1C5480
+#define                SPICTL          0x1C5484
+#define                SPISTS          0x1C5488
+#define                SPINTP          0x1C548C
+#define                SPINTE          0x1C5490
+#define                SPUTCTLAB       0x1C5494
+#define                SPUTCTLCD       0x1C5498
+
+#define                SRTSPA          0x1C54C0
+#define                SRTSPB          0x1C54C4
+#define                SRTSPC          0x1C54C8
+#define                SRTSPD          0x1C54CC
+
+#define                SRTSCTL         0x1C54D0
+#define                SRTSCTLA        0x1C54D0
+#define                SRTSCTLB        0x1C54D4
+#define                SRTSCTLC        0x1C54D8
+#define                SRTSCTLD        0x1C54DC
+
+#define                SRTI2S          0x1C54E0
+#define                SRTICTL         0x1C54F0
+
+#define                WC              0x1C6000
+#define                TIMR            0x1C6004
+# define       TIMR_IE         (1<<15)
+# define       TIMR_IP         (1<<14)
+
+#define                GIP             0x1C6010
+#define                GIE             0x1C6014
+#define                DIE             0x1C6018
+#define                DIC             0x1C601C
+#define                GPIO            0x1C6020
+#define                GPIOCTL         0x1C6024
+#define                GPIP            0x1C6028
+#define                GPIE            0x1C602C
+#define                DSPINT0         0x1C6030
+#define                DSPEIOC         0x1C6034
+#define                MUADAT          0x1C6040
+#define                MUACMD          0x1C6044
+#define        MUASTAT         0x1C6044
+#define                MUBDAT          0x1C6048
+#define                MUBCMD          0x1C604C
+#define                MUBSTAT         0x1C604C
+#define                UARTCMA         0x1C6050
+#define                UARTCMB         0x1C6054
+#define                UARTIP          0x1C6058
+#define                UARTIE          0x1C605C
+#define                PLLCTL          0x1C6060
+#define                PLLDCD          0x1C6064
+#define                GCTL            0x1C6070
+#define                ID0             0x1C6080
+#define                ID1             0x1C6084
+#define                ID2             0x1C6088
+#define                ID3             0x1C608C
+#define                SDRCTL          0x1C7000
+
+
+#define I2SA_L    0x0L
+#define I2SA_R    0x1L
+#define I2SB_L    0x8L
+#define I2SB_R    0x9L
+#define I2SC_L    0x10L
+#define I2SC_R    0x11L
+#define I2SD_L    0x18L
+#define I2SD_R    0x19L
+
+#endif /* CT20K1REG_H */
+
+
diff --git a/sound/pci/ctxfi/ct20k2reg.h b/sound/pci/ctxfi/ct20k2reg.h
new file mode 100644 (file)
index 0000000..2d07986
--- /dev/null
@@ -0,0 +1,85 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ */
+
+#ifndef _20K2REGISTERS_H_
+#define _20K2REGISTERS_H_
+
+
+/* Timer Registers */
+#define TIMER_TIMR          0x1B7004
+#define INTERRUPT_GIP       0x1B7010
+#define INTERRUPT_GIE       0x1B7014
+
+/* I2C Registers */
+#define I2C_IF_ADDRESS   0x1B9000
+#define I2C_IF_WDATA     0x1B9004
+#define I2C_IF_RDATA     0x1B9008
+#define I2C_IF_STATUS    0x1B900C
+#define I2C_IF_WLOCK     0x1B9010
+
+/* Global Control Registers */
+#define GLOBAL_CNTL_GCTL    0x1B7090
+
+/* PLL Registers */
+#define PLL_CTL                0x1B7080
+#define PLL_STAT               0x1B7084
+#define PLL_ENB                        0x1B7088
+
+/* SRC Registers */
+#define SRC_CTL             0x1A0000 /* 0x1A0000 + (256 * Chn) */
+#define SRC_CCR             0x1A0004 /* 0x1A0004 + (256 * Chn) */
+#define SRC_IMAP            0x1A0008 /* 0x1A0008 + (256 * Chn) */
+#define SRC_CA              0x1A0010 /* 0x1A0010 + (256 * Chn) */
+#define SRC_CF              0x1A0014 /* 0x1A0014 + (256 * Chn) */
+#define SRC_SA              0x1A0018 /* 0x1A0018 + (256 * Chn) */
+#define SRC_LA              0x1A001C /* 0x1A001C + (256 * Chn) */
+#define SRC_CTLSWR         0x1A0020 /* 0x1A0020 + (256 * Chn) */
+#define SRC_CD             0x1A0080 /* 0x1A0080 + (256 * Chn) + (4 * Regn) */
+#define SRC_MCTL               0x1A012C
+#define SRC_IP                 0x1A102C /* 0x1A102C + (256 * Regn) */
+#define SRC_ENB                        0x1A282C /* 0x1A282C + (256 * Regn) */
+#define SRC_ENBSTAT            0x1A202C
+#define SRC_ENBSA              0x1A232C
+#define SRC_DN0Z               0x1A0030
+#define SRC_DN1Z               0x1A0040
+#define SRC_UPZ                        0x1A0060
+
+/* GPIO Registers */
+#define GPIO_DATA           0x1B7020
+#define GPIO_CTRL           0x1B7024
+
+/* Virtual memory registers */
+#define VMEM_PTPAL          0x1C6300 /* 0x1C6300 + (16 * Chn) */
+#define VMEM_PTPAH          0x1C6304 /* 0x1C6304 + (16 * Chn) */
+#define VMEM_CTL            0x1C7000
+
+/* Transport Registers */
+#define TRANSPORT_ENB       0x1B6000
+#define TRANSPORT_CTL       0x1B6004
+#define TRANSPORT_INT       0x1B6008
+
+/* Audio IO */
+#define AUDIO_IO_AIM        0x1B5000 /* 0x1B5000 + (0x04 * Chn) */
+#define AUDIO_IO_TX_CTL     0x1B5400 /* 0x1B5400 + (0x40 * Chn) */
+#define AUDIO_IO_TX_CSTAT_L 0x1B5408 /* 0x1B5408 + (0x40 * Chn) */
+#define AUDIO_IO_TX_CSTAT_H 0x1B540C /* 0x1B540C + (0x40 * Chn) */
+#define AUDIO_IO_RX_CTL     0x1B5410 /* 0x1B5410 + (0x40 * Chn) */
+#define AUDIO_IO_RX_SRT_CTL 0x1B5420 /* 0x1B5420 + (0x40 * Chn) */
+#define AUDIO_IO_MCLK       0x1B5600
+#define AUDIO_IO_TX_BLRCLK  0x1B5604
+#define AUDIO_IO_RX_BLRCLK  0x1B5608
+
+/* Mixer */
+#define MIXER_AMOPLO           0x130000 /* 0x130000 + (8 * Chn) [4095 : 0] */
+#define MIXER_AMOPHI           0x130004 /* 0x130004 + (8 * Chn) [4095 : 0] */
+#define MIXER_PRING_LO_HI      0x188000 /* 0x188000 + (4 * Chn) [4095 : 0] */
+#define MIXER_PMOPLO           0x138000 /* 0x138000 + (8 * Chn) [4095 : 0] */
+#define MIXER_PMOPHI           0x138004 /* 0x138004 + (8 * Chn) [4095 : 0] */
+#define MIXER_AR_ENABLE                0x19000C
+
+#endif
diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c
new file mode 100644 (file)
index 0000000..a1db51b
--- /dev/null
@@ -0,0 +1,488 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctamixer.c
+ *
+ * @Brief
+ * This file contains the implementation of the Audio Mixer
+ * resource management object.
+ *
+ * @Author     Liu Chun
+ * @Date       May 21 2008
+ *
+ */
+
+#include "ctamixer.h"
+#include "cthardware.h"
+#include <linux/slab.h>
+
+#define AMIXER_RESOURCE_NUM    256
+#define SUM_RESOURCE_NUM       256
+
+#define AMIXER_Y_IMMEDIATE     1
+
+#define BLANK_SLOT             4094
+
+static int amixer_master(struct rsc *rsc)
+{
+       rsc->conj = 0;
+       return rsc->idx = container_of(rsc, struct amixer, rsc)->idx[0];
+}
+
+static int amixer_next_conj(struct rsc *rsc)
+{
+       rsc->conj++;
+       return container_of(rsc, struct amixer, rsc)->idx[rsc->conj];
+}
+
+static int amixer_index(const struct rsc *rsc)
+{
+       return container_of(rsc, struct amixer, rsc)->idx[rsc->conj];
+}
+
+static int amixer_output_slot(const struct rsc *rsc)
+{
+       return (amixer_index(rsc) << 4) + 0x4;
+}
+
+static struct rsc_ops amixer_basic_rsc_ops = {
+       .master         = amixer_master,
+       .next_conj      = amixer_next_conj,
+       .index          = amixer_index,
+       .output_slot    = amixer_output_slot,
+};
+
+static int amixer_set_input(struct amixer *amixer, struct rsc *rsc)
+{
+       struct hw *hw;
+
+       hw = amixer->rsc.hw;
+       hw->amixer_set_mode(amixer->rsc.ctrl_blk, AMIXER_Y_IMMEDIATE);
+       amixer->input = rsc;
+       if (NULL == rsc)
+               hw->amixer_set_x(amixer->rsc.ctrl_blk, BLANK_SLOT);
+       else
+               hw->amixer_set_x(amixer->rsc.ctrl_blk,
+                                       rsc->ops->output_slot(rsc));
+
+       return 0;
+}
+
+/* y is a 14-bit immediate constant */
+static int amixer_set_y(struct amixer *amixer, unsigned int y)
+{
+       struct hw *hw;
+
+       hw = amixer->rsc.hw;
+       hw->amixer_set_y(amixer->rsc.ctrl_blk, y);
+
+       return 0;
+}
+
+static int amixer_set_invalid_squash(struct amixer *amixer, unsigned int iv)
+{
+       struct hw *hw;
+
+       hw = amixer->rsc.hw;
+       hw->amixer_set_iv(amixer->rsc.ctrl_blk, iv);
+
+       return 0;
+}
+
+static int amixer_set_sum(struct amixer *amixer, struct sum *sum)
+{
+       struct hw *hw;
+
+       hw = amixer->rsc.hw;
+       amixer->sum = sum;
+       if (NULL == sum) {
+               hw->amixer_set_se(amixer->rsc.ctrl_blk, 0);
+       } else {
+               hw->amixer_set_se(amixer->rsc.ctrl_blk, 1);
+               hw->amixer_set_sadr(amixer->rsc.ctrl_blk,
+                                       sum->rsc.ops->index(&sum->rsc));
+       }
+
+       return 0;
+}
+
+static int amixer_commit_write(struct amixer *amixer)
+{
+       struct hw *hw;
+       unsigned int index;
+       int i;
+       struct rsc *input;
+       struct sum *sum;
+
+       hw = amixer->rsc.hw;
+       input = amixer->input;
+       sum = amixer->sum;
+
+       /* Program master and conjugate resources */
+       amixer->rsc.ops->master(&amixer->rsc);
+       if (NULL != input)
+               input->ops->master(input);
+
+       if (NULL != sum)
+               sum->rsc.ops->master(&sum->rsc);
+
+       for (i = 0; i < amixer->rsc.msr; i++) {
+               hw->amixer_set_dirty_all(amixer->rsc.ctrl_blk);
+               if (NULL != input) {
+                       hw->amixer_set_x(amixer->rsc.ctrl_blk,
+                                               input->ops->output_slot(input));
+                       input->ops->next_conj(input);
+               }
+               if (NULL != sum) {
+                       hw->amixer_set_sadr(amixer->rsc.ctrl_blk,
+                                               sum->rsc.ops->index(&sum->rsc));
+                       sum->rsc.ops->next_conj(&sum->rsc);
+               }
+               index = amixer->rsc.ops->output_slot(&amixer->rsc);
+               hw->amixer_commit_write(hw, index, amixer->rsc.ctrl_blk);
+               amixer->rsc.ops->next_conj(&amixer->rsc);
+       }
+       amixer->rsc.ops->master(&amixer->rsc);
+       if (NULL != input)
+               input->ops->master(input);
+
+       if (NULL != sum)
+               sum->rsc.ops->master(&sum->rsc);
+
+       return 0;
+}
+
+static int amixer_commit_raw_write(struct amixer *amixer)
+{
+       struct hw *hw;
+       unsigned int index;
+
+       hw = amixer->rsc.hw;
+       index = amixer->rsc.ops->output_slot(&amixer->rsc);
+       hw->amixer_commit_write(hw, index, amixer->rsc.ctrl_blk);
+
+       return 0;
+}
+
+static int amixer_get_y(struct amixer *amixer)
+{
+       struct hw *hw;
+
+       hw = amixer->rsc.hw;
+       return hw->amixer_get_y(amixer->rsc.ctrl_blk);
+}
+
+static int amixer_setup(struct amixer *amixer, struct rsc *input,
+                       unsigned int scale, struct sum *sum)
+{
+       amixer_set_input(amixer, input);
+       amixer_set_y(amixer, scale);
+       amixer_set_sum(amixer, sum);
+       amixer_commit_write(amixer);
+       return 0;
+}
+
+static struct amixer_rsc_ops amixer_ops = {
+       .set_input              = amixer_set_input,
+       .set_invalid_squash     = amixer_set_invalid_squash,
+       .set_scale              = amixer_set_y,
+       .set_sum                = amixer_set_sum,
+       .commit_write           = amixer_commit_write,
+       .commit_raw_write       = amixer_commit_raw_write,
+       .setup                  = amixer_setup,
+       .get_scale              = amixer_get_y,
+};
+
+static int amixer_rsc_init(struct amixer *amixer,
+                          const struct amixer_desc *desc,
+                          struct amixer_mgr *mgr)
+{
+       int err;
+
+       err = rsc_init(&amixer->rsc, amixer->idx[0],
+                       AMIXER, desc->msr, mgr->mgr.hw);
+       if (err)
+               return err;
+
+       /* Set amixer specific operations */
+       amixer->rsc.ops = &amixer_basic_rsc_ops;
+       amixer->ops = &amixer_ops;
+       amixer->input = NULL;
+       amixer->sum = NULL;
+
+       amixer_setup(amixer, NULL, 0, NULL);
+
+       return 0;
+}
+
+static int amixer_rsc_uninit(struct amixer *amixer)
+{
+       amixer_setup(amixer, NULL, 0, NULL);
+       rsc_uninit(&amixer->rsc);
+       amixer->ops = NULL;
+       amixer->input = NULL;
+       amixer->sum = NULL;
+       return 0;
+}
+
+static int get_amixer_rsc(struct amixer_mgr *mgr,
+                         const struct amixer_desc *desc,
+                         struct amixer **ramixer)
+{
+       int err, i;
+       unsigned int idx;
+       struct amixer *amixer;
+       unsigned long flags;
+
+       *ramixer = NULL;
+
+       /* Allocate mem for amixer resource */
+       amixer = kzalloc(sizeof(*amixer), GFP_KERNEL);
+       if (NULL == amixer) {
+               err = -ENOMEM;
+               return err;
+       }
+
+       /* Check whether there are sufficient
+        * amixer resources to meet request. */
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       for (i = 0; i < desc->msr; i++) {
+               err = mgr_get_resource(&mgr->mgr, 1, &idx);
+               if (err)
+                       break;
+
+               amixer->idx[i] = idx;
+       }
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       if (err) {
+               printk(KERN_ERR "ctxfi: Can't meet AMIXER resource request!\n");
+               goto error;
+       }
+
+       err = amixer_rsc_init(amixer, desc, mgr);
+       if (err)
+               goto error;
+
+       *ramixer = amixer;
+
+       return 0;
+
+error:
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       for (i--; i >= 0; i--)
+               mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]);
+
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       kfree(amixer);
+       return err;
+}
+
+static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer)
+{
+       unsigned long flags;
+       int i;
+
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       for (i = 0; i < amixer->rsc.msr; i++)
+               mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]);
+
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       amixer_rsc_uninit(amixer);
+       kfree(amixer);
+
+       return 0;
+}
+
+int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr)
+{
+       int err;
+       struct amixer_mgr *amixer_mgr;
+
+       *ramixer_mgr = NULL;
+       amixer_mgr = kzalloc(sizeof(*amixer_mgr), GFP_KERNEL);
+       if (NULL == amixer_mgr)
+               return -ENOMEM;
+
+       err = rsc_mgr_init(&amixer_mgr->mgr, AMIXER, AMIXER_RESOURCE_NUM, hw);
+       if (err)
+               goto error;
+
+       spin_lock_init(&amixer_mgr->mgr_lock);
+
+       amixer_mgr->get_amixer = get_amixer_rsc;
+       amixer_mgr->put_amixer = put_amixer_rsc;
+
+       *ramixer_mgr = amixer_mgr;
+
+       return 0;
+
+error:
+       kfree(amixer_mgr);
+       return err;
+}
+
+int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr)
+{
+       rsc_mgr_uninit(&amixer_mgr->mgr);
+       kfree(amixer_mgr);
+       return 0;
+}
+
+/* SUM resource management */
+
+static int sum_master(struct rsc *rsc)
+{
+       rsc->conj = 0;
+       return rsc->idx = container_of(rsc, struct sum, rsc)->idx[0];
+}
+
+static int sum_next_conj(struct rsc *rsc)
+{
+       rsc->conj++;
+       return container_of(rsc, struct sum, rsc)->idx[rsc->conj];
+}
+
+static int sum_index(const struct rsc *rsc)
+{
+       return container_of(rsc, struct sum, rsc)->idx[rsc->conj];
+}
+
+static int sum_output_slot(const struct rsc *rsc)
+{
+       return (sum_index(rsc) << 4) + 0xc;
+}
+
+static struct rsc_ops sum_basic_rsc_ops = {
+       .master         = sum_master,
+       .next_conj      = sum_next_conj,
+       .index          = sum_index,
+       .output_slot    = sum_output_slot,
+};
+
+static int sum_rsc_init(struct sum *sum,
+                       const struct sum_desc *desc,
+                       struct sum_mgr *mgr)
+{
+       int err;
+
+       err = rsc_init(&sum->rsc, sum->idx[0], SUM, desc->msr, mgr->mgr.hw);
+       if (err)
+               return err;
+
+       sum->rsc.ops = &sum_basic_rsc_ops;
+
+       return 0;
+}
+
+static int sum_rsc_uninit(struct sum *sum)
+{
+       rsc_uninit(&sum->rsc);
+       return 0;
+}
+
+static int get_sum_rsc(struct sum_mgr *mgr,
+                      const struct sum_desc *desc,
+                      struct sum **rsum)
+{
+       int err, i;
+       unsigned int idx;
+       struct sum *sum;
+       unsigned long flags;
+
+       *rsum = NULL;
+
+       /* Allocate mem for sum resource */
+       sum = kzalloc(sizeof(*sum), GFP_KERNEL);
+       if (NULL == sum) {
+               err = -ENOMEM;
+               return err;
+       }
+
+       /* Check whether there are sufficient sum resources to meet request. */
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       for (i = 0; i < desc->msr; i++) {
+               err = mgr_get_resource(&mgr->mgr, 1, &idx);
+               if (err)
+                       break;
+
+               sum->idx[i] = idx;
+       }
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       if (err) {
+               printk(KERN_ERR "ctxfi: Can't meet SUM resource request!\n");
+               goto error;
+       }
+
+       err = sum_rsc_init(sum, desc, mgr);
+       if (err)
+               goto error;
+
+       *rsum = sum;
+
+       return 0;
+
+error:
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       for (i--; i >= 0; i--)
+               mgr_put_resource(&mgr->mgr, 1, sum->idx[i]);
+
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       kfree(sum);
+       return err;
+}
+
+static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum)
+{
+       unsigned long flags;
+       int i;
+
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       for (i = 0; i < sum->rsc.msr; i++)
+               mgr_put_resource(&mgr->mgr, 1, sum->idx[i]);
+
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       sum_rsc_uninit(sum);
+       kfree(sum);
+
+       return 0;
+}
+
+int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr)
+{
+       int err;
+       struct sum_mgr *sum_mgr;
+
+       *rsum_mgr = NULL;
+       sum_mgr = kzalloc(sizeof(*sum_mgr), GFP_KERNEL);
+       if (NULL == sum_mgr)
+               return -ENOMEM;
+
+       err = rsc_mgr_init(&sum_mgr->mgr, SUM, SUM_RESOURCE_NUM, hw);
+       if (err)
+               goto error;
+
+       spin_lock_init(&sum_mgr->mgr_lock);
+
+       sum_mgr->get_sum = get_sum_rsc;
+       sum_mgr->put_sum = put_sum_rsc;
+
+       *rsum_mgr = sum_mgr;
+
+       return 0;
+
+error:
+       kfree(sum_mgr);
+       return err;
+}
+
+int sum_mgr_destroy(struct sum_mgr *sum_mgr)
+{
+       rsc_mgr_uninit(&sum_mgr->mgr);
+       kfree(sum_mgr);
+       return 0;
+}
+
diff --git a/sound/pci/ctxfi/ctamixer.h b/sound/pci/ctxfi/ctamixer.h
new file mode 100644 (file)
index 0000000..cc49e5a
--- /dev/null
@@ -0,0 +1,96 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctamixer.h
+ *
+ * @Brief
+ * This file contains the definition of the Audio Mixer
+ * resource management object.
+ *
+ * @Author     Liu Chun
+ * @Date       May 21 2008
+ *
+ */
+
+#ifndef CTAMIXER_H
+#define CTAMIXER_H
+
+#include "ctresource.h"
+#include <linux/spinlock.h>
+
+/* Define the descriptor of a summation node resource */
+struct sum {
+       struct rsc rsc;         /* Basic resource info */
+       unsigned char idx[8];
+};
+
+/* Define sum resource request description info */
+struct sum_desc {
+       unsigned int msr;
+};
+
+struct sum_mgr {
+       struct rsc_mgr mgr;     /* Basic resource manager info */
+       spinlock_t mgr_lock;
+
+        /* request one sum resource */
+       int (*get_sum)(struct sum_mgr *mgr,
+                       const struct sum_desc *desc, struct sum **rsum);
+       /* return one sum resource */
+       int (*put_sum)(struct sum_mgr *mgr, struct sum *sum);
+};
+
+/* Constructor and destructor of daio resource manager */
+int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr);
+int sum_mgr_destroy(struct sum_mgr *sum_mgr);
+
+/* Define the descriptor of a amixer resource */
+struct amixer_rsc_ops;
+
+struct amixer {
+       struct rsc rsc;         /* Basic resource info */
+       unsigned char idx[8];
+       struct rsc *input;      /* pointer to a resource acting as source */
+       struct sum *sum;        /* Put amixer output to this summation node */
+       struct amixer_rsc_ops *ops;     /* AMixer specific operations */
+};
+
+struct amixer_rsc_ops {
+       int (*set_input)(struct amixer *amixer, struct rsc *rsc);
+       int (*set_scale)(struct amixer *amixer, unsigned int scale);
+       int (*set_invalid_squash)(struct amixer *amixer, unsigned int iv);
+       int (*set_sum)(struct amixer *amixer, struct sum *sum);
+       int (*commit_write)(struct amixer *amixer);
+       /* Only for interleaved recording */
+       int (*commit_raw_write)(struct amixer *amixer);
+       int (*setup)(struct amixer *amixer, struct rsc *input,
+                       unsigned int scale, struct sum *sum);
+       int (*get_scale)(struct amixer *amixer);
+};
+
+/* Define amixer resource request description info */
+struct amixer_desc {
+       unsigned int msr;
+};
+
+struct amixer_mgr {
+       struct rsc_mgr mgr;     /* Basic resource manager info */
+       spinlock_t mgr_lock;
+
+        /* request one amixer resource */
+       int (*get_amixer)(struct amixer_mgr *mgr,
+                         const struct amixer_desc *desc,
+                         struct amixer **ramixer);
+       /* return one amixer resource */
+       int (*put_amixer)(struct amixer_mgr *mgr, struct amixer *amixer);
+};
+
+/* Constructor and destructor of amixer resource manager */
+int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr);
+int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr);
+
+#endif /* CTAMIXER_H */
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
new file mode 100644 (file)
index 0000000..80fb2ba
--- /dev/null
@@ -0,0 +1,1619 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File    ctatc.c
+ *
+ * @Brief
+ * This file contains the implementation of the device resource management
+ * object.
+ *
+ * @Author Liu Chun
+ * @Date Mar 28 2008
+ */
+
+#include "ctatc.h"
+#include "ctpcm.h"
+#include "ctmixer.h"
+#include "cthardware.h"
+#include "ctsrc.h"
+#include "ctamixer.h"
+#include "ctdaio.h"
+#include "cttimer.h"
+#include <linux/delay.h>
+#include <sound/pcm.h>
+#include <sound/control.h>
+#include <sound/asoundef.h>
+
+#define MONO_SUM_SCALE 0x19a8  /* 2^(-0.5) in 14-bit floating format */
+#define DAIONUM                7
+#define MAX_MULTI_CHN  8
+
+#define IEC958_DEFAULT_CON ((IEC958_AES0_NONAUDIO \
+                           | IEC958_AES0_CON_NOT_COPYRIGHT) \
+                           | ((IEC958_AES1_CON_MIXER \
+                           | IEC958_AES1_CON_ORIGINAL) << 8) \
+                           | (0x10 << 16) \
+                           | ((IEC958_AES3_CON_FS_48000) << 24))
+
+static struct snd_pci_quirk __devinitdata subsys_20k1_list[] = {
+       SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0022, "SB055x", CTSB055X),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x002f, "SB055x", CTSB055X),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0029, "SB073x", CTSB073X),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0031, "SB073x", CTSB073X),
+       SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0xf000, 0x6000,
+                          "UAA", CTUAA),
+       SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_CREATIVE,
+                            "Unknown", CT20K1_UNKNOWN),
+       { } /* terminator */
+};
+
+static struct snd_pci_quirk __devinitdata subsys_20k2_list[] = {
+       SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB0760,
+                     "SB0760", CTSB0760),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08801,
+                     "SB0880", CTSB0880),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08802,
+                     "SB0880", CTSB0880),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08803,
+                     "SB0880", CTSB0880),
+       SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0xf000,
+                          PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "HENDRIX",
+                          CTHENDRIX),
+       { } /* terminator */
+};
+
+static const char *ct_subsys_name[NUM_CTCARDS] = {
+       [CTSB055X]      = "SB055x",
+       [CTSB073X]      = "SB073x",
+       [CTSB0760]      = "SB076x",
+       [CTUAA]         = "UAA",
+       [CT20K1_UNKNOWN] = "Unknown",
+       [CTHENDRIX]     = "Hendrix",
+       [CTSB0880]      = "SB0880",
+};
+
+static struct {
+       int (*create)(struct ct_atc *atc,
+                       enum CTALSADEVS device, const char *device_name);
+       int (*destroy)(void *alsa_dev);
+       const char *public_name;
+} alsa_dev_funcs[NUM_CTALSADEVS] = {
+       [FRONT]         = { .create = ct_alsa_pcm_create,
+                           .destroy = NULL,
+                           .public_name = "Front/WaveIn"},
+       [SURROUND]      = { .create = ct_alsa_pcm_create,
+                           .destroy = NULL,
+                           .public_name = "Surround"},
+       [CLFE]          = { .create = ct_alsa_pcm_create,
+                           .destroy = NULL,
+                           .public_name = "Center/LFE"},
+       [SIDE]          = { .create = ct_alsa_pcm_create,
+                           .destroy = NULL,
+                           .public_name = "Side"},
+       [IEC958]        = { .create = ct_alsa_pcm_create,
+                           .destroy = NULL,
+                           .public_name = "IEC958 Non-audio"},
+
+       [MIXER]         = { .create = ct_alsa_mix_create,
+                           .destroy = NULL,
+                           .public_name = "Mixer"}
+};
+
+typedef int (*create_t)(void *, void **);
+typedef int (*destroy_t)(void *);
+
+static struct {
+       int (*create)(void *hw, void **rmgr);
+       int (*destroy)(void *mgr);
+} rsc_mgr_funcs[NUM_RSCTYP] = {
+       [SRC]           = { .create     = (create_t)src_mgr_create,
+                           .destroy    = (destroy_t)src_mgr_destroy    },
+       [SRCIMP]        = { .create     = (create_t)srcimp_mgr_create,
+                           .destroy    = (destroy_t)srcimp_mgr_destroy },
+       [AMIXER]        = { .create     = (create_t)amixer_mgr_create,
+                           .destroy    = (destroy_t)amixer_mgr_destroy },
+       [SUM]           = { .create     = (create_t)sum_mgr_create,
+                           .destroy    = (destroy_t)sum_mgr_destroy    },
+       [DAIO]          = { .create     = (create_t)daio_mgr_create,
+                           .destroy    = (destroy_t)daio_mgr_destroy   }
+};
+
+static int
+atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+
+/* *
+ * Only mono and interleaved modes are supported now.
+ * Always allocates a contiguous channel block.
+ * */
+
+static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct snd_pcm_runtime *runtime;
+       struct ct_vm *vm;
+
+       if (NULL == apcm->substream)
+               return 0;
+
+       runtime = apcm->substream->runtime;
+       vm = atc->vm;
+
+       apcm->vm_block = vm->map(vm, apcm->substream, runtime->dma_bytes);
+
+       if (NULL == apcm->vm_block)
+               return -ENOENT;
+
+       return 0;
+}
+
+static void ct_unmap_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct ct_vm *vm;
+
+       if (NULL == apcm->vm_block)
+               return;
+
+       vm = atc->vm;
+
+       vm->unmap(vm, apcm->vm_block);
+
+       apcm->vm_block = NULL;
+}
+
+static unsigned long atc_get_ptp_phys(struct ct_atc *atc, int index)
+{
+       struct ct_vm *vm;
+       void *kvirt_addr;
+       unsigned long phys_addr;
+
+       vm = atc->vm;
+       kvirt_addr = vm->get_ptp_virt(vm, index);
+       if (kvirt_addr == NULL)
+               phys_addr = (~0UL);
+       else
+               phys_addr = virt_to_phys(kvirt_addr);
+
+       return phys_addr;
+}
+
+static unsigned int convert_format(snd_pcm_format_t snd_format)
+{
+       switch (snd_format) {
+       case SNDRV_PCM_FORMAT_U8:
+               return SRC_SF_U8;
+       case SNDRV_PCM_FORMAT_S16_LE:
+               return SRC_SF_S16;
+       case SNDRV_PCM_FORMAT_S24_3LE:
+               return SRC_SF_S24;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               return SRC_SF_S32;
+       case SNDRV_PCM_FORMAT_FLOAT_LE:
+               return SRC_SF_F32;
+       default:
+               printk(KERN_ERR "ctxfi: not recognized snd format is %d \n",
+                       snd_format);
+               return SRC_SF_S16;
+       }
+}
+
+static unsigned int
+atc_get_pitch(unsigned int input_rate, unsigned int output_rate)
+{
+       unsigned int pitch;
+       int b;
+
+       /* get pitch and convert to fixed-point 8.24 format. */
+       pitch = (input_rate / output_rate) << 24;
+       input_rate %= output_rate;
+       input_rate /= 100;
+       output_rate /= 100;
+       for (b = 31; ((b >= 0) && !(input_rate >> b)); )
+               b--;
+
+       if (b >= 0) {
+               input_rate <<= (31 - b);
+               input_rate /= output_rate;
+               b = 24 - (31 - b);
+               if (b >= 0)
+                       input_rate <<= b;
+               else
+                       input_rate >>= -b;
+
+               pitch |= input_rate;
+       }
+
+       return pitch;
+}
+
+static int select_rom(unsigned int pitch)
+{
+       if ((pitch > 0x00428f5c) && (pitch < 0x01b851ec)) {
+               /* 0.26 <= pitch <= 1.72 */
+               return 1;
+       } else if ((0x01d66666 == pitch) || (0x01d66667 == pitch)) {
+               /* pitch == 1.8375 */
+               return 2;
+       } else if (0x02000000 == pitch) {
+               /* pitch == 2 */
+               return 3;
+       } else if ((pitch >= 0x0) && (pitch <= 0x08000000)) {
+               /* 0 <= pitch <= 8 */
+               return 0;
+       } else {
+               return -ENOENT;
+       }
+}
+
+static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct src_mgr *src_mgr = atc->rsc_mgrs[SRC];
+       struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER];
+       struct src_desc desc = {0};
+       struct amixer_desc mix_dsc = {0};
+       struct src *src;
+       struct amixer *amixer;
+       int err;
+       int n_amixer = apcm->substream->runtime->channels, i = 0;
+       int device = apcm->substream->pcm->device;
+       unsigned int pitch;
+       unsigned long flags;
+
+       if (NULL != apcm->src) {
+               /* Prepared pcm playback */
+               return 0;
+       }
+
+       /* first release old resources */
+       atc->pcm_release_resources(atc, apcm);
+
+       /* Get SRC resource */
+       desc.multi = apcm->substream->runtime->channels;
+       desc.msr = atc->msr;
+       desc.mode = MEMRD;
+       err = src_mgr->get_src(src_mgr, &desc, (struct src **)&apcm->src);
+       if (err)
+               goto error1;
+
+       pitch = atc_get_pitch(apcm->substream->runtime->rate,
+                                               (atc->rsr * atc->msr));
+       src = apcm->src;
+       src->ops->set_pitch(src, pitch);
+       src->ops->set_rom(src, select_rom(pitch));
+       src->ops->set_sf(src, convert_format(apcm->substream->runtime->format));
+       src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL));
+
+       /* Get AMIXER resource */
+       n_amixer = (n_amixer < 2) ? 2 : n_amixer;
+       apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
+       if (NULL == apcm->amixers) {
+               err = -ENOMEM;
+               goto error1;
+       }
+       mix_dsc.msr = atc->msr;
+       for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) {
+               err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc,
+                                       (struct amixer **)&apcm->amixers[i]);
+               if (err)
+                       goto error1;
+
+               apcm->n_amixer++;
+       }
+
+       /* Set up device virtual mem map */
+       err = ct_map_audio_buffer(atc, apcm);
+       if (err < 0)
+               goto error1;
+
+       /* Connect resources */
+       src = apcm->src;
+       for (i = 0; i < n_amixer; i++) {
+               amixer = apcm->amixers[i];
+               spin_lock_irqsave(&atc->atc_lock, flags);
+               amixer->ops->setup(amixer, &src->rsc,
+                                       INIT_VOL, atc->pcm[i+device*2]);
+               spin_unlock_irqrestore(&atc->atc_lock, flags);
+               src = src->ops->next_interleave(src);
+               if (NULL == src)
+                       src = apcm->src;
+       }
+
+       ct_timer_prepare(apcm->timer);
+
+       return 0;
+
+error1:
+       atc_pcm_release_resources(atc, apcm);
+       return err;
+}
+
+static int
+atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct src_mgr *src_mgr = atc->rsc_mgrs[SRC];
+       struct srcimp_mgr *srcimp_mgr = atc->rsc_mgrs[SRCIMP];
+       struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER];
+       struct sum_mgr *sum_mgr = atc->rsc_mgrs[SUM];
+       struct srcimp *srcimp;
+       int i;
+
+       if (NULL != apcm->srcimps) {
+               for (i = 0; i < apcm->n_srcimp; i++) {
+                       srcimp = apcm->srcimps[i];
+                       srcimp->ops->unmap(srcimp);
+                       srcimp_mgr->put_srcimp(srcimp_mgr, srcimp);
+                       apcm->srcimps[i] = NULL;
+               }
+               kfree(apcm->srcimps);
+               apcm->srcimps = NULL;
+       }
+
+       if (NULL != apcm->srccs) {
+               for (i = 0; i < apcm->n_srcc; i++) {
+                       src_mgr->put_src(src_mgr, apcm->srccs[i]);
+                       apcm->srccs[i] = NULL;
+               }
+               kfree(apcm->srccs);
+               apcm->srccs = NULL;
+       }
+
+       if (NULL != apcm->amixers) {
+               for (i = 0; i < apcm->n_amixer; i++) {
+                       amixer_mgr->put_amixer(amixer_mgr, apcm->amixers[i]);
+                       apcm->amixers[i] = NULL;
+               }
+               kfree(apcm->amixers);
+               apcm->amixers = NULL;
+       }
+
+       if (NULL != apcm->mono) {
+               sum_mgr->put_sum(sum_mgr, apcm->mono);
+               apcm->mono = NULL;
+       }
+
+       if (NULL != apcm->src) {
+               src_mgr->put_src(src_mgr, apcm->src);
+               apcm->src = NULL;
+       }
+
+       if (NULL != apcm->vm_block) {
+               /* Undo device virtual mem map */
+               ct_unmap_audio_buffer(atc, apcm);
+               apcm->vm_block = NULL;
+       }
+
+       return 0;
+}
+
+static int atc_pcm_playback_start(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       unsigned int max_cisz;
+       struct src *src = apcm->src;
+
+       if (apcm->started)
+               return 0;
+       apcm->started = 1;
+
+       max_cisz = src->multi * src->rsc.msr;
+       max_cisz = 0x80 * (max_cisz < 8 ? max_cisz : 8);
+
+       src->ops->set_sa(src, apcm->vm_block->addr);
+       src->ops->set_la(src, apcm->vm_block->addr + apcm->vm_block->size);
+       src->ops->set_ca(src, apcm->vm_block->addr + max_cisz);
+       src->ops->set_cisz(src, max_cisz);
+
+       src->ops->set_bm(src, 1);
+       src->ops->set_state(src, SRC_STATE_INIT);
+       src->ops->commit_write(src);
+
+       ct_timer_start(apcm->timer);
+       return 0;
+}
+
+static int atc_pcm_stop(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct src *src;
+       int i;
+
+       ct_timer_stop(apcm->timer);
+
+       src = apcm->src;
+       src->ops->set_bm(src, 0);
+       src->ops->set_state(src, SRC_STATE_OFF);
+       src->ops->commit_write(src);
+
+       if (NULL != apcm->srccs) {
+               for (i = 0; i < apcm->n_srcc; i++) {
+                       src = apcm->srccs[i];
+                       src->ops->set_bm(src, 0);
+                       src->ops->set_state(src, SRC_STATE_OFF);
+                       src->ops->commit_write(src);
+               }
+       }
+
+       apcm->started = 0;
+
+       return 0;
+}
+
+static int
+atc_pcm_playback_position(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct src *src = apcm->src;
+       u32 size, max_cisz;
+       int position;
+
+       if (!src)
+               return 0;
+       position = src->ops->get_ca(src);
+
+       size = apcm->vm_block->size;
+       max_cisz = src->multi * src->rsc.msr;
+       max_cisz = 128 * (max_cisz < 8 ? max_cisz : 8);
+
+       return (position + size - max_cisz - apcm->vm_block->addr) % size;
+}
+
+struct src_node_conf_t {
+       unsigned int pitch;
+       unsigned int msr:8;
+       unsigned int mix_msr:8;
+       unsigned int imp_msr:8;
+       unsigned int vo:1;
+};
+
+static void setup_src_node_conf(struct ct_atc *atc, struct ct_atc_pcm *apcm,
+                               struct src_node_conf_t *conf, int *n_srcc)
+{
+       unsigned int pitch;
+
+       /* get pitch and convert to fixed-point 8.24 format. */
+       pitch = atc_get_pitch((atc->rsr * atc->msr),
+                               apcm->substream->runtime->rate);
+       *n_srcc = 0;
+
+       if (1 == atc->msr) {
+               *n_srcc = apcm->substream->runtime->channels;
+               conf[0].pitch = pitch;
+               conf[0].mix_msr = conf[0].imp_msr = conf[0].msr = 1;
+               conf[0].vo = 1;
+       } else if (2 == atc->msr) {
+               if (0x8000000 < pitch) {
+                       /* Need two-stage SRCs, SRCIMPs and
+                        * AMIXERs for converting format */
+                       conf[0].pitch = (atc->msr << 24);
+                       conf[0].msr = conf[0].mix_msr = 1;
+                       conf[0].imp_msr = atc->msr;
+                       conf[0].vo = 0;
+                       conf[1].pitch = atc_get_pitch(atc->rsr,
+                                       apcm->substream->runtime->rate);
+                       conf[1].msr = conf[1].mix_msr = conf[1].imp_msr = 1;
+                       conf[1].vo = 1;
+                       *n_srcc = apcm->substream->runtime->channels * 2;
+               } else if (0x1000000 < pitch) {
+                       /* Need one-stage SRCs, SRCIMPs and
+                        * AMIXERs for converting format */
+                       conf[0].pitch = pitch;
+                       conf[0].msr = conf[0].mix_msr
+                                   = conf[0].imp_msr = atc->msr;
+                       conf[0].vo = 1;
+                       *n_srcc = apcm->substream->runtime->channels;
+               }
+       }
+}
+
+static int
+atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct src_mgr *src_mgr = atc->rsc_mgrs[SRC];
+       struct srcimp_mgr *srcimp_mgr = atc->rsc_mgrs[SRCIMP];
+       struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER];
+       struct sum_mgr *sum_mgr = atc->rsc_mgrs[SUM];
+       struct src_desc src_dsc = {0};
+       struct src *src;
+       struct srcimp_desc srcimp_dsc = {0};
+       struct srcimp *srcimp;
+       struct amixer_desc mix_dsc = {0};
+       struct sum_desc sum_dsc = {0};
+       unsigned int pitch;
+       int multi, err, i;
+       int n_srcimp, n_amixer, n_srcc, n_sum;
+       struct src_node_conf_t src_node_conf[2] = {{0} };
+
+       /* first release old resources */
+       atc_pcm_release_resources(atc, apcm);
+
+       /* The numbers of converting SRCs and SRCIMPs should be determined
+        * by pitch value. */
+
+       multi = apcm->substream->runtime->channels;
+
+       /* get pitch and convert to fixed-point 8.24 format. */
+       pitch = atc_get_pitch((atc->rsr * atc->msr),
+                               apcm->substream->runtime->rate);
+
+       setup_src_node_conf(atc, apcm, src_node_conf, &n_srcc);
+       n_sum = (1 == multi) ? 1 : 0;
+       n_amixer = n_sum * 2 + n_srcc;
+       n_srcimp = n_srcc;
+       if ((multi > 1) && (0x8000000 >= pitch)) {
+               /* Need extra AMIXERs and SRCIMPs for special treatment
+                * of interleaved recording of conjugate channels */
+               n_amixer += multi * atc->msr;
+               n_srcimp += multi * atc->msr;
+       } else {
+               n_srcimp += multi;
+       }
+
+       if (n_srcc) {
+               apcm->srccs = kzalloc(sizeof(void *)*n_srcc, GFP_KERNEL);
+               if (NULL == apcm->srccs)
+                       return -ENOMEM;
+       }
+       if (n_amixer) {
+               apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
+               if (NULL == apcm->amixers) {
+                       err = -ENOMEM;
+                       goto error1;
+               }
+       }
+       apcm->srcimps = kzalloc(sizeof(void *)*n_srcimp, GFP_KERNEL);
+       if (NULL == apcm->srcimps) {
+               err = -ENOMEM;
+               goto error1;
+       }
+
+       /* Allocate SRCs for sample rate conversion if needed */
+       src_dsc.multi = 1;
+       src_dsc.mode = ARCRW;
+       for (i = 0, apcm->n_srcc = 0; i < n_srcc; i++) {
+               src_dsc.msr = src_node_conf[i/multi].msr;
+               err = src_mgr->get_src(src_mgr, &src_dsc,
+                                       (struct src **)&apcm->srccs[i]);
+               if (err)
+                       goto error1;
+
+               src = apcm->srccs[i];
+               pitch = src_node_conf[i/multi].pitch;
+               src->ops->set_pitch(src, pitch);
+               src->ops->set_rom(src, select_rom(pitch));
+               src->ops->set_vo(src, src_node_conf[i/multi].vo);
+
+               apcm->n_srcc++;
+       }
+
+       /* Allocate AMIXERs for routing SRCs of conversion if needed */
+       for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) {
+               if (i < (n_sum*2))
+                       mix_dsc.msr = atc->msr;
+               else if (i < (n_sum*2+n_srcc))
+                       mix_dsc.msr = src_node_conf[(i-n_sum*2)/multi].mix_msr;
+               else
+                       mix_dsc.msr = 1;
+
+               err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc,
+                                       (struct amixer **)&apcm->amixers[i]);
+               if (err)
+                       goto error1;
+
+               apcm->n_amixer++;
+       }
+
+       /* Allocate a SUM resource to mix all input channels together */
+       sum_dsc.msr = atc->msr;
+       err = sum_mgr->get_sum(sum_mgr, &sum_dsc, (struct sum **)&apcm->mono);
+       if (err)
+               goto error1;
+
+       pitch = atc_get_pitch((atc->rsr * atc->msr),
+                               apcm->substream->runtime->rate);
+       /* Allocate SRCIMP resources */
+       for (i = 0, apcm->n_srcimp = 0; i < n_srcimp; i++) {
+               if (i < (n_srcc))
+                       srcimp_dsc.msr = src_node_conf[i/multi].imp_msr;
+               else if (1 == multi)
+                       srcimp_dsc.msr = (pitch <= 0x8000000) ? atc->msr : 1;
+               else
+                       srcimp_dsc.msr = 1;
+
+               err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, &srcimp);
+               if (err)
+                       goto error1;
+
+               apcm->srcimps[i] = srcimp;
+               apcm->n_srcimp++;
+       }
+
+       /* Allocate a SRC for writing data to host memory */
+       src_dsc.multi = apcm->substream->runtime->channels;
+       src_dsc.msr = 1;
+       src_dsc.mode = MEMWR;
+       err = src_mgr->get_src(src_mgr, &src_dsc, (struct src **)&apcm->src);
+       if (err)
+               goto error1;
+
+       src = apcm->src;
+       src->ops->set_pitch(src, pitch);
+
+       /* Set up device virtual mem map */
+       err = ct_map_audio_buffer(atc, apcm);
+       if (err < 0)
+               goto error1;
+
+       return 0;
+
+error1:
+       atc_pcm_release_resources(atc, apcm);
+       return err;
+}
+
+static int atc_pcm_capture_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct src *src;
+       struct amixer *amixer;
+       struct srcimp *srcimp;
+       struct ct_mixer *mixer = atc->mixer;
+       struct sum *mono;
+       struct rsc *out_ports[8] = {NULL};
+       int err, i, j, n_sum, multi;
+       unsigned int pitch;
+       int mix_base = 0, imp_base = 0;
+
+       if (NULL != apcm->src) {
+               /* Prepared pcm capture */
+               return 0;
+       }
+
+       /* Get needed resources. */
+       err = atc_pcm_capture_get_resources(atc, apcm);
+       if (err)
+               return err;
+
+       /* Connect resources */
+       mixer->get_output_ports(mixer, MIX_PCMO_FRONT,
+                               &out_ports[0], &out_ports[1]);
+
+       multi = apcm->substream->runtime->channels;
+       if (1 == multi) {
+               mono = apcm->mono;
+               for (i = 0; i < 2; i++) {
+                       amixer = apcm->amixers[i];
+                       amixer->ops->setup(amixer, out_ports[i],
+                                               MONO_SUM_SCALE, mono);
+               }
+               out_ports[0] = &mono->rsc;
+               n_sum = 1;
+               mix_base = n_sum * 2;
+       }
+
+       for (i = 0; i < apcm->n_srcc; i++) {
+               src = apcm->srccs[i];
+               srcimp = apcm->srcimps[imp_base+i];
+               amixer = apcm->amixers[mix_base+i];
+               srcimp->ops->map(srcimp, src, out_ports[i%multi]);
+               amixer->ops->setup(amixer, &src->rsc, INIT_VOL, NULL);
+               out_ports[i%multi] = &amixer->rsc;
+       }
+
+       pitch = atc_get_pitch((atc->rsr * atc->msr),
+                               apcm->substream->runtime->rate);
+
+       if ((multi > 1) && (pitch <= 0x8000000)) {
+               /* Special connection for interleaved
+                * recording with conjugate channels */
+               for (i = 0; i < multi; i++) {
+                       out_ports[i]->ops->master(out_ports[i]);
+                       for (j = 0; j < atc->msr; j++) {
+                               amixer = apcm->amixers[apcm->n_srcc+j*multi+i];
+                               amixer->ops->set_input(amixer, out_ports[i]);
+                               amixer->ops->set_scale(amixer, INIT_VOL);
+                               amixer->ops->set_sum(amixer, NULL);
+                               amixer->ops->commit_raw_write(amixer);
+                               out_ports[i]->ops->next_conj(out_ports[i]);
+
+                               srcimp = apcm->srcimps[apcm->n_srcc+j*multi+i];
+                               srcimp->ops->map(srcimp, apcm->src,
+                                                       &amixer->rsc);
+                       }
+               }
+       } else {
+               for (i = 0; i < multi; i++) {
+                       srcimp = apcm->srcimps[apcm->n_srcc+i];
+                       srcimp->ops->map(srcimp, apcm->src, out_ports[i]);
+               }
+       }
+
+       ct_timer_prepare(apcm->timer);
+
+       return 0;
+}
+
+static int atc_pcm_capture_start(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct src *src;
+       struct src_mgr *src_mgr = atc->rsc_mgrs[SRC];
+       int i, multi;
+
+       if (apcm->started)
+               return 0;
+
+       apcm->started = 1;
+       multi = apcm->substream->runtime->channels;
+       /* Set up converting SRCs */
+       for (i = 0; i < apcm->n_srcc; i++) {
+               src = apcm->srccs[i];
+               src->ops->set_pm(src, ((i%multi) != (multi-1)));
+               src_mgr->src_disable(src_mgr, src);
+       }
+
+       /*  Set up recording SRC */
+       src = apcm->src;
+       src->ops->set_sf(src, convert_format(apcm->substream->runtime->format));
+       src->ops->set_sa(src, apcm->vm_block->addr);
+       src->ops->set_la(src, apcm->vm_block->addr + apcm->vm_block->size);
+       src->ops->set_ca(src, apcm->vm_block->addr);
+       src_mgr->src_disable(src_mgr, src);
+
+       /* Disable relevant SRCs firstly */
+       src_mgr->commit_write(src_mgr);
+
+       /* Enable SRCs respectively */
+       for (i = 0; i < apcm->n_srcc; i++) {
+               src = apcm->srccs[i];
+               src->ops->set_state(src, SRC_STATE_RUN);
+               src->ops->commit_write(src);
+               src_mgr->src_enable_s(src_mgr, src);
+       }
+       src = apcm->src;
+       src->ops->set_bm(src, 1);
+       src->ops->set_state(src, SRC_STATE_RUN);
+       src->ops->commit_write(src);
+       src_mgr->src_enable_s(src_mgr, src);
+
+       /* Enable relevant SRCs synchronously */
+       src_mgr->commit_write(src_mgr);
+
+       ct_timer_start(apcm->timer);
+       return 0;
+}
+
+static int
+atc_pcm_capture_position(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct src *src = apcm->src;
+
+       if (!src)
+               return 0;
+       return src->ops->get_ca(src) - apcm->vm_block->addr;
+}
+
+static int spdif_passthru_playback_get_resources(struct ct_atc *atc,
+                                                struct ct_atc_pcm *apcm)
+{
+       struct src_mgr *src_mgr = atc->rsc_mgrs[SRC];
+       struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER];
+       struct src_desc desc = {0};
+       struct amixer_desc mix_dsc = {0};
+       struct src *src;
+       int err;
+       int n_amixer = apcm->substream->runtime->channels, i;
+       unsigned int pitch, rsr = atc->pll_rate;
+
+       /* first release old resources */
+       atc_pcm_release_resources(atc, apcm);
+
+       /* Get SRC resource */
+       desc.multi = apcm->substream->runtime->channels;
+       desc.msr = 1;
+       while (apcm->substream->runtime->rate > (rsr * desc.msr))
+               desc.msr <<= 1;
+
+       desc.mode = MEMRD;
+       err = src_mgr->get_src(src_mgr, &desc, (struct src **)&apcm->src);
+       if (err)
+               goto error1;
+
+       pitch = atc_get_pitch(apcm->substream->runtime->rate, (rsr * desc.msr));
+       src = apcm->src;
+       src->ops->set_pitch(src, pitch);
+       src->ops->set_rom(src, select_rom(pitch));
+       src->ops->set_sf(src, convert_format(apcm->substream->runtime->format));
+       src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL));
+       src->ops->set_bp(src, 1);
+
+       /* Get AMIXER resource */
+       n_amixer = (n_amixer < 2) ? 2 : n_amixer;
+       apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
+       if (NULL == apcm->amixers) {
+               err = -ENOMEM;
+               goto error1;
+       }
+       mix_dsc.msr = desc.msr;
+       for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) {
+               err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc,
+                                       (struct amixer **)&apcm->amixers[i]);
+               if (err)
+                       goto error1;
+
+               apcm->n_amixer++;
+       }
+
+       /* Set up device virtual mem map */
+       err = ct_map_audio_buffer(atc, apcm);
+       if (err < 0)
+               goto error1;
+
+       return 0;
+
+error1:
+       atc_pcm_release_resources(atc, apcm);
+       return err;
+}
+
+static int atc_pll_init(struct ct_atc *atc, int rate)
+{
+       struct hw *hw = atc->hw;
+       int err;
+       err = hw->pll_init(hw, rate);
+       atc->pll_rate = err ? 0 : rate;
+       return err;
+}
+
+static int
+spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct dao *dao = container_of(atc->daios[SPDIFOO], struct dao, daio);
+       unsigned long flags;
+       unsigned int rate = apcm->substream->runtime->rate;
+       unsigned int status;
+       int err;
+       unsigned char iec958_con_fs;
+
+       switch (rate) {
+       case 48000:
+               iec958_con_fs = IEC958_AES3_CON_FS_48000;
+               break;
+       case 44100:
+               iec958_con_fs = IEC958_AES3_CON_FS_44100;
+               break;
+       case 32000:
+               iec958_con_fs = IEC958_AES3_CON_FS_32000;
+               break;
+       default:
+               return -ENOENT;
+       }
+
+       spin_lock_irqsave(&atc->atc_lock, flags);
+       dao->ops->get_spos(dao, &status);
+       if (((status >> 24) & IEC958_AES3_CON_FS) != iec958_con_fs) {
+               status &= ((~IEC958_AES3_CON_FS) << 24);
+               status |= (iec958_con_fs << 24);
+               dao->ops->set_spos(dao, status);
+               dao->ops->commit_write(dao);
+       }
+       if ((rate != atc->pll_rate) && (32000 != rate))
+               err = atc_pll_init(atc, rate);
+       spin_unlock_irqrestore(&atc->atc_lock, flags);
+
+       return err;
+}
+
+static int
+spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct src *src;
+       struct amixer *amixer;
+       struct dao *dao;
+       int err;
+       int i;
+       unsigned long flags;
+
+       if (NULL != apcm->src)
+               return 0;
+
+       /* Configure SPDIFOO and PLL to passthrough mode;
+        * determine pll_rate. */
+       err = spdif_passthru_playback_setup(atc, apcm);
+       if (err)
+               return err;
+
+       /* Get needed resources. */
+       err = spdif_passthru_playback_get_resources(atc, apcm);
+       if (err)
+               return err;
+
+       /* Connect resources */
+       src = apcm->src;
+       for (i = 0; i < apcm->n_amixer; i++) {
+               amixer = apcm->amixers[i];
+               amixer->ops->setup(amixer, &src->rsc, INIT_VOL, NULL);
+               src = src->ops->next_interleave(src);
+               if (NULL == src)
+                       src = apcm->src;
+       }
+       /* Connect to SPDIFOO */
+       spin_lock_irqsave(&atc->atc_lock, flags);
+       dao = container_of(atc->daios[SPDIFOO], struct dao, daio);
+       amixer = apcm->amixers[0];
+       dao->ops->set_left_input(dao, &amixer->rsc);
+       amixer = apcm->amixers[1];
+       dao->ops->set_right_input(dao, &amixer->rsc);
+       spin_unlock_irqrestore(&atc->atc_lock, flags);
+
+       ct_timer_prepare(apcm->timer);
+
+       return 0;
+}
+
+static int atc_select_line_in(struct ct_atc *atc)
+{
+       struct hw *hw = atc->hw;
+       struct ct_mixer *mixer = atc->mixer;
+       struct src *src;
+
+       if (hw->is_adc_source_selected(hw, ADC_LINEIN))
+               return 0;
+
+       mixer->set_input_left(mixer, MIX_MIC_IN, NULL);
+       mixer->set_input_right(mixer, MIX_MIC_IN, NULL);
+
+       hw->select_adc_source(hw, ADC_LINEIN);
+
+       src = atc->srcs[2];
+       mixer->set_input_left(mixer, MIX_LINE_IN, &src->rsc);
+       src = atc->srcs[3];
+       mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc);
+
+       return 0;
+}
+
+static int atc_select_mic_in(struct ct_atc *atc)
+{
+       struct hw *hw = atc->hw;
+       struct ct_mixer *mixer = atc->mixer;
+       struct src *src;
+
+       if (hw->is_adc_source_selected(hw, ADC_MICIN))
+               return 0;
+
+       mixer->set_input_left(mixer, MIX_LINE_IN, NULL);
+       mixer->set_input_right(mixer, MIX_LINE_IN, NULL);
+
+       hw->select_adc_source(hw, ADC_MICIN);
+
+       src = atc->srcs[2];
+       mixer->set_input_left(mixer, MIX_MIC_IN, &src->rsc);
+       src = atc->srcs[3];
+       mixer->set_input_right(mixer, MIX_MIC_IN, &src->rsc);
+
+       return 0;
+}
+
+static int atc_have_digit_io_switch(struct ct_atc *atc)
+{
+       struct hw *hw = atc->hw;
+
+       return hw->have_digit_io_switch(hw);
+}
+
+static int atc_select_digit_io(struct ct_atc *atc)
+{
+       struct hw *hw = atc->hw;
+
+       if (hw->is_adc_source_selected(hw, ADC_NONE))
+               return 0;
+
+       hw->select_adc_source(hw, ADC_NONE);
+
+       return 0;
+}
+
+static int atc_daio_unmute(struct ct_atc *atc, unsigned char state, int type)
+{
+       struct daio_mgr *daio_mgr = atc->rsc_mgrs[DAIO];
+
+       if (state)
+               daio_mgr->daio_enable(daio_mgr, atc->daios[type]);
+       else
+               daio_mgr->daio_disable(daio_mgr, atc->daios[type]);
+
+       daio_mgr->commit_write(daio_mgr);
+
+       return 0;
+}
+
+static int
+atc_dao_get_status(struct ct_atc *atc, unsigned int *status, int type)
+{
+       struct dao *dao = container_of(atc->daios[type], struct dao, daio);
+       return dao->ops->get_spos(dao, status);
+}
+
+static int
+atc_dao_set_status(struct ct_atc *atc, unsigned int status, int type)
+{
+       struct dao *dao = container_of(atc->daios[type], struct dao, daio);
+
+       dao->ops->set_spos(dao, status);
+       dao->ops->commit_write(dao);
+       return 0;
+}
+
+static int atc_line_front_unmute(struct ct_atc *atc, unsigned char state)
+{
+       return atc_daio_unmute(atc, state, LINEO1);
+}
+
+static int atc_line_surround_unmute(struct ct_atc *atc, unsigned char state)
+{
+       return atc_daio_unmute(atc, state, LINEO4);
+}
+
+static int atc_line_clfe_unmute(struct ct_atc *atc, unsigned char state)
+{
+       return atc_daio_unmute(atc, state, LINEO3);
+}
+
+static int atc_line_rear_unmute(struct ct_atc *atc, unsigned char state)
+{
+       return atc_daio_unmute(atc, state, LINEO2);
+}
+
+static int atc_line_in_unmute(struct ct_atc *atc, unsigned char state)
+{
+       return atc_daio_unmute(atc, state, LINEIM);
+}
+
+static int atc_spdif_out_unmute(struct ct_atc *atc, unsigned char state)
+{
+       return atc_daio_unmute(atc, state, SPDIFOO);
+}
+
+static int atc_spdif_in_unmute(struct ct_atc *atc, unsigned char state)
+{
+       return atc_daio_unmute(atc, state, SPDIFIO);
+}
+
+static int atc_spdif_out_get_status(struct ct_atc *atc, unsigned int *status)
+{
+       return atc_dao_get_status(atc, status, SPDIFOO);
+}
+
+static int atc_spdif_out_set_status(struct ct_atc *atc, unsigned int status)
+{
+       return atc_dao_set_status(atc, status, SPDIFOO);
+}
+
+static int atc_spdif_out_passthru(struct ct_atc *atc, unsigned char state)
+{
+       unsigned long flags;
+       struct dao_desc da_dsc = {0};
+       struct dao *dao;
+       int err;
+       struct ct_mixer *mixer = atc->mixer;
+       struct rsc *rscs[2] = {NULL};
+       unsigned int spos = 0;
+
+       spin_lock_irqsave(&atc->atc_lock, flags);
+       dao = container_of(atc->daios[SPDIFOO], struct dao, daio);
+       da_dsc.msr = state ? 1 : atc->msr;
+       da_dsc.passthru = state ? 1 : 0;
+       err = dao->ops->reinit(dao, &da_dsc);
+       if (state) {
+               spos = IEC958_DEFAULT_CON;
+       } else {
+               mixer->get_output_ports(mixer, MIX_SPDIF_OUT,
+                                       &rscs[0], &rscs[1]);
+               dao->ops->set_left_input(dao, rscs[0]);
+               dao->ops->set_right_input(dao, rscs[1]);
+               /* Restore PLL to atc->rsr if needed. */
+               if (atc->pll_rate != atc->rsr)
+                       err = atc_pll_init(atc, atc->rsr);
+       }
+       dao->ops->set_spos(dao, spos);
+       dao->ops->commit_write(dao);
+       spin_unlock_irqrestore(&atc->atc_lock, flags);
+
+       return err;
+}
+
+static int ct_atc_destroy(struct ct_atc *atc)
+{
+       struct daio_mgr *daio_mgr;
+       struct dao *dao;
+       struct dai *dai;
+       struct daio *daio;
+       struct sum_mgr *sum_mgr;
+       struct src_mgr *src_mgr;
+       struct srcimp_mgr *srcimp_mgr;
+       struct srcimp *srcimp;
+       struct ct_mixer *mixer;
+       int i = 0;
+
+       if (NULL == atc)
+               return 0;
+
+       if (atc->timer) {
+               ct_timer_free(atc->timer);
+               atc->timer = NULL;
+       }
+
+       /* Stop hardware and disable all interrupts */
+       if (NULL != atc->hw)
+               ((struct hw *)atc->hw)->card_stop(atc->hw);
+
+       /* Destroy internal mixer objects */
+       if (NULL != atc->mixer) {
+               mixer = atc->mixer;
+               mixer->set_input_left(mixer, MIX_LINE_IN, NULL);
+               mixer->set_input_right(mixer, MIX_LINE_IN, NULL);
+               mixer->set_input_left(mixer, MIX_MIC_IN, NULL);
+               mixer->set_input_right(mixer, MIX_MIC_IN, NULL);
+               mixer->set_input_left(mixer, MIX_SPDIF_IN, NULL);
+               mixer->set_input_right(mixer, MIX_SPDIF_IN, NULL);
+               ct_mixer_destroy(atc->mixer);
+       }
+
+       if (NULL != atc->daios) {
+               daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
+               for (i = 0; i < atc->n_daio; i++) {
+                       daio = atc->daios[i];
+                       if (daio->type < LINEIM) {
+                               dao = container_of(daio, struct dao, daio);
+                               dao->ops->clear_left_input(dao);
+                               dao->ops->clear_right_input(dao);
+                       } else {
+                               dai = container_of(daio, struct dai, daio);
+                               /* some thing to do for dai ... */
+                       }
+                       daio_mgr->put_daio(daio_mgr, daio);
+               }
+               kfree(atc->daios);
+       }
+
+       if (NULL != atc->pcm) {
+               sum_mgr = atc->rsc_mgrs[SUM];
+               for (i = 0; i < atc->n_pcm; i++)
+                       sum_mgr->put_sum(sum_mgr, atc->pcm[i]);
+
+               kfree(atc->pcm);
+       }
+
+       if (NULL != atc->srcs) {
+               src_mgr = atc->rsc_mgrs[SRC];
+               for (i = 0; i < atc->n_src; i++)
+                       src_mgr->put_src(src_mgr, atc->srcs[i]);
+
+               kfree(atc->srcs);
+       }
+
+       if (NULL != atc->srcimps) {
+               srcimp_mgr = atc->rsc_mgrs[SRCIMP];
+               for (i = 0; i < atc->n_srcimp; i++) {
+                       srcimp = atc->srcimps[i];
+                       srcimp->ops->unmap(srcimp);
+                       srcimp_mgr->put_srcimp(srcimp_mgr, atc->srcimps[i]);
+               }
+               kfree(atc->srcimps);
+       }
+
+       for (i = 0; i < NUM_RSCTYP; i++) {
+               if ((NULL != rsc_mgr_funcs[i].destroy) &&
+                   (NULL != atc->rsc_mgrs[i]))
+                       rsc_mgr_funcs[i].destroy(atc->rsc_mgrs[i]);
+
+       }
+
+       if (NULL != atc->hw)
+               destroy_hw_obj((struct hw *)atc->hw);
+
+       /* Destroy device virtual memory manager object */
+       if (NULL != atc->vm) {
+               ct_vm_destroy(atc->vm);
+               atc->vm = NULL;
+       }
+
+       kfree(atc);
+
+       return 0;
+}
+
+static int atc_dev_free(struct snd_device *dev)
+{
+       struct ct_atc *atc = dev->device_data;
+       return ct_atc_destroy(atc);
+}
+
+static int __devinit atc_identify_card(struct ct_atc *atc)
+{
+       const struct snd_pci_quirk *p;
+       const struct snd_pci_quirk *list;
+
+       switch (atc->chip_type) {
+       case ATC20K1:
+               atc->chip_name = "20K1";
+               list = subsys_20k1_list;
+               break;
+       case ATC20K2:
+               atc->chip_name = "20K2";
+               list = subsys_20k2_list;
+               break;
+       default:
+               return -ENOENT;
+       }
+       p = snd_pci_quirk_lookup(atc->pci, list);
+       if (!p)
+               return -ENOENT;
+       atc->model = p->value;
+       atc->model_name = ct_subsys_name[atc->model];
+       snd_printd("ctxfi: chip %s model %s (%04x:%04x) is found\n",
+                  atc->chip_name, atc->model_name,
+                  atc->pci->subsystem_vendor,
+                  atc->pci->subsystem_device);
+       return 0;
+}
+
+int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc)
+{
+       enum CTALSADEVS i;
+       int err;
+
+       alsa_dev_funcs[MIXER].public_name = atc->chip_name;
+
+       for (i = 0; i < NUM_CTALSADEVS; i++) {
+               if (NULL == alsa_dev_funcs[i].create)
+                       continue;
+
+               err = alsa_dev_funcs[i].create(atc, i,
+                               alsa_dev_funcs[i].public_name);
+               if (err) {
+                       printk(KERN_ERR "ctxfi: "
+                              "Creating alsa device %d failed!\n", i);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+static int __devinit atc_create_hw_devs(struct ct_atc *atc)
+{
+       struct hw *hw;
+       struct card_conf info = {0};
+       int i, err;
+
+       err = create_hw_obj(atc->pci, atc->chip_type, atc->model, &hw);
+       if (err) {
+               printk(KERN_ERR "Failed to create hw obj!!!\n");
+               return err;
+       }
+       atc->hw = hw;
+
+       /* Initialize card hardware. */
+       info.rsr = atc->rsr;
+       info.msr = atc->msr;
+       info.vm_pgt_phys = atc_get_ptp_phys(atc, 0);
+       err = hw->card_init(hw, &info);
+       if (err < 0)
+               return err;
+
+       for (i = 0; i < NUM_RSCTYP; i++) {
+               if (NULL == rsc_mgr_funcs[i].create)
+                       continue;
+
+               err = rsc_mgr_funcs[i].create(atc->hw, &atc->rsc_mgrs[i]);
+               if (err) {
+                       printk(KERN_ERR "ctxfi: "
+                              "Failed to create rsc_mgr %d!!!\n", i);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+static int __devinit atc_get_resources(struct ct_atc *atc)
+{
+       struct daio_desc da_desc = {0};
+       struct daio_mgr *daio_mgr;
+       struct src_desc src_dsc = {0};
+       struct src_mgr *src_mgr;
+       struct srcimp_desc srcimp_dsc = {0};
+       struct srcimp_mgr *srcimp_mgr;
+       struct sum_desc sum_dsc = {0};
+       struct sum_mgr *sum_mgr;
+       int err, i;
+
+       atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL);
+       if (NULL == atc->daios)
+               return -ENOMEM;
+
+       atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
+       if (NULL == atc->srcs)
+               return -ENOMEM;
+
+       atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
+       if (NULL == atc->srcimps)
+               return -ENOMEM;
+
+       atc->pcm = kzalloc(sizeof(void *)*(2*4), GFP_KERNEL);
+       if (NULL == atc->pcm)
+               return -ENOMEM;
+
+       daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
+       da_desc.msr = atc->msr;
+       for (i = 0, atc->n_daio = 0; i < DAIONUM-1; i++) {
+               da_desc.type = i;
+               err = daio_mgr->get_daio(daio_mgr, &da_desc,
+                                       (struct daio **)&atc->daios[i]);
+               if (err) {
+                       printk(KERN_ERR "ctxfi: Failed to get DAIO "
+                                       "resource %d!!!\n", i);
+                       return err;
+               }
+               atc->n_daio++;
+       }
+       if (atc->model == CTSB073X)
+               da_desc.type = SPDIFI1;
+       else
+               da_desc.type = SPDIFIO;
+       err = daio_mgr->get_daio(daio_mgr, &da_desc,
+                               (struct daio **)&atc->daios[i]);
+       if (err) {
+               printk(KERN_ERR "ctxfi: Failed to get S/PDIF-in resource!!!\n");
+               return err;
+       }
+       atc->n_daio++;
+
+       src_mgr = atc->rsc_mgrs[SRC];
+       src_dsc.multi = 1;
+       src_dsc.msr = atc->msr;
+       src_dsc.mode = ARCRW;
+       for (i = 0, atc->n_src = 0; i < (2*2); i++) {
+               err = src_mgr->get_src(src_mgr, &src_dsc,
+                                       (struct src **)&atc->srcs[i]);
+               if (err)
+                       return err;
+
+               atc->n_src++;
+       }
+
+       srcimp_mgr = atc->rsc_mgrs[SRCIMP];
+       srcimp_dsc.msr = 8; /* SRCIMPs for S/PDIFIn SRT */
+       for (i = 0, atc->n_srcimp = 0; i < (2*1); i++) {
+               err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc,
+                                       (struct srcimp **)&atc->srcimps[i]);
+               if (err)
+                       return err;
+
+               atc->n_srcimp++;
+       }
+       srcimp_dsc.msr = 8; /* SRCIMPs for LINE/MICIn SRT */
+       for (i = 0; i < (2*1); i++) {
+               err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc,
+                               (struct srcimp **)&atc->srcimps[2*1+i]);
+               if (err)
+                       return err;
+
+               atc->n_srcimp++;
+       }
+
+       sum_mgr = atc->rsc_mgrs[SUM];
+       sum_dsc.msr = atc->msr;
+       for (i = 0, atc->n_pcm = 0; i < (2*4); i++) {
+               err = sum_mgr->get_sum(sum_mgr, &sum_dsc,
+                                       (struct sum **)&atc->pcm[i]);
+               if (err)
+                       return err;
+
+               atc->n_pcm++;
+       }
+
+       err = ct_mixer_create(atc, (struct ct_mixer **)&atc->mixer);
+       if (err) {
+               printk(KERN_ERR "ctxfi: Failed to create mixer obj!!!\n");
+               return err;
+       }
+
+       return 0;
+}
+
+static void __devinit
+atc_connect_dai(struct src_mgr *src_mgr, struct dai *dai,
+               struct src **srcs, struct srcimp **srcimps)
+{
+       struct rsc *rscs[2] = {NULL};
+       struct src *src;
+       struct srcimp *srcimp;
+       int i = 0;
+
+       rscs[0] = &dai->daio.rscl;
+       rscs[1] = &dai->daio.rscr;
+       for (i = 0; i < 2; i++) {
+               src = srcs[i];
+               srcimp = srcimps[i];
+               srcimp->ops->map(srcimp, src, rscs[i]);
+               src_mgr->src_disable(src_mgr, src);
+       }
+
+       src_mgr->commit_write(src_mgr); /* Actually disable SRCs */
+
+       src = srcs[0];
+       src->ops->set_pm(src, 1);
+       for (i = 0; i < 2; i++) {
+               src = srcs[i];
+               src->ops->set_state(src, SRC_STATE_RUN);
+               src->ops->commit_write(src);
+               src_mgr->src_enable_s(src_mgr, src);
+       }
+
+       dai->ops->set_srt_srcl(dai, &(srcs[0]->rsc));
+       dai->ops->set_srt_srcr(dai, &(srcs[1]->rsc));
+
+       dai->ops->set_enb_src(dai, 1);
+       dai->ops->set_enb_srt(dai, 1);
+       dai->ops->commit_write(dai);
+
+       src_mgr->commit_write(src_mgr); /* Synchronously enable SRCs */
+}
+
+static void __devinit atc_connect_resources(struct ct_atc *atc)
+{
+       struct dai *dai;
+       struct dao *dao;
+       struct src *src;
+       struct sum *sum;
+       struct ct_mixer *mixer;
+       struct rsc *rscs[2] = {NULL};
+       int i, j;
+
+       mixer = atc->mixer;
+
+       for (i = MIX_WAVE_FRONT, j = LINEO1; i <= MIX_SPDIF_OUT; i++, j++) {
+               mixer->get_output_ports(mixer, i, &rscs[0], &rscs[1]);
+               dao = container_of(atc->daios[j], struct dao, daio);
+               dao->ops->set_left_input(dao, rscs[0]);
+               dao->ops->set_right_input(dao, rscs[1]);
+       }
+
+       dai = container_of(atc->daios[LINEIM], struct dai, daio);
+       atc_connect_dai(atc->rsc_mgrs[SRC], dai,
+                       (struct src **)&atc->srcs[2],
+                       (struct srcimp **)&atc->srcimps[2]);
+       src = atc->srcs[2];
+       mixer->set_input_left(mixer, MIX_LINE_IN, &src->rsc);
+       src = atc->srcs[3];
+       mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc);
+
+       dai = container_of(atc->daios[SPDIFIO], struct dai, daio);
+       atc_connect_dai(atc->rsc_mgrs[SRC], dai,
+                       (struct src **)&atc->srcs[0],
+                       (struct srcimp **)&atc->srcimps[0]);
+
+       src = atc->srcs[0];
+       mixer->set_input_left(mixer, MIX_SPDIF_IN, &src->rsc);
+       src = atc->srcs[1];
+       mixer->set_input_right(mixer, MIX_SPDIF_IN, &src->rsc);
+
+       for (i = MIX_PCMI_FRONT, j = 0; i <= MIX_PCMI_SURROUND; i++, j += 2) {
+               sum = atc->pcm[j];
+               mixer->set_input_left(mixer, i, &sum->rsc);
+               sum = atc->pcm[j+1];
+               mixer->set_input_right(mixer, i, &sum->rsc);
+       }
+}
+
+static struct ct_atc atc_preset __devinitdata = {
+       .map_audio_buffer = ct_map_audio_buffer,
+       .unmap_audio_buffer = ct_unmap_audio_buffer,
+       .pcm_playback_prepare = atc_pcm_playback_prepare,
+       .pcm_release_resources = atc_pcm_release_resources,
+       .pcm_playback_start = atc_pcm_playback_start,
+       .pcm_playback_stop = atc_pcm_stop,
+       .pcm_playback_position = atc_pcm_playback_position,
+       .pcm_capture_prepare = atc_pcm_capture_prepare,
+       .pcm_capture_start = atc_pcm_capture_start,
+       .pcm_capture_stop = atc_pcm_stop,
+       .pcm_capture_position = atc_pcm_capture_position,
+       .spdif_passthru_playback_prepare = spdif_passthru_playback_prepare,
+       .get_ptp_phys = atc_get_ptp_phys,
+       .select_line_in = atc_select_line_in,
+       .select_mic_in = atc_select_mic_in,
+       .select_digit_io = atc_select_digit_io,
+       .line_front_unmute = atc_line_front_unmute,
+       .line_surround_unmute = atc_line_surround_unmute,
+       .line_clfe_unmute = atc_line_clfe_unmute,
+       .line_rear_unmute = atc_line_rear_unmute,
+       .line_in_unmute = atc_line_in_unmute,
+       .spdif_out_unmute = atc_spdif_out_unmute,
+       .spdif_in_unmute = atc_spdif_in_unmute,
+       .spdif_out_get_status = atc_spdif_out_get_status,
+       .spdif_out_set_status = atc_spdif_out_set_status,
+       .spdif_out_passthru = atc_spdif_out_passthru,
+       .have_digit_io_switch = atc_have_digit_io_switch,
+};
+
+/**
+ *  ct_atc_create - create and initialize a hardware manager
+ *  @card: corresponding alsa card object
+ *  @pci: corresponding kernel pci device object
+ *  @ratc: return created object address in it
+ *
+ *  Creates and initializes a hardware manager.
+ *
+ *  Creates kmallocated ct_atc structure. Initializes hardware.
+ *  Returns 0 if suceeds, or negative error code if fails.
+ */
+
+int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
+                           unsigned int rsr, unsigned int msr,
+                           int chip_type, struct ct_atc **ratc)
+{
+       struct ct_atc *atc;
+       static struct snd_device_ops ops = {
+               .dev_free = atc_dev_free,
+       };
+       int err;
+
+       *ratc = NULL;
+
+       atc = kzalloc(sizeof(*atc), GFP_KERNEL);
+       if (NULL == atc)
+               return -ENOMEM;
+
+       /* Set operations */
+       *atc = atc_preset;
+
+       atc->card = card;
+       atc->pci = pci;
+       atc->rsr = rsr;
+       atc->msr = msr;
+       atc->chip_type = chip_type;
+
+       spin_lock_init(&atc->atc_lock);
+
+       /* Find card model */
+       err = atc_identify_card(atc);
+       if (err < 0) {
+               printk(KERN_ERR "ctatc: Card not recognised\n");
+               goto error1;
+       }
+
+       /* Set up device virtual memory management object */
+       err = ct_vm_create(&atc->vm);
+       if (err < 0)
+               goto error1;
+
+       /* Create all atc hw devices */
+       err = atc_create_hw_devs(atc);
+       if (err < 0)
+               goto error1;
+
+       /* Get resources */
+       err = atc_get_resources(atc);
+       if (err < 0)
+               goto error1;
+
+       /* Build topology */
+       atc_connect_resources(atc);
+
+       atc->timer = ct_timer_new(atc);
+       if (!atc->timer)
+               goto error1;
+
+       err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, atc, &ops);
+       if (err < 0)
+               goto error1;
+
+       snd_card_set_dev(card, &pci->dev);
+
+       *ratc = atc;
+       return 0;
+
+error1:
+       ct_atc_destroy(atc);
+       printk(KERN_ERR "ctxfi: Something wrong!!!\n");
+       return err;
+}
diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h
new file mode 100644 (file)
index 0000000..a033472
--- /dev/null
@@ -0,0 +1,147 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctatc.h
+ *
+ * @Brief
+ * This file contains the definition of the device resource management object.
+ *
+ * @Author     Liu Chun
+ * @Date       Mar 28 2008
+ *
+ */
+
+#ifndef CTATC_H
+#define CTATC_H
+
+#include <linux/types.h>
+#include <linux/spinlock_types.h>
+#include <linux/pci.h>
+#include <linux/timer.h>
+#include <sound/core.h>
+
+#include "ctvmem.h"
+#include "ctresource.h"
+
+enum CTALSADEVS {              /* Types of alsa devices */
+       FRONT,
+       SURROUND,
+       CLFE,
+       SIDE,
+       IEC958,
+       MIXER,
+       NUM_CTALSADEVS          /* This should always be the last */
+};
+
+struct ct_atc_chip_sub_details {
+       u16 subsys;
+       const char *nm_model;
+};
+
+struct ct_atc_chip_details {
+       u16 vendor;
+       u16 device;
+       const struct ct_atc_chip_sub_details *sub_details;
+       const char *nm_card;
+};
+
+struct ct_atc;
+struct ct_timer;
+struct ct_timer_instance;
+
+/* alsa pcm stream descriptor */
+struct ct_atc_pcm {
+       struct snd_pcm_substream *substream;
+       void (*interrupt)(struct ct_atc_pcm *apcm);
+       struct ct_timer_instance *timer;
+       unsigned int started:1;
+
+       /* Only mono and interleaved modes are supported now. */
+       struct ct_vm_block *vm_block;
+       void *src;              /* SRC for interacting with host memory */
+       void **srccs;           /* SRCs for sample rate conversion */
+       void **srcimps;         /* SRC Input Mappers */
+       void **amixers;         /* AMIXERs for routing converted data */
+       void *mono;             /* A SUM resource for mixing chs to one */
+       unsigned char n_srcc;   /* Number of converting SRCs */
+       unsigned char n_srcimp; /* Number of SRC Input Mappers */
+       unsigned char n_amixer; /* Number of AMIXERs */
+};
+
+/* Chip resource management object */
+struct ct_atc {
+       struct pci_dev *pci;
+       struct snd_card *card;
+       unsigned int rsr; /* reference sample rate in Hz */
+       unsigned int msr; /* master sample rate in rsr */
+       unsigned int pll_rate; /* current rate of Phase Lock Loop */
+
+       int chip_type;
+       int model;
+       const char *chip_name;
+       const char *model_name;
+
+       struct ct_vm *vm; /* device virtual memory manager for this card */
+       int (*map_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+       void (*unmap_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+       unsigned long (*get_ptp_phys)(struct ct_atc *atc, int index);
+
+       spinlock_t atc_lock;
+
+       int (*pcm_playback_prepare)(struct ct_atc *atc,
+                                   struct ct_atc_pcm *apcm);
+       int (*pcm_playback_start)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+       int (*pcm_playback_stop)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+       int (*pcm_playback_position)(struct ct_atc *atc,
+                                    struct ct_atc_pcm *apcm);
+       int (*spdif_passthru_playback_prepare)(struct ct_atc *atc,
+                                              struct ct_atc_pcm *apcm);
+       int (*pcm_capture_prepare)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+       int (*pcm_capture_start)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+       int (*pcm_capture_stop)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+       int (*pcm_capture_position)(struct ct_atc *atc,
+                                   struct ct_atc_pcm *apcm);
+       int (*pcm_release_resources)(struct ct_atc *atc,
+                                    struct ct_atc_pcm *apcm);
+       int (*select_line_in)(struct ct_atc *atc);
+       int (*select_mic_in)(struct ct_atc *atc);
+       int (*select_digit_io)(struct ct_atc *atc);
+       int (*line_front_unmute)(struct ct_atc *atc, unsigned char state);
+       int (*line_surround_unmute)(struct ct_atc *atc, unsigned char state);
+       int (*line_clfe_unmute)(struct ct_atc *atc, unsigned char state);
+       int (*line_rear_unmute)(struct ct_atc *atc, unsigned char state);
+       int (*line_in_unmute)(struct ct_atc *atc, unsigned char state);
+       int (*spdif_out_unmute)(struct ct_atc *atc, unsigned char state);
+       int (*spdif_in_unmute)(struct ct_atc *atc, unsigned char state);
+       int (*spdif_out_get_status)(struct ct_atc *atc, unsigned int *status);
+       int (*spdif_out_set_status)(struct ct_atc *atc, unsigned int status);
+       int (*spdif_out_passthru)(struct ct_atc *atc, unsigned char state);
+       int (*have_digit_io_switch)(struct ct_atc *atc);
+
+       /* Don't touch! Used for internal object. */
+       void *rsc_mgrs[NUM_RSCTYP]; /* chip resource managers */
+       void *mixer;            /* internal mixer object */
+       void *hw;               /* chip specific hardware access object */
+       void **daios;           /* digital audio io resources */
+       void **pcm;             /* SUMs for collecting all pcm stream */
+       void **srcs;            /* Sample Rate Converters for input signal */
+       void **srcimps;         /* input mappers for SRCs */
+       unsigned char n_daio;
+       unsigned char n_src;
+       unsigned char n_srcimp;
+       unsigned char n_pcm;
+
+       struct ct_timer *timer;
+};
+
+
+int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
+                           unsigned int rsr, unsigned int msr, int chip_type,
+                           struct ct_atc **ratc);
+int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc);
+
+#endif /* CTATC_H */
diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c
new file mode 100644 (file)
index 0000000..082e35c
--- /dev/null
@@ -0,0 +1,769 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctdaio.c
+ *
+ * @Brief
+ * This file contains the implementation of Digital Audio Input Output
+ * resource management object.
+ *
+ * @Author     Liu Chun
+ * @Date       May 23 2008
+ *
+ */
+
+#include "ctdaio.h"
+#include "cthardware.h"
+#include "ctimap.h"
+#include <linux/slab.h>
+#include <linux/kernel.h>
+
+#define DAIO_RESOURCE_NUM      NUM_DAIOTYP
+#define DAIO_OUT_MAX           SPDIFOO
+
+union daio_usage {
+       struct {
+               unsigned short lineo1:1;
+               unsigned short lineo2:1;
+               unsigned short lineo3:1;
+               unsigned short lineo4:1;
+               unsigned short spdifoo:1;
+               unsigned short lineim:1;
+               unsigned short spdifio:1;
+               unsigned short spdifi1:1;
+       } bf;
+       unsigned short data;
+};
+
+struct daio_rsc_idx {
+       unsigned short left;
+       unsigned short right;
+};
+
+struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = {
+       [LINEO1] = {.left = 0x00, .right = 0x01},
+       [LINEO2] = {.left = 0x18, .right = 0x19},
+       [LINEO3] = {.left = 0x08, .right = 0x09},
+       [LINEO4] = {.left = 0x10, .right = 0x11},
+       [LINEIM] = {.left = 0x1b5, .right = 0x1bd},
+       [SPDIFOO] = {.left = 0x20, .right = 0x21},
+       [SPDIFIO] = {.left = 0x15, .right = 0x1d},
+       [SPDIFI1] = {.left = 0x95, .right = 0x9d},
+};
+
+struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = {
+       [LINEO1] = {.left = 0x40, .right = 0x41},
+       [LINEO2] = {.left = 0x70, .right = 0x71},
+       [LINEO3] = {.left = 0x50, .right = 0x51},
+       [LINEO4] = {.left = 0x60, .right = 0x61},
+       [LINEIM] = {.left = 0x45, .right = 0xc5},
+       [SPDIFOO] = {.left = 0x00, .right = 0x01},
+       [SPDIFIO] = {.left = 0x05, .right = 0x85},
+};
+
+static int daio_master(struct rsc *rsc)
+{
+       /* Actually, this is not the resource index of DAIO.
+        * For DAO, it is the input mapper index. And, for DAI,
+        * it is the output time-slot index. */
+       return rsc->conj = rsc->idx;
+}
+
+static int daio_index(const struct rsc *rsc)
+{
+       return rsc->conj;
+}
+
+static int daio_out_next_conj(struct rsc *rsc)
+{
+       return rsc->conj += 2;
+}
+
+static int daio_in_next_conj_20k1(struct rsc *rsc)
+{
+       return rsc->conj += 0x200;
+}
+
+static int daio_in_next_conj_20k2(struct rsc *rsc)
+{
+       return rsc->conj += 0x100;
+}
+
+static struct rsc_ops daio_out_rsc_ops = {
+       .master         = daio_master,
+       .next_conj      = daio_out_next_conj,
+       .index          = daio_index,
+       .output_slot    = NULL,
+};
+
+static struct rsc_ops daio_in_rsc_ops_20k1 = {
+       .master         = daio_master,
+       .next_conj      = daio_in_next_conj_20k1,
+       .index          = NULL,
+       .output_slot    = daio_index,
+};
+
+static struct rsc_ops daio_in_rsc_ops_20k2 = {
+       .master         = daio_master,
+       .next_conj      = daio_in_next_conj_20k2,
+       .index          = NULL,
+       .output_slot    = daio_index,
+};
+
+static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw)
+{
+       switch (hw->chip_type) {
+       case ATC20K1:
+               switch (type) {
+               case SPDIFOO:   return 0;
+               case SPDIFIO:   return 0;
+               case SPDIFI1:   return 1;
+               case LINEO1:    return 4;
+               case LINEO2:    return 7;
+               case LINEO3:    return 5;
+               case LINEO4:    return 6;
+               case LINEIM:    return 7;
+               default:        return -EINVAL;
+               }
+       case ATC20K2:
+               switch (type) {
+               case SPDIFOO:   return 0;
+               case SPDIFIO:   return 0;
+               case LINEO1:    return 4;
+               case LINEO2:    return 7;
+               case LINEO3:    return 5;
+               case LINEO4:    return 6;
+               case LINEIM:    return 4;
+               default:        return -EINVAL;
+               }
+       default:
+               return -EINVAL;
+       }
+}
+
+static int dao_rsc_reinit(struct dao *dao, const struct dao_desc *desc);
+
+static int dao_spdif_get_spos(struct dao *dao, unsigned int *spos)
+{
+       ((struct hw *)dao->hw)->dao_get_spos(dao->ctrl_blk, spos);
+       return 0;
+}
+
+static int dao_spdif_set_spos(struct dao *dao, unsigned int spos)
+{
+       ((struct hw *)dao->hw)->dao_set_spos(dao->ctrl_blk, spos);
+       return 0;
+}
+
+static int dao_commit_write(struct dao *dao)
+{
+       ((struct hw *)dao->hw)->dao_commit_write(dao->hw,
+               daio_device_index(dao->daio.type, dao->hw), dao->ctrl_blk);
+       return 0;
+}
+
+static int dao_set_left_input(struct dao *dao, struct rsc *input)
+{
+       struct imapper *entry;
+       struct daio *daio = &dao->daio;
+       int i;
+
+       entry = kzalloc((sizeof(*entry) * daio->rscl.msr), GFP_KERNEL);
+       if (NULL == entry)
+               return -ENOMEM;
+
+       /* Program master and conjugate resources */
+       input->ops->master(input);
+       daio->rscl.ops->master(&daio->rscl);
+       for (i = 0; i < daio->rscl.msr; i++, entry++) {
+               entry->slot = input->ops->output_slot(input);
+               entry->user = entry->addr = daio->rscl.ops->index(&daio->rscl);
+               dao->mgr->imap_add(dao->mgr, entry);
+               dao->imappers[i] = entry;
+
+               input->ops->next_conj(input);
+               daio->rscl.ops->next_conj(&daio->rscl);
+       }
+       input->ops->master(input);
+       daio->rscl.ops->master(&daio->rscl);
+
+       return 0;
+}
+
+static int dao_set_right_input(struct dao *dao, struct rsc *input)
+{
+       struct imapper *entry;
+       struct daio *daio = &dao->daio;
+       int i;
+
+       entry = kzalloc((sizeof(*entry) * daio->rscr.msr), GFP_KERNEL);
+       if (NULL == entry)
+               return -ENOMEM;
+
+       /* Program master and conjugate resources */
+       input->ops->master(input);
+       daio->rscr.ops->master(&daio->rscr);
+       for (i = 0; i < daio->rscr.msr; i++, entry++) {
+               entry->slot = input->ops->output_slot(input);
+               entry->user = entry->addr = daio->rscr.ops->index(&daio->rscr);
+               dao->mgr->imap_add(dao->mgr, entry);
+               dao->imappers[daio->rscl.msr + i] = entry;
+
+               input->ops->next_conj(input);
+               daio->rscr.ops->next_conj(&daio->rscr);
+       }
+       input->ops->master(input);
+       daio->rscr.ops->master(&daio->rscr);
+
+       return 0;
+}
+
+static int dao_clear_left_input(struct dao *dao)
+{
+       struct imapper *entry;
+       struct daio *daio = &dao->daio;
+       int i;
+
+       if (NULL == dao->imappers[0])
+               return 0;
+
+       entry = dao->imappers[0];
+       dao->mgr->imap_delete(dao->mgr, entry);
+       /* Program conjugate resources */
+       for (i = 1; i < daio->rscl.msr; i++) {
+               entry = dao->imappers[i];
+               dao->mgr->imap_delete(dao->mgr, entry);
+               dao->imappers[i] = NULL;
+       }
+
+       kfree(dao->imappers[0]);
+       dao->imappers[0] = NULL;
+
+       return 0;
+}
+
+static int dao_clear_right_input(struct dao *dao)
+{
+       struct imapper *entry;
+       struct daio *daio = &dao->daio;
+       int i;
+
+       if (NULL == dao->imappers[daio->rscl.msr])
+               return 0;
+
+       entry = dao->imappers[daio->rscl.msr];
+       dao->mgr->imap_delete(dao->mgr, entry);
+       /* Program conjugate resources */
+       for (i = 1; i < daio->rscr.msr; i++) {
+               entry = dao->imappers[daio->rscl.msr + i];
+               dao->mgr->imap_delete(dao->mgr, entry);
+               dao->imappers[daio->rscl.msr + i] = NULL;
+       }
+
+       kfree(dao->imappers[daio->rscl.msr]);
+       dao->imappers[daio->rscl.msr] = NULL;
+
+       return 0;
+}
+
+static struct dao_rsc_ops dao_ops = {
+       .set_spos               = dao_spdif_set_spos,
+       .commit_write           = dao_commit_write,
+       .get_spos               = dao_spdif_get_spos,
+       .reinit                 = dao_rsc_reinit,
+       .set_left_input         = dao_set_left_input,
+       .set_right_input        = dao_set_right_input,
+       .clear_left_input       = dao_clear_left_input,
+       .clear_right_input      = dao_clear_right_input,
+};
+
+static int dai_set_srt_srcl(struct dai *dai, struct rsc *src)
+{
+       src->ops->master(src);
+       ((struct hw *)dai->hw)->dai_srt_set_srcm(dai->ctrl_blk,
+                                               src->ops->index(src));
+       return 0;
+}
+
+static int dai_set_srt_srcr(struct dai *dai, struct rsc *src)
+{
+       src->ops->master(src);
+       ((struct hw *)dai->hw)->dai_srt_set_srco(dai->ctrl_blk,
+                                               src->ops->index(src));
+       return 0;
+}
+
+static int dai_set_srt_msr(struct dai *dai, unsigned int msr)
+{
+       unsigned int rsr;
+
+       for (rsr = 0; msr > 1; msr >>= 1)
+               rsr++;
+
+       ((struct hw *)dai->hw)->dai_srt_set_rsr(dai->ctrl_blk, rsr);
+       return 0;
+}
+
+static int dai_set_enb_src(struct dai *dai, unsigned int enb)
+{
+       ((struct hw *)dai->hw)->dai_srt_set_ec(dai->ctrl_blk, enb);
+       return 0;
+}
+
+static int dai_set_enb_srt(struct dai *dai, unsigned int enb)
+{
+       ((struct hw *)dai->hw)->dai_srt_set_et(dai->ctrl_blk, enb);
+       return 0;
+}
+
+static int dai_commit_write(struct dai *dai)
+{
+       ((struct hw *)dai->hw)->dai_commit_write(dai->hw,
+               daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk);
+       return 0;
+}
+
+static struct dai_rsc_ops dai_ops = {
+       .set_srt_srcl           = dai_set_srt_srcl,
+       .set_srt_srcr           = dai_set_srt_srcr,
+       .set_srt_msr            = dai_set_srt_msr,
+       .set_enb_src            = dai_set_enb_src,
+       .set_enb_srt            = dai_set_enb_srt,
+       .commit_write           = dai_commit_write,
+};
+
+static int daio_rsc_init(struct daio *daio,
+                        const struct daio_desc *desc,
+                        void *hw)
+{
+       int err;
+       unsigned int idx_l, idx_r;
+
+       switch (((struct hw *)hw)->chip_type) {
+       case ATC20K1:
+               idx_l = idx_20k1[desc->type].left;
+               idx_r = idx_20k1[desc->type].right;
+               break;
+       case ATC20K2:
+               idx_l = idx_20k2[desc->type].left;
+               idx_r = idx_20k2[desc->type].right;
+               break;
+       default:
+               return -EINVAL;
+       }
+       err = rsc_init(&daio->rscl, idx_l, DAIO, desc->msr, hw);
+       if (err)
+               return err;
+
+       err = rsc_init(&daio->rscr, idx_r, DAIO, desc->msr, hw);
+       if (err)
+               goto error1;
+
+       /* Set daio->rscl/r->ops to daio specific ones */
+       if (desc->type <= DAIO_OUT_MAX) {
+               daio->rscl.ops = daio->rscr.ops = &daio_out_rsc_ops;
+       } else {
+               switch (((struct hw *)hw)->chip_type) {
+               case ATC20K1:
+                       daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k1;
+                       break;
+               case ATC20K2:
+                       daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k2;
+                       break;
+               default:
+                       break;
+               }
+       }
+       daio->type = desc->type;
+
+       return 0;
+
+error1:
+       rsc_uninit(&daio->rscl);
+       return err;
+}
+
+static int daio_rsc_uninit(struct daio *daio)
+{
+       rsc_uninit(&daio->rscl);
+       rsc_uninit(&daio->rscr);
+
+       return 0;
+}
+
+static int dao_rsc_init(struct dao *dao,
+                       const struct daio_desc *desc,
+                       struct daio_mgr *mgr)
+{
+       struct hw *hw = mgr->mgr.hw;
+       unsigned int conf;
+       int err;
+
+       err = daio_rsc_init(&dao->daio, desc, mgr->mgr.hw);
+       if (err)
+               return err;
+
+       dao->imappers = kzalloc(sizeof(void *)*desc->msr*2, GFP_KERNEL);
+       if (NULL == dao->imappers) {
+               err = -ENOMEM;
+               goto error1;
+       }
+       dao->ops = &dao_ops;
+       dao->mgr = mgr;
+       dao->hw = hw;
+       err = hw->dao_get_ctrl_blk(&dao->ctrl_blk);
+       if (err)
+               goto error2;
+
+       hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk,
+                       daio_device_index(dao->daio.type, hw));
+       hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
+
+       conf = (desc->msr & 0x7) | (desc->passthru << 3);
+       hw->daio_mgr_dao_init(mgr->mgr.ctrl_blk,
+                       daio_device_index(dao->daio.type, hw), conf);
+       hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk,
+                       daio_device_index(dao->daio.type, hw));
+       hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
+
+       return 0;
+
+error2:
+       kfree(dao->imappers);
+       dao->imappers = NULL;
+error1:
+       daio_rsc_uninit(&dao->daio);
+       return err;
+}
+
+static int dao_rsc_uninit(struct dao *dao)
+{
+       if (NULL != dao->imappers) {
+               if (NULL != dao->imappers[0])
+                       dao_clear_left_input(dao);
+
+               if (NULL != dao->imappers[dao->daio.rscl.msr])
+                       dao_clear_right_input(dao);
+
+               kfree(dao->imappers);
+               dao->imappers = NULL;
+       }
+       ((struct hw *)dao->hw)->dao_put_ctrl_blk(dao->ctrl_blk);
+       dao->hw = dao->ctrl_blk = NULL;
+       daio_rsc_uninit(&dao->daio);
+
+       return 0;
+}
+
+static int dao_rsc_reinit(struct dao *dao, const struct dao_desc *desc)
+{
+       struct daio_mgr *mgr = dao->mgr;
+       struct daio_desc dsc = {0};
+
+       dsc.type = dao->daio.type;
+       dsc.msr = desc->msr;
+       dsc.passthru = desc->passthru;
+       dao_rsc_uninit(dao);
+       return dao_rsc_init(dao, &dsc, mgr);
+}
+
+static int dai_rsc_init(struct dai *dai,
+                       const struct daio_desc *desc,
+                       struct daio_mgr *mgr)
+{
+       int err;
+       struct hw *hw = mgr->mgr.hw;
+       unsigned int rsr, msr;
+
+       err = daio_rsc_init(&dai->daio, desc, mgr->mgr.hw);
+       if (err)
+               return err;
+
+       dai->ops = &dai_ops;
+       dai->hw = mgr->mgr.hw;
+       err = hw->dai_get_ctrl_blk(&dai->ctrl_blk);
+       if (err)
+               goto error1;
+
+       for (rsr = 0, msr = desc->msr; msr > 1; msr >>= 1)
+               rsr++;
+
+       hw->dai_srt_set_rsr(dai->ctrl_blk, rsr);
+       hw->dai_srt_set_drat(dai->ctrl_blk, 0);
+       /* default to disabling control of a SRC */
+       hw->dai_srt_set_ec(dai->ctrl_blk, 0);
+       hw->dai_srt_set_et(dai->ctrl_blk, 0); /* default to disabling SRT */
+       hw->dai_commit_write(hw,
+               daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk);
+
+       return 0;
+
+error1:
+       daio_rsc_uninit(&dai->daio);
+       return err;
+}
+
+static int dai_rsc_uninit(struct dai *dai)
+{
+       ((struct hw *)dai->hw)->dai_put_ctrl_blk(dai->ctrl_blk);
+       dai->hw = dai->ctrl_blk = NULL;
+       daio_rsc_uninit(&dai->daio);
+       return 0;
+}
+
+static int daio_mgr_get_rsc(struct rsc_mgr *mgr, enum DAIOTYP type)
+{
+       if (((union daio_usage *)mgr->rscs)->data & (0x1 << type))
+               return -ENOENT;
+
+       ((union daio_usage *)mgr->rscs)->data |= (0x1 << type);
+
+       return 0;
+}
+
+static int daio_mgr_put_rsc(struct rsc_mgr *mgr, enum DAIOTYP type)
+{
+       ((union daio_usage *)mgr->rscs)->data &= ~(0x1 << type);
+
+       return 0;
+}
+
+static int get_daio_rsc(struct daio_mgr *mgr,
+                       const struct daio_desc *desc,
+                       struct daio **rdaio)
+{
+       int err;
+       struct dai *dai = NULL;
+       struct dao *dao = NULL;
+       unsigned long flags;
+
+       *rdaio = NULL;
+
+       /* Check whether there are sufficient daio resources to meet request. */
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       err = daio_mgr_get_rsc(&mgr->mgr, desc->type);
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       if (err) {
+               printk(KERN_ERR "Can't meet DAIO resource request!\n");
+               return err;
+       }
+
+       /* Allocate mem for daio resource */
+       if (desc->type <= DAIO_OUT_MAX) {
+               dao = kzalloc(sizeof(*dao), GFP_KERNEL);
+               if (NULL == dao) {
+                       err = -ENOMEM;
+                       goto error;
+               }
+               err = dao_rsc_init(dao, desc, mgr);
+               if (err)
+                       goto error;
+
+               *rdaio = &dao->daio;
+       } else {
+               dai = kzalloc(sizeof(*dai), GFP_KERNEL);
+               if (NULL == dai) {
+                       err = -ENOMEM;
+                       goto error;
+               }
+               err = dai_rsc_init(dai, desc, mgr);
+               if (err)
+                       goto error;
+
+               *rdaio = &dai->daio;
+       }
+
+       mgr->daio_enable(mgr, *rdaio);
+       mgr->commit_write(mgr);
+
+       return 0;
+
+error:
+       if (NULL != dao)
+               kfree(dao);
+       else if (NULL != dai)
+               kfree(dai);
+
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       daio_mgr_put_rsc(&mgr->mgr, desc->type);
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       return err;
+}
+
+static int put_daio_rsc(struct daio_mgr *mgr, struct daio *daio)
+{
+       unsigned long flags;
+
+       mgr->daio_disable(mgr, daio);
+       mgr->commit_write(mgr);
+
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       daio_mgr_put_rsc(&mgr->mgr, daio->type);
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+
+       if (daio->type <= DAIO_OUT_MAX) {
+               dao_rsc_uninit(container_of(daio, struct dao, daio));
+               kfree(container_of(daio, struct dao, daio));
+       } else {
+               dai_rsc_uninit(container_of(daio, struct dai, daio));
+               kfree(container_of(daio, struct dai, daio));
+       }
+
+       return 0;
+}
+
+static int daio_mgr_enb_daio(struct daio_mgr *mgr, struct daio *daio)
+{
+       struct hw *hw = mgr->mgr.hw;
+
+       if (DAIO_OUT_MAX >= daio->type) {
+               hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk,
+                               daio_device_index(daio->type, hw));
+       } else {
+               hw->daio_mgr_enb_dai(mgr->mgr.ctrl_blk,
+                               daio_device_index(daio->type, hw));
+       }
+       return 0;
+}
+
+static int daio_mgr_dsb_daio(struct daio_mgr *mgr, struct daio *daio)
+{
+       struct hw *hw = mgr->mgr.hw;
+
+       if (DAIO_OUT_MAX >= daio->type) {
+               hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk,
+                               daio_device_index(daio->type, hw));
+       } else {
+               hw->daio_mgr_dsb_dai(mgr->mgr.ctrl_blk,
+                               daio_device_index(daio->type, hw));
+       }
+       return 0;
+}
+
+static int daio_map_op(void *data, struct imapper *entry)
+{
+       struct rsc_mgr *mgr = &((struct daio_mgr *)data)->mgr;
+       struct hw *hw = mgr->hw;
+
+       hw->daio_mgr_set_imaparc(mgr->ctrl_blk, entry->slot);
+       hw->daio_mgr_set_imapnxt(mgr->ctrl_blk, entry->next);
+       hw->daio_mgr_set_imapaddr(mgr->ctrl_blk, entry->addr);
+       hw->daio_mgr_commit_write(mgr->hw, mgr->ctrl_blk);
+
+       return 0;
+}
+
+static int daio_imap_add(struct daio_mgr *mgr, struct imapper *entry)
+{
+       unsigned long flags;
+       int err;
+
+       spin_lock_irqsave(&mgr->imap_lock, flags);
+       if ((0 == entry->addr) && (mgr->init_imap_added)) {
+               input_mapper_delete(&mgr->imappers, mgr->init_imap,
+                                                       daio_map_op, mgr);
+               mgr->init_imap_added = 0;
+       }
+       err = input_mapper_add(&mgr->imappers, entry, daio_map_op, mgr);
+       spin_unlock_irqrestore(&mgr->imap_lock, flags);
+
+       return err;
+}
+
+static int daio_imap_delete(struct daio_mgr *mgr, struct imapper *entry)
+{
+       unsigned long flags;
+       int err;
+
+       spin_lock_irqsave(&mgr->imap_lock, flags);
+       err = input_mapper_delete(&mgr->imappers, entry, daio_map_op, mgr);
+       if (list_empty(&mgr->imappers)) {
+               input_mapper_add(&mgr->imappers, mgr->init_imap,
+                                                       daio_map_op, mgr);
+               mgr->init_imap_added = 1;
+       }
+       spin_unlock_irqrestore(&mgr->imap_lock, flags);
+
+       return err;
+}
+
+static int daio_mgr_commit_write(struct daio_mgr *mgr)
+{
+       struct hw *hw = mgr->mgr.hw;
+
+       hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
+       return 0;
+}
+
+int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
+{
+       int err, i;
+       struct daio_mgr *daio_mgr;
+       struct imapper *entry;
+
+       *rdaio_mgr = NULL;
+       daio_mgr = kzalloc(sizeof(*daio_mgr), GFP_KERNEL);
+       if (NULL == daio_mgr)
+               return -ENOMEM;
+
+       err = rsc_mgr_init(&daio_mgr->mgr, DAIO, DAIO_RESOURCE_NUM, hw);
+       if (err)
+               goto error1;
+
+       spin_lock_init(&daio_mgr->mgr_lock);
+       spin_lock_init(&daio_mgr->imap_lock);
+       INIT_LIST_HEAD(&daio_mgr->imappers);
+       entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+       if (NULL == entry) {
+               err = -ENOMEM;
+               goto error2;
+       }
+       entry->slot = entry->addr = entry->next = entry->user = 0;
+       list_add(&entry->list, &daio_mgr->imappers);
+       daio_mgr->init_imap = entry;
+       daio_mgr->init_imap_added = 1;
+
+       daio_mgr->get_daio = get_daio_rsc;
+       daio_mgr->put_daio = put_daio_rsc;
+       daio_mgr->daio_enable = daio_mgr_enb_daio;
+       daio_mgr->daio_disable = daio_mgr_dsb_daio;
+       daio_mgr->imap_add = daio_imap_add;
+       daio_mgr->imap_delete = daio_imap_delete;
+       daio_mgr->commit_write = daio_mgr_commit_write;
+
+       for (i = 0; i < 8; i++) {
+               ((struct hw *)hw)->daio_mgr_dsb_dao(daio_mgr->mgr.ctrl_blk, i);
+               ((struct hw *)hw)->daio_mgr_dsb_dai(daio_mgr->mgr.ctrl_blk, i);
+       }
+       ((struct hw *)hw)->daio_mgr_commit_write(hw, daio_mgr->mgr.ctrl_blk);
+
+       *rdaio_mgr = daio_mgr;
+
+       return 0;
+
+error2:
+       rsc_mgr_uninit(&daio_mgr->mgr);
+error1:
+       kfree(daio_mgr);
+       return err;
+}
+
+int daio_mgr_destroy(struct daio_mgr *daio_mgr)
+{
+       unsigned long flags;
+
+       /* free daio input mapper list */
+       spin_lock_irqsave(&daio_mgr->imap_lock, flags);
+       free_input_mapper_list(&daio_mgr->imappers);
+       spin_unlock_irqrestore(&daio_mgr->imap_lock, flags);
+
+       rsc_mgr_uninit(&daio_mgr->mgr);
+       kfree(daio_mgr);
+
+       return 0;
+}
+
diff --git a/sound/pci/ctxfi/ctdaio.h b/sound/pci/ctxfi/ctdaio.h
new file mode 100644 (file)
index 0000000..0f52ce5
--- /dev/null
@@ -0,0 +1,122 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctdaio.h
+ *
+ * @Brief
+ * This file contains the definition of Digital Audio Input Output
+ * resource management object.
+ *
+ * @Author     Liu Chun
+ * @Date       May 23 2008
+ *
+ */
+
+#ifndef CTDAIO_H
+#define CTDAIO_H
+
+#include "ctresource.h"
+#include "ctimap.h"
+#include <linux/spinlock.h>
+#include <linux/list.h>
+
+/* Define the descriptor of a daio resource */
+enum DAIOTYP {
+       LINEO1,
+       LINEO2,
+       LINEO3,
+       LINEO4,
+       SPDIFOO,        /* S/PDIF Out (Flexijack/Optical) */
+       LINEIM,
+       SPDIFIO,        /* S/PDIF In (Flexijack/Optical) on the card */
+       SPDIFI1,        /* S/PDIF In on internal Drive Bay */
+       NUM_DAIOTYP
+};
+
+struct dao_rsc_ops;
+struct dai_rsc_ops;
+struct daio_mgr;
+
+struct daio {
+       struct rsc rscl;        /* Basic resource info for left TX/RX */
+       struct rsc rscr;        /* Basic resource info for right TX/RX */
+       enum DAIOTYP type;
+};
+
+struct dao {
+       struct daio daio;
+       struct dao_rsc_ops *ops;        /* DAO specific operations */
+       struct imapper **imappers;
+       struct daio_mgr *mgr;
+       void *hw;
+       void *ctrl_blk;
+};
+
+struct dai {
+       struct daio daio;
+       struct dai_rsc_ops *ops;        /* DAI specific operations */
+       void *hw;
+       void *ctrl_blk;
+};
+
+struct dao_desc {
+       unsigned int msr:4;
+       unsigned int passthru:1;
+};
+
+struct dao_rsc_ops {
+       int (*set_spos)(struct dao *dao, unsigned int spos);
+       int (*commit_write)(struct dao *dao);
+       int (*get_spos)(struct dao *dao, unsigned int *spos);
+       int (*reinit)(struct dao *dao, const struct dao_desc *desc);
+       int (*set_left_input)(struct dao *dao, struct rsc *input);
+       int (*set_right_input)(struct dao *dao, struct rsc *input);
+       int (*clear_left_input)(struct dao *dao);
+       int (*clear_right_input)(struct dao *dao);
+};
+
+struct dai_rsc_ops {
+       int (*set_srt_srcl)(struct dai *dai, struct rsc *src);
+       int (*set_srt_srcr)(struct dai *dai, struct rsc *src);
+       int (*set_srt_msr)(struct dai *dai, unsigned int msr);
+       int (*set_enb_src)(struct dai *dai, unsigned int enb);
+       int (*set_enb_srt)(struct dai *dai, unsigned int enb);
+       int (*commit_write)(struct dai *dai);
+};
+
+/* Define daio resource request description info */
+struct daio_desc {
+       unsigned int type:4;
+       unsigned int msr:4;
+       unsigned int passthru:1;
+};
+
+struct daio_mgr {
+       struct rsc_mgr mgr;     /* Basic resource manager info */
+       spinlock_t mgr_lock;
+       spinlock_t imap_lock;
+       struct list_head imappers;
+       struct imapper *init_imap;
+       unsigned int init_imap_added;
+
+        /* request one daio resource */
+       int (*get_daio)(struct daio_mgr *mgr,
+                       const struct daio_desc *desc, struct daio **rdaio);
+       /* return one daio resource */
+       int (*put_daio)(struct daio_mgr *mgr, struct daio *daio);
+       int (*daio_enable)(struct daio_mgr *mgr, struct daio *daio);
+       int (*daio_disable)(struct daio_mgr *mgr, struct daio *daio);
+       int (*imap_add)(struct daio_mgr *mgr, struct imapper *entry);
+       int (*imap_delete)(struct daio_mgr *mgr, struct imapper *entry);
+       int (*commit_write)(struct daio_mgr *mgr);
+};
+
+/* Constructor and destructor of daio resource manager */
+int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr);
+int daio_mgr_destroy(struct daio_mgr *daio_mgr);
+
+#endif /* CTDAIO_H */
diff --git a/sound/pci/ctxfi/cthardware.c b/sound/pci/ctxfi/cthardware.c
new file mode 100644 (file)
index 0000000..8e64f48
--- /dev/null
@@ -0,0 +1,91 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       cthardware.c
+ *
+ * @Brief
+ * This file contains the implementation of hardware access methord.
+ *
+ * @Author     Liu Chun
+ * @Date       Jun 26 2008
+ *
+ */
+
+#include "cthardware.h"
+#include "cthw20k1.h"
+#include "cthw20k2.h"
+#include <linux/bug.h>
+
+int __devinit create_hw_obj(struct pci_dev *pci, enum CHIPTYP chip_type,
+                           enum CTCARDS model, struct hw **rhw)
+{
+       int err;
+
+       switch (chip_type) {
+       case ATC20K1:
+               err = create_20k1_hw_obj(rhw);
+               break;
+       case ATC20K2:
+               err = create_20k2_hw_obj(rhw);
+               break;
+       default:
+               err = -ENODEV;
+               break;
+       }
+       if (err)
+               return err;
+
+       (*rhw)->pci = pci;
+       (*rhw)->chip_type = chip_type;
+       (*rhw)->model = model;
+
+       return 0;
+}
+
+int destroy_hw_obj(struct hw *hw)
+{
+       int err;
+
+       switch (hw->pci->device) {
+       case 0x0005:    /* 20k1 device */
+               err = destroy_20k1_hw_obj(hw);
+               break;
+       case 0x000B:    /* 20k2 device */
+               err = destroy_20k2_hw_obj(hw);
+               break;
+       default:
+               err = -ENODEV;
+               break;
+       }
+
+       return err;
+}
+
+unsigned int get_field(unsigned int data, unsigned int field)
+{
+       int i;
+
+       BUG_ON(!field);
+       /* @field should always be greater than 0 */
+       for (i = 0; !(field & (1 << i)); )
+               i++;
+
+       return (data & field) >> i;
+}
+
+void set_field(unsigned int *data, unsigned int field, unsigned int value)
+{
+       int i;
+
+       BUG_ON(!field);
+       /* @field should always be greater than 0 */
+       for (i = 0; !(field & (1 << i)); )
+               i++;
+
+       *data = (*data & (~field)) | ((value << i) & field);
+}
+
diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h
new file mode 100644 (file)
index 0000000..4a8e04f
--- /dev/null
@@ -0,0 +1,196 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       cthardware.h
+ *
+ * @Brief
+ * This file contains the definition of hardware access methord.
+ *
+ * @Author     Liu Chun
+ * @Date       May 13 2008
+ *
+ */
+
+#ifndef CTHARDWARE_H
+#define CTHARDWARE_H
+
+#include <linux/types.h>
+#include <linux/pci.h>
+
+enum CHIPTYP {
+       ATC20K1,
+       ATC20K2,
+       ATCNONE
+};
+
+enum CTCARDS {
+       /* 20k1 models */
+       CTSB055X,
+       CTSB073X,
+       CTUAA,
+       CT20K1_UNKNOWN,
+       /* 20k2 models */
+       CTSB0760,
+       CTHENDRIX,
+       CTSB0880,
+       NUM_CTCARDS             /* This should always be the last */
+};
+
+/* Type of input source for ADC */
+enum ADCSRC{
+       ADC_MICIN,
+       ADC_LINEIN,
+       ADC_VIDEO,
+       ADC_AUX,
+       ADC_NONE        /* Switch to digital input */
+};
+
+struct card_conf {
+       /* device virtual mem page table page physical addr
+        * (supporting one page table page now) */
+       unsigned long vm_pgt_phys;
+       unsigned int rsr;       /* reference sample rate in Hzs*/
+       unsigned int msr;       /* master sample rate in rsrs */
+};
+
+struct hw {
+       int (*card_init)(struct hw *hw, struct card_conf *info);
+       int (*card_stop)(struct hw *hw);
+       int (*pll_init)(struct hw *hw, unsigned int rsr);
+       int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source);
+       int (*select_adc_source)(struct hw *hw, enum ADCSRC source);
+       int (*have_digit_io_switch)(struct hw *hw);
+
+       /* SRC operations */
+       int (*src_rsc_get_ctrl_blk)(void **rblk);
+       int (*src_rsc_put_ctrl_blk)(void *blk);
+       int (*src_set_state)(void *blk, unsigned int state);
+       int (*src_set_bm)(void *blk, unsigned int bm);
+       int (*src_set_rsr)(void *blk, unsigned int rsr);
+       int (*src_set_sf)(void *blk, unsigned int sf);
+       int (*src_set_wr)(void *blk, unsigned int wr);
+       int (*src_set_pm)(void *blk, unsigned int pm);
+       int (*src_set_rom)(void *blk, unsigned int rom);
+       int (*src_set_vo)(void *blk, unsigned int vo);
+       int (*src_set_st)(void *blk, unsigned int st);
+       int (*src_set_ie)(void *blk, unsigned int ie);
+       int (*src_set_ilsz)(void *blk, unsigned int ilsz);
+       int (*src_set_bp)(void *blk, unsigned int bp);
+       int (*src_set_cisz)(void *blk, unsigned int cisz);
+       int (*src_set_ca)(void *blk, unsigned int ca);
+       int (*src_set_sa)(void *blk, unsigned int sa);
+       int (*src_set_la)(void *blk, unsigned int la);
+       int (*src_set_pitch)(void *blk, unsigned int pitch);
+       int (*src_set_clear_zbufs)(void *blk, unsigned int clear);
+       int (*src_set_dirty)(void *blk, unsigned int flags);
+       int (*src_set_dirty_all)(void *blk);
+       int (*src_commit_write)(struct hw *hw, unsigned int idx, void *blk);
+       int (*src_get_ca)(struct hw *hw, unsigned int idx, void *blk);
+       unsigned int (*src_get_dirty)(void *blk);
+       unsigned int (*src_dirty_conj_mask)(void);
+       int (*src_mgr_get_ctrl_blk)(void **rblk);
+       int (*src_mgr_put_ctrl_blk)(void *blk);
+       /* syncly enable src @idx */
+       int (*src_mgr_enbs_src)(void *blk, unsigned int idx);
+       /* enable src @idx */
+       int (*src_mgr_enb_src)(void *blk, unsigned int idx);
+       /* disable src @idx */
+       int (*src_mgr_dsb_src)(void *blk, unsigned int idx);
+       int (*src_mgr_commit_write)(struct hw *hw, void *blk);
+
+       /* SRC Input Mapper operations */
+       int (*srcimp_mgr_get_ctrl_blk)(void **rblk);
+       int (*srcimp_mgr_put_ctrl_blk)(void *blk);
+       int (*srcimp_mgr_set_imaparc)(void *blk, unsigned int slot);
+       int (*srcimp_mgr_set_imapuser)(void *blk, unsigned int user);
+       int (*srcimp_mgr_set_imapnxt)(void *blk, unsigned int next);
+       int (*srcimp_mgr_set_imapaddr)(void *blk, unsigned int addr);
+       int (*srcimp_mgr_commit_write)(struct hw *hw, void *blk);
+
+       /* AMIXER operations */
+       int (*amixer_rsc_get_ctrl_blk)(void **rblk);
+       int (*amixer_rsc_put_ctrl_blk)(void *blk);
+       int (*amixer_mgr_get_ctrl_blk)(void **rblk);
+       int (*amixer_mgr_put_ctrl_blk)(void *blk);
+       int (*amixer_set_mode)(void *blk, unsigned int mode);
+       int (*amixer_set_iv)(void *blk, unsigned int iv);
+       int (*amixer_set_x)(void *blk, unsigned int x);
+       int (*amixer_set_y)(void *blk, unsigned int y);
+       int (*amixer_set_sadr)(void *blk, unsigned int sadr);
+       int (*amixer_set_se)(void *blk, unsigned int se);
+       int (*amixer_set_dirty)(void *blk, unsigned int flags);
+       int (*amixer_set_dirty_all)(void *blk);
+       int (*amixer_commit_write)(struct hw *hw, unsigned int idx, void *blk);
+       int (*amixer_get_y)(void *blk);
+       unsigned int (*amixer_get_dirty)(void *blk);
+
+       /* DAIO operations */
+       int (*dai_get_ctrl_blk)(void **rblk);
+       int (*dai_put_ctrl_blk)(void *blk);
+       int (*dai_srt_set_srco)(void *blk, unsigned int src);
+       int (*dai_srt_set_srcm)(void *blk, unsigned int src);
+       int (*dai_srt_set_rsr)(void *blk, unsigned int rsr);
+       int (*dai_srt_set_drat)(void *blk, unsigned int drat);
+       int (*dai_srt_set_ec)(void *blk, unsigned int ec);
+       int (*dai_srt_set_et)(void *blk, unsigned int et);
+       int (*dai_commit_write)(struct hw *hw, unsigned int idx, void *blk);
+       int (*dao_get_ctrl_blk)(void **rblk);
+       int (*dao_put_ctrl_blk)(void *blk);
+       int (*dao_set_spos)(void *blk, unsigned int spos);
+       int (*dao_commit_write)(struct hw *hw, unsigned int idx, void *blk);
+       int (*dao_get_spos)(void *blk, unsigned int *spos);
+
+       int (*daio_mgr_get_ctrl_blk)(struct hw *hw, void **rblk);
+       int (*daio_mgr_put_ctrl_blk)(void *blk);
+       int (*daio_mgr_enb_dai)(void *blk, unsigned int idx);
+       int (*daio_mgr_dsb_dai)(void *blk, unsigned int idx);
+       int (*daio_mgr_enb_dao)(void *blk, unsigned int idx);
+       int (*daio_mgr_dsb_dao)(void *blk, unsigned int idx);
+       int (*daio_mgr_dao_init)(void *blk, unsigned int idx,
+                                               unsigned int conf);
+       int (*daio_mgr_set_imaparc)(void *blk, unsigned int slot);
+       int (*daio_mgr_set_imapnxt)(void *blk, unsigned int next);
+       int (*daio_mgr_set_imapaddr)(void *blk, unsigned int addr);
+       int (*daio_mgr_commit_write)(struct hw *hw, void *blk);
+
+       int (*set_timer_irq)(struct hw *hw, int enable);
+       int (*set_timer_tick)(struct hw *hw, unsigned int tick);
+       unsigned int (*get_wc)(struct hw *hw);
+
+       void (*irq_callback)(void *data, unsigned int bit);
+       void *irq_callback_data;
+
+       struct pci_dev *pci;    /* the pci kernel structure of this card */
+       int irq;
+       unsigned long io_base;
+       unsigned long mem_base;
+
+       enum CHIPTYP chip_type;
+       enum CTCARDS model;
+};
+
+int create_hw_obj(struct pci_dev *pci, enum CHIPTYP chip_type,
+                 enum CTCARDS model, struct hw **rhw);
+int destroy_hw_obj(struct hw *hw);
+
+unsigned int get_field(unsigned int data, unsigned int field);
+void set_field(unsigned int *data, unsigned int field, unsigned int value);
+
+/* IRQ bits */
+#define        PLL_INT         (1 << 10) /* PLL input-clock out-of-range */
+#define FI_INT         (1 << 9)  /* forced interrupt */
+#define IT_INT         (1 << 8)  /* timer interrupt */
+#define PCI_INT                (1 << 7)  /* PCI bus error pending */
+#define URT_INT                (1 << 6)  /* UART Tx/Rx */
+#define GPI_INT                (1 << 5)  /* GPI pin */
+#define MIX_INT                (1 << 4)  /* mixer parameter segment FIFO channels */
+#define DAI_INT                (1 << 3)  /* DAI (SR-tracker or SPDIF-receiver) */
+#define TP_INT         (1 << 2)  /* transport priority queue */
+#define DSP_INT                (1 << 1)  /* DSP */
+#define SRC_INT                (1 << 0)  /* SRC channels */
+
+#endif /* CTHARDWARE_H */
diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c
new file mode 100644 (file)
index 0000000..cb69d9d
--- /dev/null
@@ -0,0 +1,2248 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       cthw20k1.c
+ *
+ * @Brief
+ * This file contains the implementation of hardware access methord for 20k1.
+ *
+ * @Author     Liu Chun
+ * @Date       Jun 24 2008
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include "cthw20k1.h"
+#include "ct20k1reg.h"
+
+#if BITS_PER_LONG == 32
+#define CT_XFI_DMA_MASK                DMA_BIT_MASK(32) /* 32 bit PTE */
+#else
+#define CT_XFI_DMA_MASK                DMA_BIT_MASK(64) /* 64 bit PTE */
+#endif
+
+struct hw20k1 {
+       struct hw hw;
+       spinlock_t reg_20k1_lock;
+       spinlock_t reg_pci_lock;
+};
+
+static u32 hw_read_20kx(struct hw *hw, u32 reg);
+static void hw_write_20kx(struct hw *hw, u32 reg, u32 data);
+static u32 hw_read_pci(struct hw *hw, u32 reg);
+static void hw_write_pci(struct hw *hw, u32 reg, u32 data);
+
+/*
+ * Type definition block.
+ * The layout of control structures can be directly applied on 20k2 chip.
+ */
+
+/*
+ * SRC control block definitions.
+ */
+
+/* SRC resource control block */
+#define SRCCTL_STATE   0x00000007
+#define SRCCTL_BM      0x00000008
+#define SRCCTL_RSR     0x00000030
+#define SRCCTL_SF      0x000001C0
+#define SRCCTL_WR      0x00000200
+#define SRCCTL_PM      0x00000400
+#define SRCCTL_ROM     0x00001800
+#define SRCCTL_VO      0x00002000
+#define SRCCTL_ST      0x00004000
+#define SRCCTL_IE      0x00008000
+#define SRCCTL_ILSZ    0x000F0000
+#define SRCCTL_BP      0x00100000
+
+#define SRCCCR_CISZ    0x000007FF
+#define SRCCCR_CWA     0x001FF800
+#define SRCCCR_D       0x00200000
+#define SRCCCR_RS      0x01C00000
+#define SRCCCR_NAL     0x3E000000
+#define SRCCCR_RA      0xC0000000
+
+#define SRCCA_CA       0x03FFFFFF
+#define SRCCA_RS       0x1C000000
+#define SRCCA_NAL      0xE0000000
+
+#define SRCSA_SA       0x03FFFFFF
+
+#define SRCLA_LA       0x03FFFFFF
+
+/* Mixer Parameter Ring ram Low and Hight register.
+ * Fixed-point value in 8.24 format for parameter channel */
+#define MPRLH_PITCH    0xFFFFFFFF
+
+/* SRC resource register dirty flags */
+union src_dirty {
+       struct {
+               u16 ctl:1;
+               u16 ccr:1;
+               u16 sa:1;
+               u16 la:1;
+               u16 ca:1;
+               u16 mpr:1;
+               u16 czbfs:1;    /* Clear Z-Buffers */
+               u16 rsv:9;
+       } bf;
+       u16 data;
+};
+
+struct src_rsc_ctrl_blk {
+       unsigned int    ctl;
+       unsigned int    ccr;
+       unsigned int    ca;
+       unsigned int    sa;
+       unsigned int    la;
+       unsigned int    mpr;
+       union src_dirty dirty;
+};
+
+/* SRC manager control block */
+union src_mgr_dirty {
+       struct {
+               u16 enb0:1;
+               u16 enb1:1;
+               u16 enb2:1;
+               u16 enb3:1;
+               u16 enb4:1;
+               u16 enb5:1;
+               u16 enb6:1;
+               u16 enb7:1;
+               u16 enbsa:1;
+               u16 rsv:7;
+       } bf;
+       u16 data;
+};
+
+struct src_mgr_ctrl_blk {
+       unsigned int            enbsa;
+       unsigned int            enb[8];
+       union src_mgr_dirty     dirty;
+};
+
+/* SRCIMP manager control block */
+#define SRCAIM_ARC     0x00000FFF
+#define SRCAIM_NXT     0x00FF0000
+#define SRCAIM_SRC     0xFF000000
+
+struct srcimap {
+       unsigned int srcaim;
+       unsigned int idx;
+};
+
+/* SRCIMP manager register dirty flags */
+union srcimp_mgr_dirty {
+       struct {
+               u16 srcimap:1;
+               u16 rsv:15;
+       } bf;
+       u16 data;
+};
+
+struct srcimp_mgr_ctrl_blk {
+       struct srcimap          srcimap;
+       union srcimp_mgr_dirty  dirty;
+};
+
+/*
+ * Function implementation block.
+ */
+
+static int src_get_rsc_ctrl_blk(void **rblk)
+{
+       struct src_rsc_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int src_put_rsc_ctrl_blk(void *blk)
+{
+       kfree((struct src_rsc_ctrl_blk *)blk);
+
+       return 0;
+}
+
+static int src_set_state(void *blk, unsigned int state)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_STATE, state);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_bm(void *blk, unsigned int bm)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_BM, bm);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_rsr(void *blk, unsigned int rsr)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_RSR, rsr);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_sf(void *blk, unsigned int sf)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_SF, sf);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_wr(void *blk, unsigned int wr)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_WR, wr);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_pm(void *blk, unsigned int pm)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_PM, pm);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_rom(void *blk, unsigned int rom)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_ROM, rom);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_vo(void *blk, unsigned int vo)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_VO, vo);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_st(void *blk, unsigned int st)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_ST, st);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_ie(void *blk, unsigned int ie)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_IE, ie);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_ilsz(void *blk, unsigned int ilsz)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_ILSZ, ilsz);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_bp(void *blk, unsigned int bp)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_BP, bp);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_cisz(void *blk, unsigned int cisz)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ccr, SRCCCR_CISZ, cisz);
+       ctl->dirty.bf.ccr = 1;
+       return 0;
+}
+
+static int src_set_ca(void *blk, unsigned int ca)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ca, SRCCA_CA, ca);
+       ctl->dirty.bf.ca = 1;
+       return 0;
+}
+
+static int src_set_sa(void *blk, unsigned int sa)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->sa, SRCSA_SA, sa);
+       ctl->dirty.bf.sa = 1;
+       return 0;
+}
+
+static int src_set_la(void *blk, unsigned int la)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->la, SRCLA_LA, la);
+       ctl->dirty.bf.la = 1;
+       return 0;
+}
+
+static int src_set_pitch(void *blk, unsigned int pitch)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->mpr, MPRLH_PITCH, pitch);
+       ctl->dirty.bf.mpr = 1;
+       return 0;
+}
+
+static int src_set_clear_zbufs(void *blk, unsigned int clear)
+{
+       ((struct src_rsc_ctrl_blk *)blk)->dirty.bf.czbfs = (clear ? 1 : 0);
+       return 0;
+}
+
+static int src_set_dirty(void *blk, unsigned int flags)
+{
+       ((struct src_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff);
+       return 0;
+}
+
+static int src_set_dirty_all(void *blk)
+{
+       ((struct src_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0);
+       return 0;
+}
+
+#define AR_SLOT_SIZE           4096
+#define AR_SLOT_BLOCK_SIZE     16
+#define AR_PTS_PITCH           6
+#define AR_PARAM_SRC_OFFSET    0x60
+
+static unsigned int src_param_pitch_mixer(unsigned int src_idx)
+{
+       return ((src_idx << 4) + AR_PTS_PITCH + AR_SLOT_SIZE
+                       - AR_PARAM_SRC_OFFSET) % AR_SLOT_SIZE;
+
+}
+
+static int src_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+       int i;
+
+       if (ctl->dirty.bf.czbfs) {
+               /* Clear Z-Buffer registers */
+               for (i = 0; i < 8; i++)
+                       hw_write_20kx(hw, SRCUPZ+idx*0x100+i*0x4, 0);
+
+               for (i = 0; i < 4; i++)
+                       hw_write_20kx(hw, SRCDN0Z+idx*0x100+i*0x4, 0);
+
+               for (i = 0; i < 8; i++)
+                       hw_write_20kx(hw, SRCDN1Z+idx*0x100+i*0x4, 0);
+
+               ctl->dirty.bf.czbfs = 0;
+       }
+       if (ctl->dirty.bf.mpr) {
+               /* Take the parameter mixer resource in the same group as that
+                * the idx src is in for simplicity. Unlike src, all conjugate
+                * parameter mixer resources must be programmed for
+                * corresponding conjugate src resources. */
+               unsigned int pm_idx = src_param_pitch_mixer(idx);
+               hw_write_20kx(hw, PRING_LO_HI+4*pm_idx, ctl->mpr);
+               hw_write_20kx(hw, PMOPLO+8*pm_idx, 0x3);
+               hw_write_20kx(hw, PMOPHI+8*pm_idx, 0x0);
+               ctl->dirty.bf.mpr = 0;
+       }
+       if (ctl->dirty.bf.sa) {
+               hw_write_20kx(hw, SRCSA+idx*0x100, ctl->sa);
+               ctl->dirty.bf.sa = 0;
+       }
+       if (ctl->dirty.bf.la) {
+               hw_write_20kx(hw, SRCLA+idx*0x100, ctl->la);
+               ctl->dirty.bf.la = 0;
+       }
+       if (ctl->dirty.bf.ca) {
+               hw_write_20kx(hw, SRCCA+idx*0x100, ctl->ca);
+               ctl->dirty.bf.ca = 0;
+       }
+
+       /* Write srccf register */
+       hw_write_20kx(hw, SRCCF+idx*0x100, 0x0);
+
+       if (ctl->dirty.bf.ccr) {
+               hw_write_20kx(hw, SRCCCR+idx*0x100, ctl->ccr);
+               ctl->dirty.bf.ccr = 0;
+       }
+       if (ctl->dirty.bf.ctl) {
+               hw_write_20kx(hw, SRCCTL+idx*0x100, ctl->ctl);
+               ctl->dirty.bf.ctl = 0;
+       }
+
+       return 0;
+}
+
+static int src_get_ca(struct hw *hw, unsigned int idx, void *blk)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       ctl->ca = hw_read_20kx(hw, SRCCA+idx*0x100);
+       ctl->dirty.bf.ca = 0;
+
+       return get_field(ctl->ca, SRCCA_CA);
+}
+
+static unsigned int src_get_dirty(void *blk)
+{
+       return ((struct src_rsc_ctrl_blk *)blk)->dirty.data;
+}
+
+static unsigned int src_dirty_conj_mask(void)
+{
+       return 0x20;
+}
+
+static int src_mgr_enbs_src(void *blk, unsigned int idx)
+{
+       ((struct src_mgr_ctrl_blk *)blk)->enbsa = ~(0x0);
+       ((struct src_mgr_ctrl_blk *)blk)->dirty.bf.enbsa = 1;
+       ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32));
+       return 0;
+}
+
+static int src_mgr_enb_src(void *blk, unsigned int idx)
+{
+       ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32));
+       ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32));
+       return 0;
+}
+
+static int src_mgr_dsb_src(void *blk, unsigned int idx)
+{
+       ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] &= ~(0x1 << (idx%32));
+       ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32));
+       return 0;
+}
+
+static int src_mgr_commit_write(struct hw *hw, void *blk)
+{
+       struct src_mgr_ctrl_blk *ctl = blk;
+       int i;
+       unsigned int ret;
+
+       if (ctl->dirty.bf.enbsa) {
+               do {
+                       ret = hw_read_20kx(hw, SRCENBSTAT);
+               } while (ret & 0x1);
+               hw_write_20kx(hw, SRCENBS, ctl->enbsa);
+               ctl->dirty.bf.enbsa = 0;
+       }
+       for (i = 0; i < 8; i++) {
+               if ((ctl->dirty.data & (0x1 << i))) {
+                       hw_write_20kx(hw, SRCENB+(i*0x100), ctl->enb[i]);
+                       ctl->dirty.data &= ~(0x1 << i);
+               }
+       }
+
+       return 0;
+}
+
+static int src_mgr_get_ctrl_blk(void **rblk)
+{
+       struct src_mgr_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int src_mgr_put_ctrl_blk(void *blk)
+{
+       kfree((struct src_mgr_ctrl_blk *)blk);
+
+       return 0;
+}
+
+static int srcimp_mgr_get_ctrl_blk(void **rblk)
+{
+       struct srcimp_mgr_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int srcimp_mgr_put_ctrl_blk(void *blk)
+{
+       kfree((struct srcimp_mgr_ctrl_blk *)blk);
+
+       return 0;
+}
+
+static int srcimp_mgr_set_imaparc(void *blk, unsigned int slot)
+{
+       struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srcimap.srcaim, SRCAIM_ARC, slot);
+       ctl->dirty.bf.srcimap = 1;
+       return 0;
+}
+
+static int srcimp_mgr_set_imapuser(void *blk, unsigned int user)
+{
+       struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srcimap.srcaim, SRCAIM_SRC, user);
+       ctl->dirty.bf.srcimap = 1;
+       return 0;
+}
+
+static int srcimp_mgr_set_imapnxt(void *blk, unsigned int next)
+{
+       struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srcimap.srcaim, SRCAIM_NXT, next);
+       ctl->dirty.bf.srcimap = 1;
+       return 0;
+}
+
+static int srcimp_mgr_set_imapaddr(void *blk, unsigned int addr)
+{
+       struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+       ctl->srcimap.idx = addr;
+       ctl->dirty.bf.srcimap = 1;
+       return 0;
+}
+
+static int srcimp_mgr_commit_write(struct hw *hw, void *blk)
+{
+       struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+       if (ctl->dirty.bf.srcimap) {
+               hw_write_20kx(hw, SRCIMAP+ctl->srcimap.idx*0x100,
+                                               ctl->srcimap.srcaim);
+               ctl->dirty.bf.srcimap = 0;
+       }
+
+       return 0;
+}
+
+/*
+ * AMIXER control block definitions.
+ */
+
+#define AMOPLO_M       0x00000003
+#define AMOPLO_X       0x0003FFF0
+#define AMOPLO_Y       0xFFFC0000
+
+#define AMOPHI_SADR    0x000000FF
+#define AMOPHI_SE      0x80000000
+
+/* AMIXER resource register dirty flags */
+union amixer_dirty {
+       struct {
+               u16 amoplo:1;
+               u16 amophi:1;
+               u16 rsv:14;
+       } bf;
+       u16 data;
+};
+
+/* AMIXER resource control block */
+struct amixer_rsc_ctrl_blk {
+       unsigned int            amoplo;
+       unsigned int            amophi;
+       union amixer_dirty      dirty;
+};
+
+static int amixer_set_mode(void *blk, unsigned int mode)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amoplo, AMOPLO_M, mode);
+       ctl->dirty.bf.amoplo = 1;
+       return 0;
+}
+
+static int amixer_set_iv(void *blk, unsigned int iv)
+{
+       /* 20k1 amixer does not have this field */
+       return 0;
+}
+
+static int amixer_set_x(void *blk, unsigned int x)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amoplo, AMOPLO_X, x);
+       ctl->dirty.bf.amoplo = 1;
+       return 0;
+}
+
+static int amixer_set_y(void *blk, unsigned int y)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amoplo, AMOPLO_Y, y);
+       ctl->dirty.bf.amoplo = 1;
+       return 0;
+}
+
+static int amixer_set_sadr(void *blk, unsigned int sadr)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amophi, AMOPHI_SADR, sadr);
+       ctl->dirty.bf.amophi = 1;
+       return 0;
+}
+
+static int amixer_set_se(void *blk, unsigned int se)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amophi, AMOPHI_SE, se);
+       ctl->dirty.bf.amophi = 1;
+       return 0;
+}
+
+static int amixer_set_dirty(void *blk, unsigned int flags)
+{
+       ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff);
+       return 0;
+}
+
+static int amixer_set_dirty_all(void *blk)
+{
+       ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0);
+       return 0;
+}
+
+static int amixer_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       if (ctl->dirty.bf.amoplo || ctl->dirty.bf.amophi) {
+               hw_write_20kx(hw, AMOPLO+idx*8, ctl->amoplo);
+               ctl->dirty.bf.amoplo = 0;
+               hw_write_20kx(hw, AMOPHI+idx*8, ctl->amophi);
+               ctl->dirty.bf.amophi = 0;
+       }
+
+       return 0;
+}
+
+static int amixer_get_y(void *blk)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       return get_field(ctl->amoplo, AMOPLO_Y);
+}
+
+static unsigned int amixer_get_dirty(void *blk)
+{
+       return ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data;
+}
+
+static int amixer_rsc_get_ctrl_blk(void **rblk)
+{
+       struct amixer_rsc_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int amixer_rsc_put_ctrl_blk(void *blk)
+{
+       kfree((struct amixer_rsc_ctrl_blk *)blk);
+
+       return 0;
+}
+
+static int amixer_mgr_get_ctrl_blk(void **rblk)
+{
+       /*amixer_mgr_ctrl_blk_t *blk;*/
+
+       *rblk = NULL;
+       /*blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;*/
+
+       return 0;
+}
+
+static int amixer_mgr_put_ctrl_blk(void *blk)
+{
+       /*kfree((amixer_mgr_ctrl_blk_t *)blk);*/
+
+       return 0;
+}
+
+/*
+ * DAIO control block definitions.
+ */
+
+/* Receiver Sample Rate Tracker Control register */
+#define SRTCTL_SRCR    0x000000FF
+#define SRTCTL_SRCL    0x0000FF00
+#define SRTCTL_RSR     0x00030000
+#define SRTCTL_DRAT    0x000C0000
+#define SRTCTL_RLE     0x10000000
+#define SRTCTL_RLP     0x20000000
+#define SRTCTL_EC      0x40000000
+#define SRTCTL_ET      0x80000000
+
+/* DAIO Receiver register dirty flags */
+union dai_dirty {
+       struct {
+               u16 srtctl:1;
+               u16 rsv:15;
+       } bf;
+       u16 data;
+};
+
+/* DAIO Receiver control block */
+struct dai_ctrl_blk {
+       unsigned int    srtctl;
+       union dai_dirty dirty;
+};
+
+/* S/PDIF Transmitter register dirty flags */
+union dao_dirty {
+       struct {
+               u16 spos:1;
+               u16 rsv:15;
+       } bf;
+       u16 data;
+};
+
+/* S/PDIF Transmitter control block */
+struct dao_ctrl_blk {
+       unsigned int    spos; /* S/PDIF Output Channel Status Register */
+       union dao_dirty dirty;
+};
+
+/* Audio Input Mapper RAM */
+#define AIM_ARC                0x00000FFF
+#define AIM_NXT                0x007F0000
+
+struct daoimap {
+       unsigned int aim;
+       unsigned int idx;
+};
+
+/* I2S Transmitter/Receiver Control register */
+#define I2SCTL_EA      0x00000004
+#define I2SCTL_EI      0x00000010
+
+/* S/PDIF Transmitter Control register */
+#define SPOCTL_OE      0x00000001
+#define SPOCTL_OS      0x0000000E
+#define SPOCTL_RIV     0x00000010
+#define SPOCTL_LIV     0x00000020
+#define SPOCTL_SR      0x000000C0
+
+/* S/PDIF Receiver Control register */
+#define SPICTL_EN      0x00000001
+#define SPICTL_I24     0x00000002
+#define SPICTL_IB      0x00000004
+#define SPICTL_SM      0x00000008
+#define SPICTL_VM      0x00000010
+
+/* DAIO manager register dirty flags */
+union daio_mgr_dirty {
+       struct {
+               u32 i2soctl:4;
+               u32 i2sictl:4;
+               u32 spoctl:4;
+               u32 spictl:4;
+               u32 daoimap:1;
+               u32 rsv:15;
+       } bf;
+       u32 data;
+};
+
+/* DAIO manager control block */
+struct daio_mgr_ctrl_blk {
+       unsigned int            i2sctl;
+       unsigned int            spoctl;
+       unsigned int            spictl;
+       struct daoimap          daoimap;
+       union daio_mgr_dirty    dirty;
+};
+
+static int dai_srt_set_srcr(void *blk, unsigned int src)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srtctl, SRTCTL_SRCR, src);
+       ctl->dirty.bf.srtctl = 1;
+       return 0;
+}
+
+static int dai_srt_set_srcl(void *blk, unsigned int src)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srtctl, SRTCTL_SRCL, src);
+       ctl->dirty.bf.srtctl = 1;
+       return 0;
+}
+
+static int dai_srt_set_rsr(void *blk, unsigned int rsr)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srtctl, SRTCTL_RSR, rsr);
+       ctl->dirty.bf.srtctl = 1;
+       return 0;
+}
+
+static int dai_srt_set_drat(void *blk, unsigned int drat)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srtctl, SRTCTL_DRAT, drat);
+       ctl->dirty.bf.srtctl = 1;
+       return 0;
+}
+
+static int dai_srt_set_ec(void *blk, unsigned int ec)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srtctl, SRTCTL_EC, ec ? 1 : 0);
+       ctl->dirty.bf.srtctl = 1;
+       return 0;
+}
+
+static int dai_srt_set_et(void *blk, unsigned int et)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srtctl, SRTCTL_ET, et ? 1 : 0);
+       ctl->dirty.bf.srtctl = 1;
+       return 0;
+}
+
+static int dai_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       if (ctl->dirty.bf.srtctl) {
+               if (idx < 4) {
+                       /* S/PDIF SRTs */
+                       hw_write_20kx(hw, SRTSCTL+0x4*idx, ctl->srtctl);
+               } else {
+                       /* I2S SRT */
+                       hw_write_20kx(hw, SRTICTL, ctl->srtctl);
+               }
+               ctl->dirty.bf.srtctl = 0;
+       }
+
+       return 0;
+}
+
+static int dai_get_ctrl_blk(void **rblk)
+{
+       struct dai_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int dai_put_ctrl_blk(void *blk)
+{
+       kfree((struct dai_ctrl_blk *)blk);
+
+       return 0;
+}
+
+static int dao_set_spos(void *blk, unsigned int spos)
+{
+       ((struct dao_ctrl_blk *)blk)->spos = spos;
+       ((struct dao_ctrl_blk *)blk)->dirty.bf.spos = 1;
+       return 0;
+}
+
+static int dao_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+       struct dao_ctrl_blk *ctl = blk;
+
+       if (ctl->dirty.bf.spos) {
+               if (idx < 4) {
+                       /* S/PDIF SPOSx */
+                       hw_write_20kx(hw, SPOS+0x4*idx, ctl->spos);
+               }
+               ctl->dirty.bf.spos = 0;
+       }
+
+       return 0;
+}
+
+static int dao_get_spos(void *blk, unsigned int *spos)
+{
+       *spos = ((struct dao_ctrl_blk *)blk)->spos;
+       return 0;
+}
+
+static int dao_get_ctrl_blk(void **rblk)
+{
+       struct dao_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int dao_put_ctrl_blk(void *blk)
+{
+       kfree((struct dao_ctrl_blk *)blk);
+
+       return 0;
+}
+
+static int daio_mgr_enb_dai(void *blk, unsigned int idx)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       if (idx < 4) {
+               /* S/PDIF input */
+               set_field(&ctl->spictl, SPICTL_EN << (idx*8), 1);
+               ctl->dirty.bf.spictl |= (0x1 << idx);
+       } else {
+               /* I2S input */
+               idx %= 4;
+               set_field(&ctl->i2sctl, I2SCTL_EI << (idx*8), 1);
+               ctl->dirty.bf.i2sictl |= (0x1 << idx);
+       }
+       return 0;
+}
+
+static int daio_mgr_dsb_dai(void *blk, unsigned int idx)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       if (idx < 4) {
+               /* S/PDIF input */
+               set_field(&ctl->spictl, SPICTL_EN << (idx*8), 0);
+               ctl->dirty.bf.spictl |= (0x1 << idx);
+       } else {
+               /* I2S input */
+               idx %= 4;
+               set_field(&ctl->i2sctl, I2SCTL_EI << (idx*8), 0);
+               ctl->dirty.bf.i2sictl |= (0x1 << idx);
+       }
+       return 0;
+}
+
+static int daio_mgr_enb_dao(void *blk, unsigned int idx)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       if (idx < 4) {
+               /* S/PDIF output */
+               set_field(&ctl->spoctl, SPOCTL_OE << (idx*8), 1);
+               ctl->dirty.bf.spoctl |= (0x1 << idx);
+       } else {
+               /* I2S output */
+               idx %= 4;
+               set_field(&ctl->i2sctl, I2SCTL_EA << (idx*8), 1);
+               ctl->dirty.bf.i2soctl |= (0x1 << idx);
+       }
+       return 0;
+}
+
+static int daio_mgr_dsb_dao(void *blk, unsigned int idx)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       if (idx < 4) {
+               /* S/PDIF output */
+               set_field(&ctl->spoctl, SPOCTL_OE << (idx*8), 0);
+               ctl->dirty.bf.spoctl |= (0x1 << idx);
+       } else {
+               /* I2S output */
+               idx %= 4;
+               set_field(&ctl->i2sctl, I2SCTL_EA << (idx*8), 0);
+               ctl->dirty.bf.i2soctl |= (0x1 << idx);
+       }
+       return 0;
+}
+
+static int daio_mgr_dao_init(void *blk, unsigned int idx, unsigned int conf)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       if (idx < 4) {
+               /* S/PDIF output */
+               switch ((conf & 0x7)) {
+               case 0:
+                       set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 3);
+                       break; /* CDIF */
+               case 1:
+                       set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 0);
+                       break;
+               case 2:
+                       set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 1);
+                       break;
+               case 4:
+                       set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 2);
+                       break;
+               default:
+                       break;
+               }
+               set_field(&ctl->spoctl, SPOCTL_LIV << (idx*8),
+                         (conf >> 4) & 0x1); /* Non-audio */
+               set_field(&ctl->spoctl, SPOCTL_RIV << (idx*8),
+                         (conf >> 4) & 0x1); /* Non-audio */
+               set_field(&ctl->spoctl, SPOCTL_OS << (idx*8),
+                         ((conf >> 3) & 0x1) ? 2 : 2); /* Raw */
+
+               ctl->dirty.bf.spoctl |= (0x1 << idx);
+       } else {
+               /* I2S output */
+               /*idx %= 4; */
+       }
+       return 0;
+}
+
+static int daio_mgr_set_imaparc(void *blk, unsigned int slot)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->daoimap.aim, AIM_ARC, slot);
+       ctl->dirty.bf.daoimap = 1;
+       return 0;
+}
+
+static int daio_mgr_set_imapnxt(void *blk, unsigned int next)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->daoimap.aim, AIM_NXT, next);
+       ctl->dirty.bf.daoimap = 1;
+       return 0;
+}
+
+static int daio_mgr_set_imapaddr(void *blk, unsigned int addr)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       ctl->daoimap.idx = addr;
+       ctl->dirty.bf.daoimap = 1;
+       return 0;
+}
+
+static int daio_mgr_commit_write(struct hw *hw, void *blk)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+       int i;
+
+       if (ctl->dirty.bf.i2sictl || ctl->dirty.bf.i2soctl) {
+               for (i = 0; i < 4; i++) {
+                       if ((ctl->dirty.bf.i2sictl & (0x1 << i)))
+                               ctl->dirty.bf.i2sictl &= ~(0x1 << i);
+
+                       if ((ctl->dirty.bf.i2soctl & (0x1 << i)))
+                               ctl->dirty.bf.i2soctl &= ~(0x1 << i);
+               }
+               hw_write_20kx(hw, I2SCTL, ctl->i2sctl);
+               mdelay(1);
+       }
+       if (ctl->dirty.bf.spoctl) {
+               for (i = 0; i < 4; i++) {
+                       if ((ctl->dirty.bf.spoctl & (0x1 << i)))
+                               ctl->dirty.bf.spoctl &= ~(0x1 << i);
+               }
+               hw_write_20kx(hw, SPOCTL, ctl->spoctl);
+               mdelay(1);
+       }
+       if (ctl->dirty.bf.spictl) {
+               for (i = 0; i < 4; i++) {
+                       if ((ctl->dirty.bf.spictl & (0x1 << i)))
+                               ctl->dirty.bf.spictl &= ~(0x1 << i);
+               }
+               hw_write_20kx(hw, SPICTL, ctl->spictl);
+               mdelay(1);
+       }
+       if (ctl->dirty.bf.daoimap) {
+               hw_write_20kx(hw, DAOIMAP+ctl->daoimap.idx*4,
+                                       ctl->daoimap.aim);
+               ctl->dirty.bf.daoimap = 0;
+       }
+
+       return 0;
+}
+
+static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk)
+{
+       struct daio_mgr_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       blk->i2sctl = hw_read_20kx(hw, I2SCTL);
+       blk->spoctl = hw_read_20kx(hw, SPOCTL);
+       blk->spictl = hw_read_20kx(hw, SPICTL);
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int daio_mgr_put_ctrl_blk(void *blk)
+{
+       kfree((struct daio_mgr_ctrl_blk *)blk);
+
+       return 0;
+}
+
+/* Timer interrupt */
+static int set_timer_irq(struct hw *hw, int enable)
+{
+       hw_write_20kx(hw, GIE, enable ? IT_INT : 0);
+       return 0;
+}
+
+static int set_timer_tick(struct hw *hw, unsigned int ticks)
+{
+       if (ticks)
+               ticks |= TIMR_IE | TIMR_IP;
+       hw_write_20kx(hw, TIMR, ticks);
+       return 0;
+}
+
+static unsigned int get_wc(struct hw *hw)
+{
+       return hw_read_20kx(hw, WC);
+}
+
+/* Card hardware initialization block */
+struct dac_conf {
+       unsigned int msr; /* master sample rate in rsrs */
+};
+
+struct adc_conf {
+       unsigned int msr;       /* master sample rate in rsrs */
+       unsigned char input;    /* the input source of ADC */
+       unsigned char mic20db;  /* boost mic by 20db if input is microphone */
+};
+
+struct daio_conf {
+       unsigned int msr; /* master sample rate in rsrs */
+};
+
+struct trn_conf {
+       unsigned long vm_pgt_phys;
+};
+
+static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
+{
+       u32 i2sorg;
+       u32 spdorg;
+
+       /* Read I2S CTL.  Keep original value. */
+       /*i2sorg = hw_read_20kx(hw, I2SCTL);*/
+       i2sorg = 0x94040404; /* enable all audio out and I2S-D input */
+       /* Program I2S with proper master sample rate and enable
+        * the correct I2S channel. */
+       i2sorg &= 0xfffffffc;
+
+       /* Enable S/PDIF-out-A in fixed 24-bit data
+        * format and default to 48kHz. */
+       /* Disable all before doing any changes. */
+       hw_write_20kx(hw, SPOCTL, 0x0);
+       spdorg = 0x05;
+
+       switch (info->msr) {
+       case 1:
+               i2sorg |= 1;
+               spdorg |= (0x0 << 6);
+               break;
+       case 2:
+               i2sorg |= 2;
+               spdorg |= (0x1 << 6);
+               break;
+       case 4:
+               i2sorg |= 3;
+               spdorg |= (0x2 << 6);
+               break;
+       default:
+               i2sorg |= 1;
+               break;
+       }
+
+       hw_write_20kx(hw, I2SCTL, i2sorg);
+       hw_write_20kx(hw, SPOCTL, spdorg);
+
+       /* Enable S/PDIF-in-A in fixed 24-bit data format. */
+       /* Disable all before doing any changes. */
+       hw_write_20kx(hw, SPICTL, 0x0);
+       mdelay(1);
+       spdorg = 0x0a0a0a0a;
+       hw_write_20kx(hw, SPICTL, spdorg);
+       mdelay(1);
+
+       return 0;
+}
+
+/* TRANSPORT operations */
+static int hw_trn_init(struct hw *hw, const struct trn_conf *info)
+{
+       u32 trnctl;
+       u32 ptp_phys_low, ptp_phys_high;
+
+       /* Set up device page table */
+       if ((~0UL) == info->vm_pgt_phys) {
+               printk(KERN_ERR "Wrong device page table page address!\n");
+               return -1;
+       }
+
+       trnctl = 0x13;  /* 32-bit, 4k-size page */
+       ptp_phys_low = (u32)info->vm_pgt_phys;
+       ptp_phys_high = upper_32_bits(info->vm_pgt_phys);
+       if (sizeof(void *) == 8) /* 64bit address */
+               trnctl |= (1 << 2);
+#if 0 /* Only 4k h/w pages for simplicitiy */
+#if PAGE_SIZE == 8192
+       trnctl |= (1<<5);
+#endif
+#endif
+       hw_write_20kx(hw, PTPALX, ptp_phys_low);
+       hw_write_20kx(hw, PTPAHX, ptp_phys_high);
+       hw_write_20kx(hw, TRNCTL, trnctl);
+       hw_write_20kx(hw, TRNIS, 0x200c01); /* realy needed? */
+
+       return 0;
+}
+
+/* Card initialization */
+#define GCTL_EAC       0x00000001
+#define GCTL_EAI       0x00000002
+#define GCTL_BEP       0x00000004
+#define GCTL_BES       0x00000008
+#define GCTL_DSP       0x00000010
+#define GCTL_DBP       0x00000020
+#define GCTL_ABP       0x00000040
+#define GCTL_TBP       0x00000080
+#define GCTL_SBP       0x00000100
+#define GCTL_FBP       0x00000200
+#define GCTL_XA                0x00000400
+#define GCTL_ET                0x00000800
+#define GCTL_PR                0x00001000
+#define GCTL_MRL       0x00002000
+#define GCTL_SDE       0x00004000
+#define GCTL_SDI       0x00008000
+#define GCTL_SM                0x00010000
+#define GCTL_SR                0x00020000
+#define GCTL_SD                0x00040000
+#define GCTL_SE                0x00080000
+#define GCTL_AID       0x00100000
+
+static int hw_pll_init(struct hw *hw, unsigned int rsr)
+{
+       unsigned int pllctl;
+       int i;
+
+       pllctl = (48000 == rsr) ? 0x1480a001 : 0x1480a731;
+       for (i = 0; i < 3; i++) {
+               if (hw_read_20kx(hw, PLLCTL) == pllctl)
+                       break;
+
+               hw_write_20kx(hw, PLLCTL, pllctl);
+               mdelay(40);
+       }
+       if (i >= 3) {
+               printk(KERN_ALERT "PLL initialization failed!!!\n");
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+static int hw_auto_init(struct hw *hw)
+{
+       unsigned int gctl;
+       int i;
+
+       gctl = hw_read_20kx(hw, GCTL);
+       set_field(&gctl, GCTL_EAI, 0);
+       hw_write_20kx(hw, GCTL, gctl);
+       set_field(&gctl, GCTL_EAI, 1);
+       hw_write_20kx(hw, GCTL, gctl);
+       mdelay(10);
+       for (i = 0; i < 400000; i++) {
+               gctl = hw_read_20kx(hw, GCTL);
+               if (get_field(gctl, GCTL_AID))
+                       break;
+       }
+       if (!get_field(gctl, GCTL_AID)) {
+               printk(KERN_ALERT "Card Auto-init failed!!!\n");
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+static int i2c_unlock(struct hw *hw)
+{
+       if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa)
+               return 0;
+
+       hw_write_pci(hw, 0xcc, 0x8c);
+       hw_write_pci(hw, 0xcc, 0x0e);
+       if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa)
+               return 0;
+
+       hw_write_pci(hw, 0xcc, 0xee);
+       hw_write_pci(hw, 0xcc, 0xaa);
+       if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa)
+               return 0;
+
+       return -1;
+}
+
+static void i2c_lock(struct hw *hw)
+{
+       if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa)
+               hw_write_pci(hw, 0xcc, 0x00);
+}
+
+static void i2c_write(struct hw *hw, u32 device, u32 addr, u32 data)
+{
+       unsigned int ret;
+
+       do {
+               ret = hw_read_pci(hw, 0xEC);
+       } while (!(ret & 0x800000));
+       hw_write_pci(hw, 0xE0, device);
+       hw_write_pci(hw, 0xE4, (data << 8) | (addr & 0xff));
+}
+
+/* DAC operations */
+
+static int hw_reset_dac(struct hw *hw)
+{
+       u32 i;
+       u16 gpioorg;
+       unsigned int ret;
+
+       if (i2c_unlock(hw))
+               return -1;
+
+       do {
+               ret = hw_read_pci(hw, 0xEC);
+       } while (!(ret & 0x800000));
+       hw_write_pci(hw, 0xEC, 0x05);  /* write to i2c status control */
+
+       /* To be effective, need to reset the DAC twice. */
+       for (i = 0; i < 2;  i++) {
+               /* set gpio */
+               mdelay(100);
+               gpioorg = (u16)hw_read_20kx(hw, GPIO);
+               gpioorg &= 0xfffd;
+               hw_write_20kx(hw, GPIO, gpioorg);
+               mdelay(1);
+               hw_write_20kx(hw, GPIO, gpioorg | 0x2);
+       }
+
+       i2c_write(hw, 0x00180080, 0x01, 0x80);
+       i2c_write(hw, 0x00180080, 0x02, 0x10);
+
+       i2c_lock(hw);
+
+       return 0;
+}
+
+static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
+{
+       u32 data;
+       u16 gpioorg;
+       unsigned int ret;
+
+       if (hw->model == CTSB055X) {
+               /* SB055x, unmute outputs */
+               gpioorg = (u16)hw_read_20kx(hw, GPIO);
+               gpioorg &= 0xffbf;      /* set GPIO6 to low */
+               gpioorg |= 2;           /* set GPIO1 to high */
+               hw_write_20kx(hw, GPIO, gpioorg);
+               return 0;
+       }
+
+       /* mute outputs */
+       gpioorg = (u16)hw_read_20kx(hw, GPIO);
+       gpioorg &= 0xffbf;
+       hw_write_20kx(hw, GPIO, gpioorg);
+
+       hw_reset_dac(hw);
+
+       if (i2c_unlock(hw))
+               return -1;
+
+       hw_write_pci(hw, 0xEC, 0x05);  /* write to i2c status control */
+       do {
+               ret = hw_read_pci(hw, 0xEC);
+       } while (!(ret & 0x800000));
+
+       switch (info->msr) {
+       case 1:
+               data = 0x24;
+               break;
+       case 2:
+               data = 0x25;
+               break;
+       case 4:
+               data = 0x26;
+               break;
+       default:
+               data = 0x24;
+               break;
+       }
+
+       i2c_write(hw, 0x00180080, 0x06, data);
+       i2c_write(hw, 0x00180080, 0x09, data);
+       i2c_write(hw, 0x00180080, 0x0c, data);
+       i2c_write(hw, 0x00180080, 0x0f, data);
+
+       i2c_lock(hw);
+
+       /* unmute outputs */
+       gpioorg = (u16)hw_read_20kx(hw, GPIO);
+       gpioorg = gpioorg | 0x40;
+       hw_write_20kx(hw, GPIO, gpioorg);
+
+       return 0;
+}
+
+/* ADC operations */
+
+static int is_adc_input_selected_SB055x(struct hw *hw, enum ADCSRC type)
+{
+       return 0;
+}
+
+static int is_adc_input_selected_SBx(struct hw *hw, enum ADCSRC type)
+{
+       u32 data;
+
+       data = hw_read_20kx(hw, GPIO);
+       switch (type) {
+       case ADC_MICIN:
+               data = ((data & (0x1<<7)) && (data & (0x1<<8)));
+               break;
+       case ADC_LINEIN:
+               data = (!(data & (0x1<<7)) && (data & (0x1<<8)));
+               break;
+       case ADC_NONE: /* Digital I/O */
+               data = (!(data & (0x1<<8)));
+               break;
+       default:
+               data = 0;
+       }
+       return data;
+}
+
+static int is_adc_input_selected_hendrix(struct hw *hw, enum ADCSRC type)
+{
+       u32 data;
+
+       data = hw_read_20kx(hw, GPIO);
+       switch (type) {
+       case ADC_MICIN:
+               data = (data & (0x1 << 7)) ? 1 : 0;
+               break;
+       case ADC_LINEIN:
+               data = (data & (0x1 << 7)) ? 0 : 1;
+               break;
+       default:
+               data = 0;
+       }
+       return data;
+}
+
+static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type)
+{
+       switch (hw->model) {
+       case CTSB055X:
+               return is_adc_input_selected_SB055x(hw, type);
+       case CTSB073X:
+               return is_adc_input_selected_hendrix(hw, type);
+       case CTUAA:
+               return is_adc_input_selected_hendrix(hw, type);
+       default:
+               return is_adc_input_selected_SBx(hw, type);
+       }
+}
+
+static int
+adc_input_select_SB055x(struct hw *hw, enum ADCSRC type, unsigned char boost)
+{
+       u32 data;
+
+       /*
+        * check and set the following GPIO bits accordingly
+        * ADC_Gain             = GPIO2
+        * DRM_off              = GPIO3
+        * Mic_Pwr_on           = GPIO7
+        * Digital_IO_Sel       = GPIO8
+        * Mic_Sw               = GPIO9
+        * Aux/MicLine_Sw       = GPIO12
+        */
+       data = hw_read_20kx(hw, GPIO);
+       data &= 0xec73;
+       switch (type) {
+       case ADC_MICIN:
+               data |= (0x1<<7) | (0x1<<8) | (0x1<<9) ;
+               data |= boost ? (0x1<<2) : 0;
+               break;
+       case ADC_LINEIN:
+               data |= (0x1<<8);
+               break;
+       case ADC_AUX:
+               data |= (0x1<<8) | (0x1<<12);
+               break;
+       case ADC_NONE:
+               data |= (0x1<<12);  /* set to digital */
+               break;
+       default:
+               return -1;
+       }
+
+       hw_write_20kx(hw, GPIO, data);
+
+       return 0;
+}
+
+
+static int
+adc_input_select_SBx(struct hw *hw, enum ADCSRC type, unsigned char boost)
+{
+       u32 data;
+       u32 i2c_data;
+       unsigned int ret;
+
+       if (i2c_unlock(hw))
+               return -1;
+
+       do {
+               ret = hw_read_pci(hw, 0xEC);
+       } while (!(ret & 0x800000)); /* i2c ready poll */
+       /* set i2c access mode as Direct Control */
+       hw_write_pci(hw, 0xEC, 0x05);
+
+       data = hw_read_20kx(hw, GPIO);
+       switch (type) {
+       case ADC_MICIN:
+               data |= ((0x1 << 7) | (0x1 << 8));
+               i2c_data = 0x1;  /* Mic-in */
+               break;
+       case ADC_LINEIN:
+               data &= ~(0x1 << 7);
+               data |= (0x1 << 8);
+               i2c_data = 0x2; /* Line-in */
+               break;
+       case ADC_NONE:
+               data &= ~(0x1 << 8);
+               i2c_data = 0x0; /* set to Digital */
+               break;
+       default:
+               i2c_lock(hw);
+               return -1;
+       }
+       hw_write_20kx(hw, GPIO, data);
+       i2c_write(hw, 0x001a0080, 0x2a, i2c_data);
+       if (boost) {
+               i2c_write(hw, 0x001a0080, 0x1c, 0xe7); /* +12dB boost */
+               i2c_write(hw, 0x001a0080, 0x1e, 0xe7); /* +12dB boost */
+       } else {
+               i2c_write(hw, 0x001a0080, 0x1c, 0xcf); /* No boost */
+               i2c_write(hw, 0x001a0080, 0x1e, 0xcf); /* No boost */
+       }
+
+       i2c_lock(hw);
+
+       return 0;
+}
+
+static int
+adc_input_select_hendrix(struct hw *hw, enum ADCSRC type, unsigned char boost)
+{
+       u32 data;
+       u32 i2c_data;
+       unsigned int ret;
+
+       if (i2c_unlock(hw))
+               return -1;
+
+       do {
+               ret = hw_read_pci(hw, 0xEC);
+       } while (!(ret & 0x800000)); /* i2c ready poll */
+       /* set i2c access mode as Direct Control */
+       hw_write_pci(hw, 0xEC, 0x05);
+
+       data = hw_read_20kx(hw, GPIO);
+       switch (type) {
+       case ADC_MICIN:
+               data |= (0x1 << 7);
+               i2c_data = 0x1;  /* Mic-in */
+               break;
+       case ADC_LINEIN:
+               data &= ~(0x1 << 7);
+               i2c_data = 0x2; /* Line-in */
+               break;
+       default:
+               i2c_lock(hw);
+               return -1;
+       }
+       hw_write_20kx(hw, GPIO, data);
+       i2c_write(hw, 0x001a0080, 0x2a, i2c_data);
+       if (boost) {
+               i2c_write(hw, 0x001a0080, 0x1c, 0xe7); /* +12dB boost */
+               i2c_write(hw, 0x001a0080, 0x1e, 0xe7); /* +12dB boost */
+       } else {
+               i2c_write(hw, 0x001a0080, 0x1c, 0xcf); /* No boost */
+               i2c_write(hw, 0x001a0080, 0x1e, 0xcf); /* No boost */
+       }
+
+       i2c_lock(hw);
+
+       return 0;
+}
+
+static int hw_adc_input_select(struct hw *hw, enum ADCSRC type)
+{
+       int state = type == ADC_MICIN;
+
+       switch (hw->model) {
+       case CTSB055X:
+               return adc_input_select_SB055x(hw, type, state);
+       case CTSB073X:
+               return adc_input_select_hendrix(hw, type, state);
+       case CTUAA:
+               return adc_input_select_hendrix(hw, type, state);
+       default:
+               return adc_input_select_SBx(hw, type, state);
+       }
+}
+
+static int adc_init_SB055x(struct hw *hw, int input, int mic20db)
+{
+       return adc_input_select_SB055x(hw, input, mic20db);
+}
+
+static int adc_init_SBx(struct hw *hw, int input, int mic20db)
+{
+       u16 gpioorg;
+       u16 input_source;
+       u32 adcdata;
+       unsigned int ret;
+
+       input_source = 0x100;  /* default to analog */
+       switch (input) {
+       case ADC_MICIN:
+               adcdata = 0x1;
+               input_source = 0x180;  /* set GPIO7 to select Mic */
+               break;
+       case ADC_LINEIN:
+               adcdata = 0x2;
+               break;
+       case ADC_VIDEO:
+               adcdata = 0x4;
+               break;
+       case ADC_AUX:
+               adcdata = 0x8;
+               break;
+       case ADC_NONE:
+               adcdata = 0x0;
+               input_source = 0x0;  /* set to Digital */
+               break;
+       default:
+               adcdata = 0x0;
+               break;
+       }
+
+       if (i2c_unlock(hw))
+               return -1;
+
+       do {
+               ret = hw_read_pci(hw, 0xEC);
+       } while (!(ret & 0x800000)); /* i2c ready poll */
+       hw_write_pci(hw, 0xEC, 0x05);  /* write to i2c status control */
+
+       i2c_write(hw, 0x001a0080, 0x0e, 0x08);
+       i2c_write(hw, 0x001a0080, 0x18, 0x0a);
+       i2c_write(hw, 0x001a0080, 0x28, 0x86);
+       i2c_write(hw, 0x001a0080, 0x2a, adcdata);
+
+       if (mic20db) {
+               i2c_write(hw, 0x001a0080, 0x1c, 0xf7);
+               i2c_write(hw, 0x001a0080, 0x1e, 0xf7);
+       } else {
+               i2c_write(hw, 0x001a0080, 0x1c, 0xcf);
+               i2c_write(hw, 0x001a0080, 0x1e, 0xcf);
+       }
+
+       if (!(hw_read_20kx(hw, ID0) & 0x100))
+               i2c_write(hw, 0x001a0080, 0x16, 0x26);
+
+       i2c_lock(hw);
+
+       gpioorg = (u16)hw_read_20kx(hw,  GPIO);
+       gpioorg &= 0xfe7f;
+       gpioorg |= input_source;
+       hw_write_20kx(hw, GPIO, gpioorg);
+
+       return 0;
+}
+
+static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
+{
+       if (hw->model == CTSB055X)
+               return adc_init_SB055x(hw, info->input, info->mic20db);
+       else
+               return adc_init_SBx(hw, info->input, info->mic20db);
+}
+
+static int hw_have_digit_io_switch(struct hw *hw)
+{
+       /* SB073x and Vista compatible cards have no digit IO switch */
+       return !(hw->model == CTSB073X || hw->model == CTUAA);
+}
+
+#define CTLBITS(a, b, c, d)    (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
+
+#define UAA_CFG_PWRSTATUS      0x44
+#define UAA_CFG_SPACE_FLAG     0xA0
+#define UAA_CORE_CHANGE                0x3FFC
+static int uaa_to_xfi(struct pci_dev *pci)
+{
+       unsigned int bar0, bar1, bar2, bar3, bar4, bar5;
+       unsigned int cmd, irq, cl_size, l_timer, pwr;
+       unsigned int is_uaa;
+       unsigned int data[4] = {0};
+       unsigned int io_base;
+       void *mem_base;
+       int i;
+       const u32 CTLX = CTLBITS('C', 'T', 'L', 'X');
+       const u32 CTL_ = CTLBITS('C', 'T', 'L', '-');
+       const u32 CTLF = CTLBITS('C', 'T', 'L', 'F');
+       const u32 CTLi = CTLBITS('C', 'T', 'L', 'i');
+       const u32 CTLA = CTLBITS('C', 'T', 'L', 'A');
+       const u32 CTLZ = CTLBITS('C', 'T', 'L', 'Z');
+       const u32 CTLL = CTLBITS('C', 'T', 'L', 'L');
+
+       /* By default, Hendrix card UAA Bar0 should be using memory... */
+       io_base = pci_resource_start(pci, 0);
+       mem_base = ioremap(io_base, pci_resource_len(pci, 0));
+       if (NULL == mem_base)
+               return -ENOENT;
+
+       /* Read current mode from Mode Change Register */
+       for (i = 0; i < 4; i++)
+               data[i] = readl(mem_base + UAA_CORE_CHANGE);
+
+       /* Determine current mode... */
+       if (data[0] == CTLA) {
+               is_uaa = ((data[1] == CTLZ && data[2] == CTLL
+                         && data[3] == CTLA) || (data[1] == CTLA
+                         && data[2] == CTLZ && data[3] == CTLL));
+       } else if (data[0] == CTLZ) {
+               is_uaa = (data[1] == CTLL
+                               && data[2] == CTLA && data[3] == CTLA);
+       } else if (data[0] == CTLL) {
+               is_uaa = (data[1] == CTLA
+                               && data[2] == CTLA && data[3] == CTLZ);
+       } else {
+               is_uaa = 0;
+       }
+
+       if (!is_uaa) {
+               /* Not in UAA mode currently. Return directly. */
+               iounmap(mem_base);
+               return 0;
+       }
+
+       pci_read_config_dword(pci, PCI_BASE_ADDRESS_0, &bar0);
+       pci_read_config_dword(pci, PCI_BASE_ADDRESS_1, &bar1);
+       pci_read_config_dword(pci, PCI_BASE_ADDRESS_2, &bar2);
+       pci_read_config_dword(pci, PCI_BASE_ADDRESS_3, &bar3);
+       pci_read_config_dword(pci, PCI_BASE_ADDRESS_4, &bar4);
+       pci_read_config_dword(pci, PCI_BASE_ADDRESS_5, &bar5);
+       pci_read_config_dword(pci, PCI_INTERRUPT_LINE, &irq);
+       pci_read_config_dword(pci, PCI_CACHE_LINE_SIZE, &cl_size);
+       pci_read_config_dword(pci, PCI_LATENCY_TIMER, &l_timer);
+       pci_read_config_dword(pci, UAA_CFG_PWRSTATUS, &pwr);
+       pci_read_config_dword(pci, PCI_COMMAND, &cmd);
+
+       /* Set up X-Fi core PCI configuration space. */
+       /* Switch to X-Fi config space with BAR0 exposed. */
+       pci_write_config_dword(pci, UAA_CFG_SPACE_FLAG, 0x87654321);
+       /* Copy UAA's BAR5 into X-Fi BAR0 */
+       pci_write_config_dword(pci, PCI_BASE_ADDRESS_0, bar5);
+       /* Switch to X-Fi config space without BAR0 exposed. */
+       pci_write_config_dword(pci, UAA_CFG_SPACE_FLAG, 0x12345678);
+       pci_write_config_dword(pci, PCI_BASE_ADDRESS_1, bar1);
+       pci_write_config_dword(pci, PCI_BASE_ADDRESS_2, bar2);
+       pci_write_config_dword(pci, PCI_BASE_ADDRESS_3, bar3);
+       pci_write_config_dword(pci, PCI_BASE_ADDRESS_4, bar4);
+       pci_write_config_dword(pci, PCI_INTERRUPT_LINE, irq);
+       pci_write_config_dword(pci, PCI_CACHE_LINE_SIZE, cl_size);
+       pci_write_config_dword(pci, PCI_LATENCY_TIMER, l_timer);
+       pci_write_config_dword(pci, UAA_CFG_PWRSTATUS, pwr);
+       pci_write_config_dword(pci, PCI_COMMAND, cmd);
+
+       /* Switch to X-Fi mode */
+       writel(CTLX, (mem_base + UAA_CORE_CHANGE));
+       writel(CTL_, (mem_base + UAA_CORE_CHANGE));
+       writel(CTLF, (mem_base + UAA_CORE_CHANGE));
+       writel(CTLi, (mem_base + UAA_CORE_CHANGE));
+
+       iounmap(mem_base);
+
+       return 0;
+}
+
+static irqreturn_t ct_20k1_interrupt(int irq, void *dev_id)
+{
+       struct hw *hw = dev_id;
+       unsigned int status;
+
+       status = hw_read_20kx(hw, GIP);
+       if (!status)
+               return IRQ_NONE;
+
+       if (hw->irq_callback)
+               hw->irq_callback(hw->irq_callback_data, status);
+
+       hw_write_20kx(hw, GIP, status);
+       return IRQ_HANDLED;
+}
+
+static int hw_card_start(struct hw *hw)
+{
+       int err;
+       struct pci_dev *pci = hw->pci;
+
+       err = pci_enable_device(pci);
+       if (err < 0)
+               return err;
+
+       /* Set DMA transfer mask */
+       if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 ||
+           pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) {
+               printk(KERN_ERR "architecture does not support PCI "
+                               "busmaster DMA with mask 0x%llx\n",
+                      CT_XFI_DMA_MASK);
+               err = -ENXIO;
+               goto error1;
+       }
+
+       err = pci_request_regions(pci, "XFi");
+       if (err < 0)
+               goto error1;
+
+       /* Switch to X-Fi mode from UAA mode if neeeded */
+       if (hw->model == CTUAA) {
+               err = uaa_to_xfi(pci);
+               if (err)
+                       goto error2;
+
+               hw->io_base = pci_resource_start(pci, 5);
+       } else {
+               hw->io_base = pci_resource_start(pci, 0);
+       }
+
+       err = request_irq(pci->irq, ct_20k1_interrupt, IRQF_SHARED,
+                         "ctxfi", hw);
+       if (err < 0) {
+               printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq);
+               goto error2;
+       }
+       hw->irq = pci->irq;
+
+       pci_set_master(pci);
+
+       return 0;
+
+error2:
+       pci_release_regions(pci);
+       hw->io_base = 0;
+error1:
+       pci_disable_device(pci);
+       return err;
+}
+
+static int hw_card_stop(struct hw *hw)
+{
+       /* TODO: Disable interrupt and so on... */
+       if (hw->irq >= 0)
+               synchronize_irq(hw->irq);
+       return 0;
+}
+
+static int hw_card_shutdown(struct hw *hw)
+{
+       if (hw->irq >= 0)
+               free_irq(hw->irq, hw);
+
+       hw->irq = -1;
+
+       if (NULL != ((void *)hw->mem_base))
+               iounmap((void *)hw->mem_base);
+
+       hw->mem_base = (unsigned long)NULL;
+
+       if (hw->io_base)
+               pci_release_regions(hw->pci);
+
+       hw->io_base = 0;
+
+       pci_disable_device(hw->pci);
+
+       return 0;
+}
+
+static int hw_card_init(struct hw *hw, struct card_conf *info)
+{
+       int err;
+       unsigned int gctl;
+       u32 data;
+       struct dac_conf dac_info = {0};
+       struct adc_conf adc_info = {0};
+       struct daio_conf daio_info = {0};
+       struct trn_conf trn_info = {0};
+
+       /* Get PCI io port base address and do Hendrix switch if needed. */
+       if (!hw->io_base) {
+               err = hw_card_start(hw);
+               if (err)
+                       return err;
+       }
+
+       /* PLL init */
+       err = hw_pll_init(hw, info->rsr);
+       if (err < 0)
+               return err;
+
+       /* kick off auto-init */
+       err = hw_auto_init(hw);
+       if (err < 0)
+               return err;
+
+       /* Enable audio ring */
+       gctl = hw_read_20kx(hw, GCTL);
+       set_field(&gctl, GCTL_EAC, 1);
+       set_field(&gctl, GCTL_DBP, 1);
+       set_field(&gctl, GCTL_TBP, 1);
+       set_field(&gctl, GCTL_FBP, 1);
+       set_field(&gctl, GCTL_ET, 1);
+       hw_write_20kx(hw, GCTL, gctl);
+       mdelay(10);
+
+       /* Reset all global pending interrupts */
+       hw_write_20kx(hw, GIE, 0);
+       /* Reset all SRC pending interrupts */
+       hw_write_20kx(hw, SRCIP, 0);
+       mdelay(30);
+
+       /* Detect the card ID and configure GPIO accordingly. */
+       switch (hw->model) {
+       case CTSB055X:
+               hw_write_20kx(hw, GPIOCTL, 0x13fe);
+               break;
+       case CTSB073X:
+               hw_write_20kx(hw, GPIOCTL, 0x00e6);
+               break;
+       case CTUAA:
+               hw_write_20kx(hw, GPIOCTL, 0x00c2);
+               break;
+       default:
+               hw_write_20kx(hw, GPIOCTL, 0x01e6);
+               break;
+       }
+
+       trn_info.vm_pgt_phys = info->vm_pgt_phys;
+       err = hw_trn_init(hw, &trn_info);
+       if (err < 0)
+               return err;
+
+       daio_info.msr = info->msr;
+       err = hw_daio_init(hw, &daio_info);
+       if (err < 0)
+               return err;
+
+       dac_info.msr = info->msr;
+       err = hw_dac_init(hw, &dac_info);
+       if (err < 0)
+               return err;
+
+       adc_info.msr = info->msr;
+       adc_info.input = ADC_LINEIN;
+       adc_info.mic20db = 0;
+       err = hw_adc_init(hw, &adc_info);
+       if (err < 0)
+               return err;
+
+       data = hw_read_20kx(hw, SRCMCTL);
+       data |= 0x1; /* Enables input from the audio ring */
+       hw_write_20kx(hw, SRCMCTL, data);
+
+       return 0;
+}
+
+static u32 hw_read_20kx(struct hw *hw, u32 reg)
+{
+       u32 value;
+       unsigned long flags;
+
+       spin_lock_irqsave(
+               &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags);
+       outl(reg, hw->io_base + 0x0);
+       value = inl(hw->io_base + 0x4);
+       spin_unlock_irqrestore(
+               &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags);
+
+       return value;
+}
+
+static void hw_write_20kx(struct hw *hw, u32 reg, u32 data)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(
+               &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags);
+       outl(reg, hw->io_base + 0x0);
+       outl(data, hw->io_base + 0x4);
+       spin_unlock_irqrestore(
+               &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags);
+
+}
+
+static u32 hw_read_pci(struct hw *hw, u32 reg)
+{
+       u32 value;
+       unsigned long flags;
+
+       spin_lock_irqsave(
+               &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
+       outl(reg, hw->io_base + 0x10);
+       value = inl(hw->io_base + 0x14);
+       spin_unlock_irqrestore(
+               &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
+
+       return value;
+}
+
+static void hw_write_pci(struct hw *hw, u32 reg, u32 data)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(
+               &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
+       outl(reg, hw->io_base + 0x10);
+       outl(data, hw->io_base + 0x14);
+       spin_unlock_irqrestore(
+               &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
+}
+
+static struct hw ct20k1_preset __devinitdata = {
+       .irq = -1,
+
+       .card_init = hw_card_init,
+       .card_stop = hw_card_stop,
+       .pll_init = hw_pll_init,
+       .is_adc_source_selected = hw_is_adc_input_selected,
+       .select_adc_source = hw_adc_input_select,
+       .have_digit_io_switch = hw_have_digit_io_switch,
+
+       .src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk,
+       .src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk,
+       .src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk,
+       .src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk,
+       .src_set_state = src_set_state,
+       .src_set_bm = src_set_bm,
+       .src_set_rsr = src_set_rsr,
+       .src_set_sf = src_set_sf,
+       .src_set_wr = src_set_wr,
+       .src_set_pm = src_set_pm,
+       .src_set_rom = src_set_rom,
+       .src_set_vo = src_set_vo,
+       .src_set_st = src_set_st,
+       .src_set_ie = src_set_ie,
+       .src_set_ilsz = src_set_ilsz,
+       .src_set_bp = src_set_bp,
+       .src_set_cisz = src_set_cisz,
+       .src_set_ca = src_set_ca,
+       .src_set_sa = src_set_sa,
+       .src_set_la = src_set_la,
+       .src_set_pitch = src_set_pitch,
+       .src_set_dirty = src_set_dirty,
+       .src_set_clear_zbufs = src_set_clear_zbufs,
+       .src_set_dirty_all = src_set_dirty_all,
+       .src_commit_write = src_commit_write,
+       .src_get_ca = src_get_ca,
+       .src_get_dirty = src_get_dirty,
+       .src_dirty_conj_mask = src_dirty_conj_mask,
+       .src_mgr_enbs_src = src_mgr_enbs_src,
+       .src_mgr_enb_src = src_mgr_enb_src,
+       .src_mgr_dsb_src = src_mgr_dsb_src,
+       .src_mgr_commit_write = src_mgr_commit_write,
+
+       .srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk,
+       .srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk,
+       .srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc,
+       .srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser,
+       .srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt,
+       .srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr,
+       .srcimp_mgr_commit_write = srcimp_mgr_commit_write,
+
+       .amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk,
+       .amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk,
+       .amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk,
+       .amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk,
+       .amixer_set_mode = amixer_set_mode,
+       .amixer_set_iv = amixer_set_iv,
+       .amixer_set_x = amixer_set_x,
+       .amixer_set_y = amixer_set_y,
+       .amixer_set_sadr = amixer_set_sadr,
+       .amixer_set_se = amixer_set_se,
+       .amixer_set_dirty = amixer_set_dirty,
+       .amixer_set_dirty_all = amixer_set_dirty_all,
+       .amixer_commit_write = amixer_commit_write,
+       .amixer_get_y = amixer_get_y,
+       .amixer_get_dirty = amixer_get_dirty,
+
+       .dai_get_ctrl_blk = dai_get_ctrl_blk,
+       .dai_put_ctrl_blk = dai_put_ctrl_blk,
+       .dai_srt_set_srco = dai_srt_set_srcr,
+       .dai_srt_set_srcm = dai_srt_set_srcl,
+       .dai_srt_set_rsr = dai_srt_set_rsr,
+       .dai_srt_set_drat = dai_srt_set_drat,
+       .dai_srt_set_ec = dai_srt_set_ec,
+       .dai_srt_set_et = dai_srt_set_et,
+       .dai_commit_write = dai_commit_write,
+
+       .dao_get_ctrl_blk = dao_get_ctrl_blk,
+       .dao_put_ctrl_blk = dao_put_ctrl_blk,
+       .dao_set_spos = dao_set_spos,
+       .dao_commit_write = dao_commit_write,
+       .dao_get_spos = dao_get_spos,
+
+       .daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk,
+       .daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk,
+       .daio_mgr_enb_dai = daio_mgr_enb_dai,
+       .daio_mgr_dsb_dai = daio_mgr_dsb_dai,
+       .daio_mgr_enb_dao = daio_mgr_enb_dao,
+       .daio_mgr_dsb_dao = daio_mgr_dsb_dao,
+       .daio_mgr_dao_init = daio_mgr_dao_init,
+       .daio_mgr_set_imaparc = daio_mgr_set_imaparc,
+       .daio_mgr_set_imapnxt = daio_mgr_set_imapnxt,
+       .daio_mgr_set_imapaddr = daio_mgr_set_imapaddr,
+       .daio_mgr_commit_write = daio_mgr_commit_write,
+
+       .set_timer_irq = set_timer_irq,
+       .set_timer_tick = set_timer_tick,
+       .get_wc = get_wc,
+};
+
+int __devinit create_20k1_hw_obj(struct hw **rhw)
+{
+       struct hw20k1 *hw20k1;
+
+       *rhw = NULL;
+       hw20k1 = kzalloc(sizeof(*hw20k1), GFP_KERNEL);
+       if (NULL == hw20k1)
+               return -ENOMEM;
+
+       spin_lock_init(&hw20k1->reg_20k1_lock);
+       spin_lock_init(&hw20k1->reg_pci_lock);
+
+       hw20k1->hw = ct20k1_preset;
+
+       *rhw = &hw20k1->hw;
+
+       return 0;
+}
+
+int destroy_20k1_hw_obj(struct hw *hw)
+{
+       if (hw->io_base)
+               hw_card_shutdown(hw);
+
+       kfree(container_of(hw, struct hw20k1, hw));
+       return 0;
+}
diff --git a/sound/pci/ctxfi/cthw20k1.h b/sound/pci/ctxfi/cthw20k1.h
new file mode 100644 (file)
index 0000000..02f72fb
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       cthw20k1.h
+ *
+ * @Brief
+ * This file contains the definition of hardware access methord.
+ *
+ * @Author     Liu Chun
+ * @Date       May 13 2008
+ *
+ */
+
+#ifndef CTHW20K1_H
+#define CTHW20K1_H
+
+#include "cthardware.h"
+
+int create_20k1_hw_obj(struct hw **rhw);
+int destroy_20k1_hw_obj(struct hw *hw);
+
+#endif /* CTHW20K1_H */
diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c
new file mode 100644 (file)
index 0000000..4493a51
--- /dev/null
@@ -0,0 +1,2137 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       cthw20k2.c
+ *
+ * @Brief
+ * This file contains the implementation of hardware access methord for 20k2.
+ *
+ * @Author     Liu Chun
+ * @Date       May 14 2008
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include "cthw20k2.h"
+#include "ct20k2reg.h"
+
+#if BITS_PER_LONG == 32
+#define CT_XFI_DMA_MASK                DMA_BIT_MASK(32) /* 32 bit PTE */
+#else
+#define CT_XFI_DMA_MASK                DMA_BIT_MASK(64) /* 64 bit PTE */
+#endif
+
+struct hw20k2 {
+       struct hw hw;
+       /* for i2c */
+       unsigned char dev_id;
+       unsigned char addr_size;
+       unsigned char data_size;
+};
+
+static u32 hw_read_20kx(struct hw *hw, u32 reg);
+static void hw_write_20kx(struct hw *hw, u32 reg, u32 data);
+
+/*
+ * Type definition block.
+ * The layout of control structures can be directly applied on 20k2 chip.
+ */
+
+/*
+ * SRC control block definitions.
+ */
+
+/* SRC resource control block */
+#define SRCCTL_STATE   0x00000007
+#define SRCCTL_BM      0x00000008
+#define SRCCTL_RSR     0x00000030
+#define SRCCTL_SF      0x000001C0
+#define SRCCTL_WR      0x00000200
+#define SRCCTL_PM      0x00000400
+#define SRCCTL_ROM     0x00001800
+#define SRCCTL_VO      0x00002000
+#define SRCCTL_ST      0x00004000
+#define SRCCTL_IE      0x00008000
+#define SRCCTL_ILSZ    0x000F0000
+#define SRCCTL_BP      0x00100000
+
+#define SRCCCR_CISZ    0x000007FF
+#define SRCCCR_CWA     0x001FF800
+#define SRCCCR_D       0x00200000
+#define SRCCCR_RS      0x01C00000
+#define SRCCCR_NAL     0x3E000000
+#define SRCCCR_RA      0xC0000000
+
+#define SRCCA_CA       0x0FFFFFFF
+#define SRCCA_RS       0xE0000000
+
+#define SRCSA_SA       0x0FFFFFFF
+
+#define SRCLA_LA       0x0FFFFFFF
+
+/* Mixer Parameter Ring ram Low and Hight register.
+ * Fixed-point value in 8.24 format for parameter channel */
+#define MPRLH_PITCH    0xFFFFFFFF
+
+/* SRC resource register dirty flags */
+union src_dirty {
+       struct {
+               u16 ctl:1;
+               u16 ccr:1;
+               u16 sa:1;
+               u16 la:1;
+               u16 ca:1;
+               u16 mpr:1;
+               u16 czbfs:1;    /* Clear Z-Buffers */
+               u16 rsv:9;
+       } bf;
+       u16 data;
+};
+
+struct src_rsc_ctrl_blk {
+       unsigned int    ctl;
+       unsigned int    ccr;
+       unsigned int    ca;
+       unsigned int    sa;
+       unsigned int    la;
+       unsigned int    mpr;
+       union src_dirty dirty;
+};
+
+/* SRC manager control block */
+union src_mgr_dirty {
+       struct {
+               u16 enb0:1;
+               u16 enb1:1;
+               u16 enb2:1;
+               u16 enb3:1;
+               u16 enb4:1;
+               u16 enb5:1;
+               u16 enb6:1;
+               u16 enb7:1;
+               u16 enbsa:1;
+               u16 rsv:7;
+       } bf;
+       u16 data;
+};
+
+struct src_mgr_ctrl_blk {
+       unsigned int            enbsa;
+       unsigned int            enb[8];
+       union src_mgr_dirty     dirty;
+};
+
+/* SRCIMP manager control block */
+#define SRCAIM_ARC     0x00000FFF
+#define SRCAIM_NXT     0x00FF0000
+#define SRCAIM_SRC     0xFF000000
+
+struct srcimap {
+       unsigned int srcaim;
+       unsigned int idx;
+};
+
+/* SRCIMP manager register dirty flags */
+union srcimp_mgr_dirty {
+       struct {
+               u16 srcimap:1;
+               u16 rsv:15;
+       } bf;
+       u16 data;
+};
+
+struct srcimp_mgr_ctrl_blk {
+       struct srcimap          srcimap;
+       union srcimp_mgr_dirty  dirty;
+};
+
+/*
+ * Function implementation block.
+ */
+
+static int src_get_rsc_ctrl_blk(void **rblk)
+{
+       struct src_rsc_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int src_put_rsc_ctrl_blk(void *blk)
+{
+       kfree(blk);
+
+       return 0;
+}
+
+static int src_set_state(void *blk, unsigned int state)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_STATE, state);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_bm(void *blk, unsigned int bm)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_BM, bm);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_rsr(void *blk, unsigned int rsr)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_RSR, rsr);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_sf(void *blk, unsigned int sf)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_SF, sf);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_wr(void *blk, unsigned int wr)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_WR, wr);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_pm(void *blk, unsigned int pm)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_PM, pm);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_rom(void *blk, unsigned int rom)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_ROM, rom);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_vo(void *blk, unsigned int vo)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_VO, vo);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_st(void *blk, unsigned int st)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_ST, st);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_ie(void *blk, unsigned int ie)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_IE, ie);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_ilsz(void *blk, unsigned int ilsz)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_ILSZ, ilsz);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_bp(void *blk, unsigned int bp)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_BP, bp);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_cisz(void *blk, unsigned int cisz)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ccr, SRCCCR_CISZ, cisz);
+       ctl->dirty.bf.ccr = 1;
+       return 0;
+}
+
+static int src_set_ca(void *blk, unsigned int ca)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ca, SRCCA_CA, ca);
+       ctl->dirty.bf.ca = 1;
+       return 0;
+}
+
+static int src_set_sa(void *blk, unsigned int sa)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->sa, SRCSA_SA, sa);
+       ctl->dirty.bf.sa = 1;
+       return 0;
+}
+
+static int src_set_la(void *blk, unsigned int la)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->la, SRCLA_LA, la);
+       ctl->dirty.bf.la = 1;
+       return 0;
+}
+
+static int src_set_pitch(void *blk, unsigned int pitch)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->mpr, MPRLH_PITCH, pitch);
+       ctl->dirty.bf.mpr = 1;
+       return 0;
+}
+
+static int src_set_clear_zbufs(void *blk, unsigned int clear)
+{
+       ((struct src_rsc_ctrl_blk *)blk)->dirty.bf.czbfs = (clear ? 1 : 0);
+       return 0;
+}
+
+static int src_set_dirty(void *blk, unsigned int flags)
+{
+       ((struct src_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff);
+       return 0;
+}
+
+static int src_set_dirty_all(void *blk)
+{
+       ((struct src_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0);
+       return 0;
+}
+
+#define AR_SLOT_SIZE           4096
+#define AR_SLOT_BLOCK_SIZE     16
+#define AR_PTS_PITCH           6
+#define AR_PARAM_SRC_OFFSET    0x60
+
+static unsigned int src_param_pitch_mixer(unsigned int src_idx)
+{
+       return ((src_idx << 4) + AR_PTS_PITCH + AR_SLOT_SIZE
+                       - AR_PARAM_SRC_OFFSET) % AR_SLOT_SIZE;
+
+}
+
+static int src_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+       int i;
+
+       if (ctl->dirty.bf.czbfs) {
+               /* Clear Z-Buffer registers */
+               for (i = 0; i < 8; i++)
+                       hw_write_20kx(hw, SRC_UPZ+idx*0x100+i*0x4, 0);
+
+               for (i = 0; i < 4; i++)
+                       hw_write_20kx(hw, SRC_DN0Z+idx*0x100+i*0x4, 0);
+
+               for (i = 0; i < 8; i++)
+                       hw_write_20kx(hw, SRC_DN1Z+idx*0x100+i*0x4, 0);
+
+               ctl->dirty.bf.czbfs = 0;
+       }
+       if (ctl->dirty.bf.mpr) {
+               /* Take the parameter mixer resource in the same group as that
+                * the idx src is in for simplicity. Unlike src, all conjugate
+                * parameter mixer resources must be programmed for
+                * corresponding conjugate src resources. */
+               unsigned int pm_idx = src_param_pitch_mixer(idx);
+               hw_write_20kx(hw, MIXER_PRING_LO_HI+4*pm_idx, ctl->mpr);
+               hw_write_20kx(hw, MIXER_PMOPLO+8*pm_idx, 0x3);
+               hw_write_20kx(hw, MIXER_PMOPHI+8*pm_idx, 0x0);
+               ctl->dirty.bf.mpr = 0;
+       }
+       if (ctl->dirty.bf.sa) {
+               hw_write_20kx(hw, SRC_SA+idx*0x100, ctl->sa);
+               ctl->dirty.bf.sa = 0;
+       }
+       if (ctl->dirty.bf.la) {
+               hw_write_20kx(hw, SRC_LA+idx*0x100, ctl->la);
+               ctl->dirty.bf.la = 0;
+       }
+       if (ctl->dirty.bf.ca) {
+               hw_write_20kx(hw, SRC_CA+idx*0x100, ctl->ca);
+               ctl->dirty.bf.ca = 0;
+       }
+
+       /* Write srccf register */
+       hw_write_20kx(hw, SRC_CF+idx*0x100, 0x0);
+
+       if (ctl->dirty.bf.ccr) {
+               hw_write_20kx(hw, SRC_CCR+idx*0x100, ctl->ccr);
+               ctl->dirty.bf.ccr = 0;
+       }
+       if (ctl->dirty.bf.ctl) {
+               hw_write_20kx(hw, SRC_CTL+idx*0x100, ctl->ctl);
+               ctl->dirty.bf.ctl = 0;
+       }
+
+       return 0;
+}
+
+static int src_get_ca(struct hw *hw, unsigned int idx, void *blk)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       ctl->ca = hw_read_20kx(hw, SRC_CA+idx*0x100);
+       ctl->dirty.bf.ca = 0;
+
+       return get_field(ctl->ca, SRCCA_CA);
+}
+
+static unsigned int src_get_dirty(void *blk)
+{
+       return ((struct src_rsc_ctrl_blk *)blk)->dirty.data;
+}
+
+static unsigned int src_dirty_conj_mask(void)
+{
+       return 0x20;
+}
+
+static int src_mgr_enbs_src(void *blk, unsigned int idx)
+{
+       ((struct src_mgr_ctrl_blk *)blk)->enbsa |= (0x1 << ((idx%128)/4));
+       ((struct src_mgr_ctrl_blk *)blk)->dirty.bf.enbsa = 1;
+       ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32));
+       return 0;
+}
+
+static int src_mgr_enb_src(void *blk, unsigned int idx)
+{
+       ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32));
+       ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32));
+       return 0;
+}
+
+static int src_mgr_dsb_src(void *blk, unsigned int idx)
+{
+       ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] &= ~(0x1 << (idx%32));
+       ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32));
+       return 0;
+}
+
+static int src_mgr_commit_write(struct hw *hw, void *blk)
+{
+       struct src_mgr_ctrl_blk *ctl = blk;
+       int i;
+       unsigned int ret;
+
+       if (ctl->dirty.bf.enbsa) {
+               do {
+                       ret = hw_read_20kx(hw, SRC_ENBSTAT);
+               } while (ret & 0x1);
+               hw_write_20kx(hw, SRC_ENBSA, ctl->enbsa);
+               ctl->dirty.bf.enbsa = 0;
+       }
+       for (i = 0; i < 8; i++) {
+               if ((ctl->dirty.data & (0x1 << i))) {
+                       hw_write_20kx(hw, SRC_ENB+(i*0x100), ctl->enb[i]);
+                       ctl->dirty.data &= ~(0x1 << i);
+               }
+       }
+
+       return 0;
+}
+
+static int src_mgr_get_ctrl_blk(void **rblk)
+{
+       struct src_mgr_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int src_mgr_put_ctrl_blk(void *blk)
+{
+       kfree(blk);
+
+       return 0;
+}
+
+static int srcimp_mgr_get_ctrl_blk(void **rblk)
+{
+       struct srcimp_mgr_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int srcimp_mgr_put_ctrl_blk(void *blk)
+{
+       kfree(blk);
+
+       return 0;
+}
+
+static int srcimp_mgr_set_imaparc(void *blk, unsigned int slot)
+{
+       struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srcimap.srcaim, SRCAIM_ARC, slot);
+       ctl->dirty.bf.srcimap = 1;
+       return 0;
+}
+
+static int srcimp_mgr_set_imapuser(void *blk, unsigned int user)
+{
+       struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srcimap.srcaim, SRCAIM_SRC, user);
+       ctl->dirty.bf.srcimap = 1;
+       return 0;
+}
+
+static int srcimp_mgr_set_imapnxt(void *blk, unsigned int next)
+{
+       struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srcimap.srcaim, SRCAIM_NXT, next);
+       ctl->dirty.bf.srcimap = 1;
+       return 0;
+}
+
+static int srcimp_mgr_set_imapaddr(void *blk, unsigned int addr)
+{
+       ((struct srcimp_mgr_ctrl_blk *)blk)->srcimap.idx = addr;
+       ((struct srcimp_mgr_ctrl_blk *)blk)->dirty.bf.srcimap = 1;
+       return 0;
+}
+
+static int srcimp_mgr_commit_write(struct hw *hw, void *blk)
+{
+       struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+       if (ctl->dirty.bf.srcimap) {
+               hw_write_20kx(hw, SRC_IMAP+ctl->srcimap.idx*0x100,
+                                               ctl->srcimap.srcaim);
+               ctl->dirty.bf.srcimap = 0;
+       }
+
+       return 0;
+}
+
+/*
+ * AMIXER control block definitions.
+ */
+
+#define AMOPLO_M       0x00000003
+#define AMOPLO_IV      0x00000004
+#define AMOPLO_X       0x0003FFF0
+#define AMOPLO_Y       0xFFFC0000
+
+#define AMOPHI_SADR    0x000000FF
+#define AMOPHI_SE      0x80000000
+
+/* AMIXER resource register dirty flags */
+union amixer_dirty {
+       struct {
+               u16 amoplo:1;
+               u16 amophi:1;
+               u16 rsv:14;
+       } bf;
+       u16 data;
+};
+
+/* AMIXER resource control block */
+struct amixer_rsc_ctrl_blk {
+       unsigned int            amoplo;
+       unsigned int            amophi;
+       union amixer_dirty      dirty;
+};
+
+static int amixer_set_mode(void *blk, unsigned int mode)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amoplo, AMOPLO_M, mode);
+       ctl->dirty.bf.amoplo = 1;
+       return 0;
+}
+
+static int amixer_set_iv(void *blk, unsigned int iv)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amoplo, AMOPLO_IV, iv);
+       ctl->dirty.bf.amoplo = 1;
+       return 0;
+}
+
+static int amixer_set_x(void *blk, unsigned int x)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amoplo, AMOPLO_X, x);
+       ctl->dirty.bf.amoplo = 1;
+       return 0;
+}
+
+static int amixer_set_y(void *blk, unsigned int y)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amoplo, AMOPLO_Y, y);
+       ctl->dirty.bf.amoplo = 1;
+       return 0;
+}
+
+static int amixer_set_sadr(void *blk, unsigned int sadr)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amophi, AMOPHI_SADR, sadr);
+       ctl->dirty.bf.amophi = 1;
+       return 0;
+}
+
+static int amixer_set_se(void *blk, unsigned int se)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amophi, AMOPHI_SE, se);
+       ctl->dirty.bf.amophi = 1;
+       return 0;
+}
+
+static int amixer_set_dirty(void *blk, unsigned int flags)
+{
+       ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff);
+       return 0;
+}
+
+static int amixer_set_dirty_all(void *blk)
+{
+       ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0);
+       return 0;
+}
+
+static int amixer_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       if (ctl->dirty.bf.amoplo || ctl->dirty.bf.amophi) {
+               hw_write_20kx(hw, MIXER_AMOPLO+idx*8, ctl->amoplo);
+               ctl->dirty.bf.amoplo = 0;
+               hw_write_20kx(hw, MIXER_AMOPHI+idx*8, ctl->amophi);
+               ctl->dirty.bf.amophi = 0;
+       }
+
+       return 0;
+}
+
+static int amixer_get_y(void *blk)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       return get_field(ctl->amoplo, AMOPLO_Y);
+}
+
+static unsigned int amixer_get_dirty(void *blk)
+{
+       return ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data;
+}
+
+static int amixer_rsc_get_ctrl_blk(void **rblk)
+{
+       struct amixer_rsc_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int amixer_rsc_put_ctrl_blk(void *blk)
+{
+       kfree(blk);
+
+       return 0;
+}
+
+static int amixer_mgr_get_ctrl_blk(void **rblk)
+{
+       *rblk = NULL;
+
+       return 0;
+}
+
+static int amixer_mgr_put_ctrl_blk(void *blk)
+{
+       return 0;
+}
+
+/*
+ * DAIO control block definitions.
+ */
+
+/* Receiver Sample Rate Tracker Control register */
+#define SRTCTL_SRCO    0x000000FF
+#define SRTCTL_SRCM    0x0000FF00
+#define SRTCTL_RSR     0x00030000
+#define SRTCTL_DRAT    0x00300000
+#define SRTCTL_EC      0x01000000
+#define SRTCTL_ET      0x10000000
+
+/* DAIO Receiver register dirty flags */
+union dai_dirty {
+       struct {
+               u16 srt:1;
+               u16 rsv:15;
+       } bf;
+       u16 data;
+};
+
+/* DAIO Receiver control block */
+struct dai_ctrl_blk {
+       unsigned int    srt;
+       union dai_dirty dirty;
+};
+
+/* Audio Input Mapper RAM */
+#define AIM_ARC                0x00000FFF
+#define AIM_NXT                0x007F0000
+
+struct daoimap {
+       unsigned int aim;
+       unsigned int idx;
+};
+
+/* Audio Transmitter Control and Status register */
+#define ATXCTL_EN      0x00000001
+#define ATXCTL_MODE    0x00000010
+#define ATXCTL_CD      0x00000020
+#define ATXCTL_RAW     0x00000100
+#define ATXCTL_MT      0x00000200
+#define ATXCTL_NUC     0x00003000
+#define ATXCTL_BEN     0x00010000
+#define ATXCTL_BMUX    0x00700000
+#define ATXCTL_B24     0x01000000
+#define ATXCTL_CPF     0x02000000
+#define ATXCTL_RIV     0x10000000
+#define ATXCTL_LIV     0x20000000
+#define ATXCTL_RSAT    0x40000000
+#define ATXCTL_LSAT    0x80000000
+
+/* XDIF Transmitter register dirty flags */
+union dao_dirty {
+       struct {
+               u16 atxcsl:1;
+               u16 rsv:15;
+       } bf;
+       u16 data;
+};
+
+/* XDIF Transmitter control block */
+struct dao_ctrl_blk {
+       /* XDIF Transmitter Channel Status Low Register */
+       unsigned int    atxcsl;
+       union dao_dirty dirty;
+};
+
+/* Audio Receiver Control register */
+#define ARXCTL_EN      0x00000001
+
+/* DAIO manager register dirty flags */
+union daio_mgr_dirty {
+       struct {
+               u32 atxctl:8;
+               u32 arxctl:8;
+               u32 daoimap:1;
+               u32 rsv:15;
+       } bf;
+       u32 data;
+};
+
+/* DAIO manager control block */
+struct daio_mgr_ctrl_blk {
+       struct daoimap          daoimap;
+       unsigned int            txctl[8];
+       unsigned int            rxctl[8];
+       union daio_mgr_dirty    dirty;
+};
+
+static int dai_srt_set_srco(void *blk, unsigned int src)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srt, SRTCTL_SRCO, src);
+       ctl->dirty.bf.srt = 1;
+       return 0;
+}
+
+static int dai_srt_set_srcm(void *blk, unsigned int src)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srt, SRTCTL_SRCM, src);
+       ctl->dirty.bf.srt = 1;
+       return 0;
+}
+
+static int dai_srt_set_rsr(void *blk, unsigned int rsr)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srt, SRTCTL_RSR, rsr);
+       ctl->dirty.bf.srt = 1;
+       return 0;
+}
+
+static int dai_srt_set_drat(void *blk, unsigned int drat)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srt, SRTCTL_DRAT, drat);
+       ctl->dirty.bf.srt = 1;
+       return 0;
+}
+
+static int dai_srt_set_ec(void *blk, unsigned int ec)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srt, SRTCTL_EC, ec ? 1 : 0);
+       ctl->dirty.bf.srt = 1;
+       return 0;
+}
+
+static int dai_srt_set_et(void *blk, unsigned int et)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srt, SRTCTL_ET, et ? 1 : 0);
+       ctl->dirty.bf.srt = 1;
+       return 0;
+}
+
+static int dai_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       if (ctl->dirty.bf.srt) {
+               hw_write_20kx(hw, AUDIO_IO_RX_SRT_CTL+0x40*idx, ctl->srt);
+               ctl->dirty.bf.srt = 0;
+       }
+
+       return 0;
+}
+
+static int dai_get_ctrl_blk(void **rblk)
+{
+       struct dai_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int dai_put_ctrl_blk(void *blk)
+{
+       kfree(blk);
+
+       return 0;
+}
+
+static int dao_set_spos(void *blk, unsigned int spos)
+{
+       ((struct dao_ctrl_blk *)blk)->atxcsl = spos;
+       ((struct dao_ctrl_blk *)blk)->dirty.bf.atxcsl = 1;
+       return 0;
+}
+
+static int dao_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+       struct dao_ctrl_blk *ctl = blk;
+
+       if (ctl->dirty.bf.atxcsl) {
+               if (idx < 4) {
+                       /* S/PDIF SPOSx */
+                       hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_L+0x40*idx,
+                                                       ctl->atxcsl);
+               }
+               ctl->dirty.bf.atxcsl = 0;
+       }
+
+       return 0;
+}
+
+static int dao_get_spos(void *blk, unsigned int *spos)
+{
+       *spos = ((struct dao_ctrl_blk *)blk)->atxcsl;
+       return 0;
+}
+
+static int dao_get_ctrl_blk(void **rblk)
+{
+       struct dao_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int dao_put_ctrl_blk(void *blk)
+{
+       kfree(blk);
+
+       return 0;
+}
+
+static int daio_mgr_enb_dai(void *blk, unsigned int idx)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->rxctl[idx], ARXCTL_EN, 1);
+       ctl->dirty.bf.arxctl |= (0x1 << idx);
+       return 0;
+}
+
+static int daio_mgr_dsb_dai(void *blk, unsigned int idx)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->rxctl[idx], ARXCTL_EN, 0);
+
+       ctl->dirty.bf.arxctl |= (0x1 << idx);
+       return 0;
+}
+
+static int daio_mgr_enb_dao(void *blk, unsigned int idx)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->txctl[idx], ATXCTL_EN, 1);
+       ctl->dirty.bf.atxctl |= (0x1 << idx);
+       return 0;
+}
+
+static int daio_mgr_dsb_dao(void *blk, unsigned int idx)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->txctl[idx], ATXCTL_EN, 0);
+       ctl->dirty.bf.atxctl |= (0x1 << idx);
+       return 0;
+}
+
+static int daio_mgr_dao_init(void *blk, unsigned int idx, unsigned int conf)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       if (idx < 4) {
+               /* S/PDIF output */
+               switch ((conf & 0x7)) {
+               case 1:
+                       set_field(&ctl->txctl[idx], ATXCTL_NUC, 0);
+                       break;
+               case 2:
+                       set_field(&ctl->txctl[idx], ATXCTL_NUC, 1);
+                       break;
+               case 4:
+                       set_field(&ctl->txctl[idx], ATXCTL_NUC, 2);
+                       break;
+               case 8:
+                       set_field(&ctl->txctl[idx], ATXCTL_NUC, 3);
+                       break;
+               default:
+                       break;
+               }
+               /* CDIF */
+               set_field(&ctl->txctl[idx], ATXCTL_CD, (!(conf & 0x7)));
+               /* Non-audio */
+               set_field(&ctl->txctl[idx], ATXCTL_LIV, (conf >> 4) & 0x1);
+               /* Non-audio */
+               set_field(&ctl->txctl[idx], ATXCTL_RIV, (conf >> 4) & 0x1);
+               set_field(&ctl->txctl[idx], ATXCTL_RAW,
+                         ((conf >> 3) & 0x1) ? 0 : 0);
+               ctl->dirty.bf.atxctl |= (0x1 << idx);
+       } else {
+               /* I2S output */
+               /*idx %= 4; */
+       }
+       return 0;
+}
+
+static int daio_mgr_set_imaparc(void *blk, unsigned int slot)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->daoimap.aim, AIM_ARC, slot);
+       ctl->dirty.bf.daoimap = 1;
+       return 0;
+}
+
+static int daio_mgr_set_imapnxt(void *blk, unsigned int next)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->daoimap.aim, AIM_NXT, next);
+       ctl->dirty.bf.daoimap = 1;
+       return 0;
+}
+
+static int daio_mgr_set_imapaddr(void *blk, unsigned int addr)
+{
+       ((struct daio_mgr_ctrl_blk *)blk)->daoimap.idx = addr;
+       ((struct daio_mgr_ctrl_blk *)blk)->dirty.bf.daoimap = 1;
+       return 0;
+}
+
+static int daio_mgr_commit_write(struct hw *hw, void *blk)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+       unsigned int data;
+       int i;
+
+       for (i = 0; i < 8; i++) {
+               if ((ctl->dirty.bf.atxctl & (0x1 << i))) {
+                       data = ctl->txctl[i];
+                       hw_write_20kx(hw, (AUDIO_IO_TX_CTL+(0x40*i)), data);
+                       ctl->dirty.bf.atxctl &= ~(0x1 << i);
+                       mdelay(1);
+               }
+               if ((ctl->dirty.bf.arxctl & (0x1 << i))) {
+                       data = ctl->rxctl[i];
+                       hw_write_20kx(hw, (AUDIO_IO_RX_CTL+(0x40*i)), data);
+                       ctl->dirty.bf.arxctl &= ~(0x1 << i);
+                       mdelay(1);
+               }
+       }
+       if (ctl->dirty.bf.daoimap) {
+               hw_write_20kx(hw, AUDIO_IO_AIM+ctl->daoimap.idx*4,
+                                               ctl->daoimap.aim);
+               ctl->dirty.bf.daoimap = 0;
+       }
+
+       return 0;
+}
+
+static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk)
+{
+       struct daio_mgr_ctrl_blk *blk;
+       int i;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       for (i = 0; i < 8; i++) {
+               blk->txctl[i] = hw_read_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i));
+               blk->rxctl[i] = hw_read_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i));
+       }
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int daio_mgr_put_ctrl_blk(void *blk)
+{
+       kfree(blk);
+
+       return 0;
+}
+
+/* Card hardware initialization block */
+struct dac_conf {
+       unsigned int msr; /* master sample rate in rsrs */
+};
+
+struct adc_conf {
+       unsigned int msr;       /* master sample rate in rsrs */
+       unsigned char input;    /* the input source of ADC */
+       unsigned char mic20db;  /* boost mic by 20db if input is microphone */
+};
+
+struct daio_conf {
+       unsigned int msr; /* master sample rate in rsrs */
+};
+
+struct trn_conf {
+       unsigned long vm_pgt_phys;
+};
+
+static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
+{
+       u32 data;
+       int i;
+
+       /* Program I2S with proper sample rate and enable the correct I2S
+        * channel. ED(0/8/16/24): Enable all I2S/I2X master clock output */
+       if (1 == info->msr) {
+               hw_write_20kx(hw, AUDIO_IO_MCLK, 0x01010101);
+               hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x01010101);
+               hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
+       } else if (2 == info->msr) {
+               hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11111111);
+               /* Specify all playing 96khz
+                * EA [0]       - Enabled
+                * RTA [4:5]    - 96kHz
+                * EB [8]       - Enabled
+                * RTB [12:13]  - 96kHz
+                * EC [16]      - Enabled
+                * RTC [20:21]  - 96kHz
+                * ED [24]      - Enabled
+                * RTD [28:29]  - 96kHz */
+               hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x11111111);
+               hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
+       } else {
+               printk(KERN_ALERT "ctxfi: ERROR!!! Invalid sampling rate!!!\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < 8; i++) {
+               if (i <= 3) {
+                       /* 1st 3 channels are SPDIFs (SB0960) */
+                       if (i == 3)
+                               data = 0x1001001;
+                       else
+                               data = 0x1000001;
+
+                       hw_write_20kx(hw, (AUDIO_IO_TX_CTL+(0x40*i)), data);
+                       hw_write_20kx(hw, (AUDIO_IO_RX_CTL+(0x40*i)), data);
+
+                       /* Initialize the SPDIF Out Channel status registers.
+                        * The value specified here is based on the typical
+                        * values provided in the specification, namely: Clock
+                        * Accuracy of 1000ppm, Sample Rate of 48KHz,
+                        * unspecified source number, Generation status = 1,
+                        * Category code = 0x12 (Digital Signal Mixer),
+                        * Mode = 0, Emph = 0, Copy Permitted, AN = 0
+                        * (indicating that we're transmitting digital audio,
+                        * and the Professional Use bit is 0. */
+
+                       hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_L+(0x40*i),
+                                       0x02109204); /* Default to 48kHz */
+
+                       hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_H+(0x40*i), 0x0B);
+               } else {
+                       /* Next 5 channels are I2S (SB0960) */
+                       data = 0x11;
+                       hw_write_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i), data);
+                       if (2 == info->msr) {
+                               /* Four channels per sample period */
+                               data |= 0x1000;
+                       }
+                       hw_write_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i), data);
+               }
+       }
+
+       return 0;
+}
+
+/* TRANSPORT operations */
+static int hw_trn_init(struct hw *hw, const struct trn_conf *info)
+{
+       u32 vmctl, data;
+       u32 ptp_phys_low, ptp_phys_high;
+       int i;
+
+       /* Set up device page table */
+       if ((~0UL) == info->vm_pgt_phys) {
+               printk(KERN_ALERT "ctxfi: "
+                      "Wrong device page table page address!!!\n");
+               return -1;
+       }
+
+       vmctl = 0x80000C0F;  /* 32-bit, 4k-size page */
+       ptp_phys_low = (u32)info->vm_pgt_phys;
+       ptp_phys_high = upper_32_bits(info->vm_pgt_phys);
+       if (sizeof(void *) == 8) /* 64bit address */
+               vmctl |= (3 << 8);
+       /* Write page table physical address to all PTPAL registers */
+       for (i = 0; i < 64; i++) {
+               hw_write_20kx(hw, VMEM_PTPAL+(16*i), ptp_phys_low);
+               hw_write_20kx(hw, VMEM_PTPAH+(16*i), ptp_phys_high);
+       }
+       /* Enable virtual memory transfer */
+       hw_write_20kx(hw, VMEM_CTL, vmctl);
+       /* Enable transport bus master and queueing of request */
+       hw_write_20kx(hw, TRANSPORT_CTL, 0x03);
+       hw_write_20kx(hw, TRANSPORT_INT, 0x200c01);
+       /* Enable transport ring */
+       data = hw_read_20kx(hw, TRANSPORT_ENB);
+       hw_write_20kx(hw, TRANSPORT_ENB, (data | 0x03));
+
+       return 0;
+}
+
+/* Card initialization */
+#define GCTL_AIE       0x00000001
+#define GCTL_UAA       0x00000002
+#define GCTL_DPC       0x00000004
+#define GCTL_DBP       0x00000008
+#define GCTL_ABP       0x00000010
+#define GCTL_TBP       0x00000020
+#define GCTL_SBP       0x00000040
+#define GCTL_FBP       0x00000080
+#define GCTL_ME                0x00000100
+#define GCTL_AID       0x00001000
+
+#define PLLCTL_SRC     0x00000007
+#define PLLCTL_SPE     0x00000008
+#define PLLCTL_RD      0x000000F0
+#define PLLCTL_FD      0x0001FF00
+#define PLLCTL_OD      0x00060000
+#define PLLCTL_B       0x00080000
+#define PLLCTL_AS      0x00100000
+#define PLLCTL_LF      0x03E00000
+#define PLLCTL_SPS     0x1C000000
+#define PLLCTL_AD      0x60000000
+
+#define PLLSTAT_CCS    0x00000007
+#define PLLSTAT_SPL    0x00000008
+#define PLLSTAT_CRD    0x000000F0
+#define PLLSTAT_CFD    0x0001FF00
+#define PLLSTAT_SL     0x00020000
+#define PLLSTAT_FAS    0x00040000
+#define PLLSTAT_B      0x00080000
+#define PLLSTAT_PD     0x00100000
+#define PLLSTAT_OCA    0x00200000
+#define PLLSTAT_NCA    0x00400000
+
+static int hw_pll_init(struct hw *hw, unsigned int rsr)
+{
+       unsigned int pllenb;
+       unsigned int pllctl;
+       unsigned int pllstat;
+       int i;
+
+       pllenb = 0xB;
+       hw_write_20kx(hw, PLL_ENB, pllenb);
+       pllctl = 0x20D00000;
+       set_field(&pllctl, PLLCTL_FD, 16 - 4);
+       hw_write_20kx(hw, PLL_CTL, pllctl);
+       mdelay(40);
+       pllctl = hw_read_20kx(hw, PLL_CTL);
+       set_field(&pllctl, PLLCTL_B, 0);
+       if (48000 == rsr) {
+               set_field(&pllctl, PLLCTL_FD, 16 - 2);
+               set_field(&pllctl, PLLCTL_RD, 1 - 1);
+       } else { /* 44100 */
+               set_field(&pllctl, PLLCTL_FD, 147 - 2);
+               set_field(&pllctl, PLLCTL_RD, 10 - 1);
+       }
+       hw_write_20kx(hw, PLL_CTL, pllctl);
+       mdelay(40);
+       for (i = 0; i < 1000; i++) {
+               pllstat = hw_read_20kx(hw, PLL_STAT);
+               if (get_field(pllstat, PLLSTAT_PD))
+                       continue;
+
+               if (get_field(pllstat, PLLSTAT_B) !=
+                                       get_field(pllctl, PLLCTL_B))
+                       continue;
+
+               if (get_field(pllstat, PLLSTAT_CCS) !=
+                                       get_field(pllctl, PLLCTL_SRC))
+                       continue;
+
+               if (get_field(pllstat, PLLSTAT_CRD) !=
+                                       get_field(pllctl, PLLCTL_RD))
+                       continue;
+
+               if (get_field(pllstat, PLLSTAT_CFD) !=
+                                       get_field(pllctl, PLLCTL_FD))
+                       continue;
+
+               break;
+       }
+       if (i >= 1000) {
+               printk(KERN_ALERT "ctxfi: PLL initialization failed!!!\n");
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+static int hw_auto_init(struct hw *hw)
+{
+       unsigned int gctl;
+       int i;
+
+       gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL);
+       set_field(&gctl, GCTL_AIE, 0);
+       hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
+       set_field(&gctl, GCTL_AIE, 1);
+       hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
+       mdelay(10);
+       for (i = 0; i < 400000; i++) {
+               gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL);
+               if (get_field(gctl, GCTL_AID))
+                       break;
+       }
+       if (!get_field(gctl, GCTL_AID)) {
+               printk(KERN_ALERT "ctxfi: Card Auto-init failed!!!\n");
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+/* DAC operations */
+
+#define CS4382_MC1             0x1
+#define CS4382_MC2             0x2
+#define CS4382_MC3             0x3
+#define CS4382_FC              0x4
+#define CS4382_IC              0x5
+#define CS4382_XC1             0x6
+#define CS4382_VCA1            0x7
+#define CS4382_VCB1            0x8
+#define CS4382_XC2             0x9
+#define CS4382_VCA2            0xA
+#define CS4382_VCB2            0xB
+#define CS4382_XC3             0xC
+#define CS4382_VCA3            0xD
+#define CS4382_VCB3            0xE
+#define CS4382_XC4             0xF
+#define CS4382_VCA4            0x10
+#define CS4382_VCB4            0x11
+#define CS4382_CREV            0x12
+
+/* I2C status */
+#define STATE_LOCKED           0x00
+#define STATE_UNLOCKED         0xAA
+#define DATA_READY             0x800000    /* Used with I2C_IF_STATUS */
+#define DATA_ABORT             0x10000     /* Used with I2C_IF_STATUS */
+
+#define I2C_STATUS_DCM 0x00000001
+#define I2C_STATUS_BC  0x00000006
+#define I2C_STATUS_APD 0x00000008
+#define I2C_STATUS_AB  0x00010000
+#define I2C_STATUS_DR  0x00800000
+
+#define I2C_ADDRESS_PTAD       0x0000FFFF
+#define I2C_ADDRESS_SLAD       0x007F0000
+
+struct regs_cs4382 {
+       u32 mode_control_1;
+       u32 mode_control_2;
+       u32 mode_control_3;
+
+       u32 filter_control;
+       u32 invert_control;
+
+       u32 mix_control_P1;
+       u32 vol_control_A1;
+       u32 vol_control_B1;
+
+       u32 mix_control_P2;
+       u32 vol_control_A2;
+       u32 vol_control_B2;
+
+       u32 mix_control_P3;
+       u32 vol_control_A3;
+       u32 vol_control_B3;
+
+       u32 mix_control_P4;
+       u32 vol_control_A4;
+       u32 vol_control_B4;
+};
+
+static int hw20k2_i2c_unlock_full_access(struct hw *hw)
+{
+       u8 UnlockKeySequence_FLASH_FULLACCESS_MODE[2] =  {0xB3, 0xD4};
+
+       /* Send keys for forced BIOS mode */
+       hw_write_20kx(hw, I2C_IF_WLOCK,
+                       UnlockKeySequence_FLASH_FULLACCESS_MODE[0]);
+       hw_write_20kx(hw, I2C_IF_WLOCK,
+                       UnlockKeySequence_FLASH_FULLACCESS_MODE[1]);
+       /* Check whether the chip is unlocked */
+       if (hw_read_20kx(hw, I2C_IF_WLOCK) == STATE_UNLOCKED)
+               return 0;
+
+       return -1;
+}
+
+static int hw20k2_i2c_lock_chip(struct hw *hw)
+{
+       /* Write twice */
+       hw_write_20kx(hw, I2C_IF_WLOCK, STATE_LOCKED);
+       hw_write_20kx(hw, I2C_IF_WLOCK, STATE_LOCKED);
+       if (hw_read_20kx(hw, I2C_IF_WLOCK) == STATE_LOCKED)
+               return 0;
+
+       return -1;
+}
+
+static int hw20k2_i2c_init(struct hw *hw, u8 dev_id, u8 addr_size, u8 data_size)
+{
+       struct hw20k2 *hw20k2 = (struct hw20k2 *)hw;
+       int err;
+       unsigned int i2c_status;
+       unsigned int i2c_addr;
+
+       err = hw20k2_i2c_unlock_full_access(hw);
+       if (err < 0)
+               return err;
+
+       hw20k2->addr_size = addr_size;
+       hw20k2->data_size = data_size;
+       hw20k2->dev_id = dev_id;
+
+       i2c_addr = 0;
+       set_field(&i2c_addr, I2C_ADDRESS_SLAD, dev_id);
+
+       hw_write_20kx(hw, I2C_IF_ADDRESS, i2c_addr);
+
+       i2c_status = hw_read_20kx(hw, I2C_IF_STATUS);
+
+       set_field(&i2c_status, I2C_STATUS_DCM, 1); /* Direct control mode */
+
+       hw_write_20kx(hw, I2C_IF_STATUS, i2c_status);
+
+       return 0;
+}
+
+static int hw20k2_i2c_uninit(struct hw *hw)
+{
+       unsigned int i2c_status;
+       unsigned int i2c_addr;
+
+       i2c_addr = 0;
+       set_field(&i2c_addr, I2C_ADDRESS_SLAD, 0x57); /* I2C id */
+
+       hw_write_20kx(hw, I2C_IF_ADDRESS, i2c_addr);
+
+       i2c_status = hw_read_20kx(hw, I2C_IF_STATUS);
+
+       set_field(&i2c_status, I2C_STATUS_DCM, 0); /* I2C mode */
+
+       hw_write_20kx(hw, I2C_IF_STATUS, i2c_status);
+
+       return hw20k2_i2c_lock_chip(hw);
+}
+
+static int hw20k2_i2c_wait_data_ready(struct hw *hw)
+{
+       int i = 0x400000;
+       unsigned int ret;
+
+       do {
+               ret = hw_read_20kx(hw, I2C_IF_STATUS);
+       } while ((!(ret & DATA_READY)) && --i);
+
+       return i;
+}
+
+static int hw20k2_i2c_read(struct hw *hw, u16 addr, u32 *datap)
+{
+       struct hw20k2 *hw20k2 = (struct hw20k2 *)hw;
+       unsigned int i2c_status;
+
+       i2c_status = hw_read_20kx(hw, I2C_IF_STATUS);
+       set_field(&i2c_status, I2C_STATUS_BC,
+                 (4 == hw20k2->addr_size) ? 0 : hw20k2->addr_size);
+       hw_write_20kx(hw, I2C_IF_STATUS, i2c_status);
+       if (!hw20k2_i2c_wait_data_ready(hw))
+               return -1;
+
+       hw_write_20kx(hw, I2C_IF_WDATA, addr);
+       if (!hw20k2_i2c_wait_data_ready(hw))
+               return -1;
+
+       /* Force a read operation */
+       hw_write_20kx(hw, I2C_IF_RDATA, 0);
+       if (!hw20k2_i2c_wait_data_ready(hw))
+               return -1;
+
+       *datap = hw_read_20kx(hw, I2C_IF_RDATA);
+
+       return 0;
+}
+
+static int hw20k2_i2c_write(struct hw *hw, u16 addr, u32 data)
+{
+       struct hw20k2 *hw20k2 = (struct hw20k2 *)hw;
+       unsigned int i2c_data = (data << (hw20k2->addr_size * 8)) | addr;
+       unsigned int i2c_status;
+
+       i2c_status = hw_read_20kx(hw, I2C_IF_STATUS);
+
+       set_field(&i2c_status, I2C_STATUS_BC,
+                 (4 == (hw20k2->addr_size + hw20k2->data_size)) ?
+                 0 : (hw20k2->addr_size + hw20k2->data_size));
+
+       hw_write_20kx(hw, I2C_IF_STATUS, i2c_status);
+       hw20k2_i2c_wait_data_ready(hw);
+       /* Dummy write to trigger the write oprtation */
+       hw_write_20kx(hw, I2C_IF_WDATA, 0);
+       hw20k2_i2c_wait_data_ready(hw);
+
+       /* This is the real data */
+       hw_write_20kx(hw, I2C_IF_WDATA, i2c_data);
+       hw20k2_i2c_wait_data_ready(hw);
+
+       return 0;
+}
+
+static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
+{
+       int err;
+       u32 data;
+       int i;
+       struct regs_cs4382 cs_read = {0};
+       struct regs_cs4382 cs_def = {
+                                  0x00000001,  /* Mode Control 1 */
+                                  0x00000000,  /* Mode Control 2 */
+                                  0x00000084,  /* Mode Control 3 */
+                                  0x00000000,  /* Filter Control */
+                                  0x00000000,  /* Invert Control */
+                                  0x00000024,  /* Mixing Control Pair 1 */
+                                  0x00000000,  /* Vol Control A1 */
+                                  0x00000000,  /* Vol Control B1 */
+                                  0x00000024,  /* Mixing Control Pair 2 */
+                                  0x00000000,  /* Vol Control A2 */
+                                  0x00000000,  /* Vol Control B2 */
+                                  0x00000024,  /* Mixing Control Pair 3 */
+                                  0x00000000,  /* Vol Control A3 */
+                                  0x00000000,  /* Vol Control B3 */
+                                  0x00000024,  /* Mixing Control Pair 4 */
+                                  0x00000000,  /* Vol Control A4 */
+                                  0x00000000   /* Vol Control B4 */
+                                };
+
+       /* Set DAC reset bit as output */
+       data = hw_read_20kx(hw, GPIO_CTRL);
+       data |= 0x02;
+       hw_write_20kx(hw, GPIO_CTRL, data);
+
+       err = hw20k2_i2c_init(hw, 0x18, 1, 1);
+       if (err < 0)
+               goto End;
+
+       for (i = 0; i < 2; i++) {
+               /* Reset DAC twice just in-case the chip
+                * didn't initialized properly */
+               data = hw_read_20kx(hw, GPIO_DATA);
+               /* GPIO data bit 1 */
+               data &= 0xFFFFFFFD;
+               hw_write_20kx(hw, GPIO_DATA, data);
+               mdelay(10);
+               data |= 0x2;
+               hw_write_20kx(hw, GPIO_DATA, data);
+               mdelay(50);
+
+               /* Reset the 2nd time */
+               data &= 0xFFFFFFFD;
+               hw_write_20kx(hw, GPIO_DATA, data);
+               mdelay(10);
+               data |= 0x2;
+               hw_write_20kx(hw, GPIO_DATA, data);
+               mdelay(50);
+
+               if (hw20k2_i2c_read(hw, CS4382_MC1,  &cs_read.mode_control_1))
+                       continue;
+
+               if (hw20k2_i2c_read(hw, CS4382_MC2,  &cs_read.mode_control_2))
+                       continue;
+
+               if (hw20k2_i2c_read(hw, CS4382_MC3,  &cs_read.mode_control_3))
+                       continue;
+
+               if (hw20k2_i2c_read(hw, CS4382_FC,   &cs_read.filter_control))
+                       continue;
+
+               if (hw20k2_i2c_read(hw, CS4382_IC,   &cs_read.invert_control))
+                       continue;
+
+               if (hw20k2_i2c_read(hw, CS4382_XC1,  &cs_read.mix_control_P1))
+                       continue;
+
+               if (hw20k2_i2c_read(hw, CS4382_VCA1, &cs_read.vol_control_A1))
+                       continue;
+
+               if (hw20k2_i2c_read(hw, CS4382_VCB1, &cs_read.vol_control_B1))
+                       continue;
+
+               if (hw20k2_i2c_read(hw, CS4382_XC2,  &cs_read.mix_control_P2))
+                       continue;
+
+               if (hw20k2_i2c_read(hw, CS4382_VCA2, &cs_read.vol_control_A2))
+                       continue;
+
+               if (hw20k2_i2c_read(hw, CS4382_VCB2, &cs_read.vol_control_B2))
+                       continue;
+
+               if (hw20k2_i2c_read(hw, CS4382_XC3,  &cs_read.mix_control_P3))
+                       continue;
+
+               if (hw20k2_i2c_read(hw, CS4382_VCA3, &cs_read.vol_control_A3))
+                       continue;
+
+               if (hw20k2_i2c_read(hw, CS4382_VCB3, &cs_read.vol_control_B3))
+                       continue;
+
+               if (hw20k2_i2c_read(hw, CS4382_XC4,  &cs_read.mix_control_P4))
+                       continue;
+
+               if (hw20k2_i2c_read(hw, CS4382_VCA4, &cs_read.vol_control_A4))
+                       continue;
+
+               if (hw20k2_i2c_read(hw, CS4382_VCB4, &cs_read.vol_control_B4))
+                       continue;
+
+               if (memcmp(&cs_read, &cs_def, sizeof(cs_read)))
+                       continue;
+               else
+                       break;
+       }
+
+       if (i >= 2)
+               goto End;
+
+       /* Note: Every I2C write must have some delay.
+        * This is not a requirement but the delay works here... */
+       hw20k2_i2c_write(hw, CS4382_MC1, 0x80);
+       hw20k2_i2c_write(hw, CS4382_MC2, 0x10);
+       if (1 == info->msr) {
+               hw20k2_i2c_write(hw, CS4382_XC1, 0x24);
+               hw20k2_i2c_write(hw, CS4382_XC2, 0x24);
+               hw20k2_i2c_write(hw, CS4382_XC3, 0x24);
+               hw20k2_i2c_write(hw, CS4382_XC4, 0x24);
+       } else if (2 == info->msr) {
+               hw20k2_i2c_write(hw, CS4382_XC1, 0x25);
+               hw20k2_i2c_write(hw, CS4382_XC2, 0x25);
+               hw20k2_i2c_write(hw, CS4382_XC3, 0x25);
+               hw20k2_i2c_write(hw, CS4382_XC4, 0x25);
+       } else {
+               hw20k2_i2c_write(hw, CS4382_XC1, 0x26);
+               hw20k2_i2c_write(hw, CS4382_XC2, 0x26);
+               hw20k2_i2c_write(hw, CS4382_XC3, 0x26);
+               hw20k2_i2c_write(hw, CS4382_XC4, 0x26);
+       }
+
+       return 0;
+End:
+
+       hw20k2_i2c_uninit(hw);
+       return -1;
+}
+
+/* ADC operations */
+#define MAKE_WM8775_ADDR(addr, data)   (u32)(((addr<<1)&0xFE)|((data>>8)&0x1))
+#define MAKE_WM8775_DATA(data) (u32)(data&0xFF)
+
+#define WM8775_IC       0x0B
+#define WM8775_MMC      0x0C
+#define WM8775_AADCL    0x0E
+#define WM8775_AADCR    0x0F
+#define WM8775_ADCMC    0x15
+#define WM8775_RESET    0x17
+
+static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type)
+{
+       u32 data;
+
+       data = hw_read_20kx(hw, GPIO_DATA);
+       switch (type) {
+       case ADC_MICIN:
+               data = (data & (0x1 << 14)) ? 1 : 0;
+               break;
+       case ADC_LINEIN:
+               data = (data & (0x1 << 14)) ? 0 : 1;
+               break;
+       default:
+               data = 0;
+       }
+       return data;
+}
+
+static int hw_adc_input_select(struct hw *hw, enum ADCSRC type)
+{
+       u32 data;
+
+       data = hw_read_20kx(hw, GPIO_DATA);
+       switch (type) {
+       case ADC_MICIN:
+               data |= (0x1 << 14);
+               hw_write_20kx(hw, GPIO_DATA, data);
+               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101),
+                               MAKE_WM8775_DATA(0x101)); /* Mic-in */
+               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7),
+                               MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
+               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7),
+                               MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
+               break;
+       case ADC_LINEIN:
+               data &= ~(0x1 << 14);
+               hw_write_20kx(hw, GPIO_DATA, data);
+               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102),
+                               MAKE_WM8775_DATA(0x102)); /* Line-in */
+               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF),
+                               MAKE_WM8775_DATA(0xCF)); /* No boost */
+               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF),
+                               MAKE_WM8775_DATA(0xCF)); /* No boost */
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
+{
+       int err;
+       u32 mux = 2, data, ctl;
+
+       /*  Set ADC reset bit as output */
+       data = hw_read_20kx(hw, GPIO_CTRL);
+       data |= (0x1 << 15);
+       hw_write_20kx(hw, GPIO_CTRL, data);
+
+       /* Initialize I2C */
+       err = hw20k2_i2c_init(hw, 0x1A, 1, 1);
+       if (err < 0) {
+               printk(KERN_ALERT "ctxfi: Failure to acquire I2C!!!\n");
+               goto error;
+       }
+
+       /* Make ADC in normal operation */
+       data = hw_read_20kx(hw, GPIO_DATA);
+       data &= ~(0x1 << 15);
+       mdelay(10);
+       data |= (0x1 << 15);
+       hw_write_20kx(hw, GPIO_DATA, data);
+       mdelay(50);
+
+       /* Set the master mode (256fs) */
+       if (1 == info->msr) {
+               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x02),
+                                               MAKE_WM8775_DATA(0x02));
+       } else if (2 == info->msr) {
+               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A),
+                                               MAKE_WM8775_DATA(0x0A));
+       } else {
+               printk(KERN_ALERT "ctxfi: Invalid master sampling "
+                                 "rate (msr %d)!!!\n", info->msr);
+               err = -EINVAL;
+               goto error;
+       }
+
+       /* Configure GPIO bit 14 change to line-in/mic-in */
+       ctl = hw_read_20kx(hw, GPIO_CTRL);
+       ctl |= 0x1 << 14;
+       hw_write_20kx(hw, GPIO_CTRL, ctl);
+
+       /* Check using Mic-in or Line-in */
+       data = hw_read_20kx(hw, GPIO_DATA);
+
+       if (mux == 1) {
+               /* Configures GPIO data to select Mic-in */
+               data |= 0x1 << 14;
+               hw_write_20kx(hw, GPIO_DATA, data);
+
+               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101),
+                               MAKE_WM8775_DATA(0x101)); /* Mic-in */
+               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7),
+                               MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
+               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7),
+                               MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
+       } else if (mux == 2) {
+               /* Configures GPIO data to select Line-in */
+               data &= ~(0x1 << 14);
+               hw_write_20kx(hw, GPIO_DATA, data);
+
+               /* Setup ADC */
+               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102),
+                               MAKE_WM8775_DATA(0x102)); /* Line-in */
+               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF),
+                               MAKE_WM8775_DATA(0xCF)); /* No boost */
+               hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF),
+                               MAKE_WM8775_DATA(0xCF)); /* No boost */
+       } else {
+               printk(KERN_ALERT "ctxfi: ERROR!!! Invalid input mux!!!\n");
+               err = -EINVAL;
+               goto error;
+       }
+
+       return 0;
+
+error:
+       hw20k2_i2c_uninit(hw);
+       return err;
+}
+
+static int hw_have_digit_io_switch(struct hw *hw)
+{
+       return 0;
+}
+
+static int hw_card_start(struct hw *hw)
+{
+       int err = 0;
+       struct pci_dev *pci = hw->pci;
+       unsigned int gctl;
+
+       err = pci_enable_device(pci);
+       if (err < 0)
+               return err;
+
+       /* Set DMA transfer mask */
+       if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 ||
+           pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) {
+               printk(KERN_ERR "ctxfi: architecture does not support PCI "
+               "busmaster DMA with mask 0x%llx\n", CT_XFI_DMA_MASK);
+               err = -ENXIO;
+               goto error1;
+       }
+
+       err = pci_request_regions(pci, "XFi");
+       if (err < 0)
+               goto error1;
+
+       hw->io_base = pci_resource_start(hw->pci, 2);
+       hw->mem_base = (unsigned long)ioremap(hw->io_base,
+                                       pci_resource_len(hw->pci, 2));
+       if (NULL == (void *)hw->mem_base) {
+               err = -ENOENT;
+               goto error2;
+       }
+
+       /* Switch to 20k2 mode from UAA mode. */
+       gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL);
+       set_field(&gctl, GCTL_UAA, 0);
+       hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
+
+       /*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED,
+                               atc->chip_details->nm_card, hw))) {
+               goto error3;
+       }
+       hw->irq = pci->irq;
+       */
+
+       pci_set_master(pci);
+
+       return 0;
+
+/*error3:
+       iounmap((void *)hw->mem_base);
+       hw->mem_base = (unsigned long)NULL;*/
+error2:
+       pci_release_regions(pci);
+       hw->io_base = 0;
+error1:
+       pci_disable_device(pci);
+       return err;
+}
+
+static int hw_card_stop(struct hw *hw)
+{
+       /* TODO: Disable interrupt and so on... */
+       return 0;
+}
+
+static int hw_card_shutdown(struct hw *hw)
+{
+       if (hw->irq >= 0)
+               free_irq(hw->irq, hw);
+
+       hw->irq = -1;
+
+       if (NULL != ((void *)hw->mem_base))
+               iounmap((void *)hw->mem_base);
+
+       hw->mem_base = (unsigned long)NULL;
+
+       if (hw->io_base)
+               pci_release_regions(hw->pci);
+
+       hw->io_base = 0;
+
+       pci_disable_device(hw->pci);
+
+       return 0;
+}
+
+static int hw_card_init(struct hw *hw, struct card_conf *info)
+{
+       int err;
+       unsigned int gctl;
+       u32 data = 0;
+       struct dac_conf dac_info = {0};
+       struct adc_conf adc_info = {0};
+       struct daio_conf daio_info = {0};
+       struct trn_conf trn_info = {0};
+
+       /* Get PCI io port/memory base address and
+        * do 20kx core switch if needed. */
+       if (!hw->io_base) {
+               err = hw_card_start(hw);
+               if (err)
+                       return err;
+       }
+
+       /* PLL init */
+       err = hw_pll_init(hw, info->rsr);
+       if (err < 0)
+               return err;
+
+       /* kick off auto-init */
+       err = hw_auto_init(hw);
+       if (err < 0)
+               return err;
+
+       gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL);
+       set_field(&gctl, GCTL_DBP, 1);
+       set_field(&gctl, GCTL_TBP, 1);
+       set_field(&gctl, GCTL_FBP, 1);
+       set_field(&gctl, GCTL_DPC, 0);
+       hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
+
+       /* Reset all global pending interrupts */
+       hw_write_20kx(hw, INTERRUPT_GIE, 0);
+       /* Reset all SRC pending interrupts */
+       hw_write_20kx(hw, SRC_IP, 0);
+
+       /* TODO: detect the card ID and configure GPIO accordingly. */
+       /* Configures GPIO (0xD802 0x98028) */
+       /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/
+       /* Configures GPIO (SB0880) */
+       /*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/
+       hw_write_20kx(hw, GPIO_CTRL, 0xD802);
+
+       /* Enable audio ring */
+       hw_write_20kx(hw, MIXER_AR_ENABLE, 0x01);
+
+       trn_info.vm_pgt_phys = info->vm_pgt_phys;
+       err = hw_trn_init(hw, &trn_info);
+       if (err < 0)
+               return err;
+
+       daio_info.msr = info->msr;
+       err = hw_daio_init(hw, &daio_info);
+       if (err < 0)
+               return err;
+
+       dac_info.msr = info->msr;
+       err = hw_dac_init(hw, &dac_info);
+       if (err < 0)
+               return err;
+
+       adc_info.msr = info->msr;
+       adc_info.input = ADC_LINEIN;
+       adc_info.mic20db = 0;
+       err = hw_adc_init(hw, &adc_info);
+       if (err < 0)
+               return err;
+
+       data = hw_read_20kx(hw, SRC_MCTL);
+       data |= 0x1; /* Enables input from the audio ring */
+       hw_write_20kx(hw, SRC_MCTL, data);
+
+       return 0;
+}
+
+static u32 hw_read_20kx(struct hw *hw, u32 reg)
+{
+       return readl((void *)(hw->mem_base + reg));
+}
+
+static void hw_write_20kx(struct hw *hw, u32 reg, u32 data)
+{
+       writel(data, (void *)(hw->mem_base + reg));
+}
+
+static struct hw ct20k2_preset __devinitdata = {
+       .irq = -1,
+
+       .card_init = hw_card_init,
+       .card_stop = hw_card_stop,
+       .pll_init = hw_pll_init,
+       .is_adc_source_selected = hw_is_adc_input_selected,
+       .select_adc_source = hw_adc_input_select,
+       .have_digit_io_switch = hw_have_digit_io_switch,
+
+       .src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk,
+       .src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk,
+       .src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk,
+       .src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk,
+       .src_set_state = src_set_state,
+       .src_set_bm = src_set_bm,
+       .src_set_rsr = src_set_rsr,
+       .src_set_sf = src_set_sf,
+       .src_set_wr = src_set_wr,
+       .src_set_pm = src_set_pm,
+       .src_set_rom = src_set_rom,
+       .src_set_vo = src_set_vo,
+       .src_set_st = src_set_st,
+       .src_set_ie = src_set_ie,
+       .src_set_ilsz = src_set_ilsz,
+       .src_set_bp = src_set_bp,
+       .src_set_cisz = src_set_cisz,
+       .src_set_ca = src_set_ca,
+       .src_set_sa = src_set_sa,
+       .src_set_la = src_set_la,
+       .src_set_pitch = src_set_pitch,
+       .src_set_dirty = src_set_dirty,
+       .src_set_clear_zbufs = src_set_clear_zbufs,
+       .src_set_dirty_all = src_set_dirty_all,
+       .src_commit_write = src_commit_write,
+       .src_get_ca = src_get_ca,
+       .src_get_dirty = src_get_dirty,
+       .src_dirty_conj_mask = src_dirty_conj_mask,
+       .src_mgr_enbs_src = src_mgr_enbs_src,
+       .src_mgr_enb_src = src_mgr_enb_src,
+       .src_mgr_dsb_src = src_mgr_dsb_src,
+       .src_mgr_commit_write = src_mgr_commit_write,
+
+       .srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk,
+       .srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk,
+       .srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc,
+       .srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser,
+       .srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt,
+       .srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr,
+       .srcimp_mgr_commit_write = srcimp_mgr_commit_write,
+
+       .amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk,
+       .amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk,
+       .amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk,
+       .amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk,
+       .amixer_set_mode = amixer_set_mode,
+       .amixer_set_iv = amixer_set_iv,
+       .amixer_set_x = amixer_set_x,
+       .amixer_set_y = amixer_set_y,
+       .amixer_set_sadr = amixer_set_sadr,
+       .amixer_set_se = amixer_set_se,
+       .amixer_set_dirty = amixer_set_dirty,
+       .amixer_set_dirty_all = amixer_set_dirty_all,
+       .amixer_commit_write = amixer_commit_write,
+       .amixer_get_y = amixer_get_y,
+       .amixer_get_dirty = amixer_get_dirty,
+
+       .dai_get_ctrl_blk = dai_get_ctrl_blk,
+       .dai_put_ctrl_blk = dai_put_ctrl_blk,
+       .dai_srt_set_srco = dai_srt_set_srco,
+       .dai_srt_set_srcm = dai_srt_set_srcm,
+       .dai_srt_set_rsr = dai_srt_set_rsr,
+       .dai_srt_set_drat = dai_srt_set_drat,
+       .dai_srt_set_ec = dai_srt_set_ec,
+       .dai_srt_set_et = dai_srt_set_et,
+       .dai_commit_write = dai_commit_write,
+
+       .dao_get_ctrl_blk = dao_get_ctrl_blk,
+       .dao_put_ctrl_blk = dao_put_ctrl_blk,
+       .dao_set_spos = dao_set_spos,
+       .dao_commit_write = dao_commit_write,
+       .dao_get_spos = dao_get_spos,
+
+       .daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk,
+       .daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk,
+       .daio_mgr_enb_dai = daio_mgr_enb_dai,
+       .daio_mgr_dsb_dai = daio_mgr_dsb_dai,
+       .daio_mgr_enb_dao = daio_mgr_enb_dao,
+       .daio_mgr_dsb_dao = daio_mgr_dsb_dao,
+       .daio_mgr_dao_init = daio_mgr_dao_init,
+       .daio_mgr_set_imaparc = daio_mgr_set_imaparc,
+       .daio_mgr_set_imapnxt = daio_mgr_set_imapnxt,
+       .daio_mgr_set_imapaddr = daio_mgr_set_imapaddr,
+       .daio_mgr_commit_write = daio_mgr_commit_write,
+};
+
+int __devinit create_20k2_hw_obj(struct hw **rhw)
+{
+       struct hw20k2 *hw20k2;
+
+       *rhw = NULL;
+       hw20k2 = kzalloc(sizeof(*hw20k2), GFP_KERNEL);
+       if (!hw20k2)
+               return -ENOMEM;
+
+       hw20k2->hw = ct20k2_preset;
+       *rhw = &hw20k2->hw;
+
+       return 0;
+}
+
+int destroy_20k2_hw_obj(struct hw *hw)
+{
+       if (hw->io_base)
+               hw_card_shutdown(hw);
+
+       kfree(hw);
+       return 0;
+}
diff --git a/sound/pci/ctxfi/cthw20k2.h b/sound/pci/ctxfi/cthw20k2.h
new file mode 100644 (file)
index 0000000..d2b7daa
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       cthw20k2.h
+ *
+ * @Brief
+ * This file contains the definition of hardware access methord.
+ *
+ * @Author     Liu Chun
+ * @Date       May 13 2008
+ *
+ */
+
+#ifndef CTHW20K2_H
+#define CTHW20K2_H
+
+#include "cthardware.h"
+
+int create_20k2_hw_obj(struct hw **rhw);
+int destroy_20k2_hw_obj(struct hw *hw);
+
+#endif /* CTHW20K2_H */
diff --git a/sound/pci/ctxfi/ctimap.c b/sound/pci/ctxfi/ctimap.c
new file mode 100644 (file)
index 0000000..0b73368
--- /dev/null
@@ -0,0 +1,112 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctimap.c
+ *
+ * @Brief
+ * This file contains the implementation of generic input mapper operations
+ * for input mapper management.
+ *
+ * @Author     Liu Chun
+ * @Date       May 23 2008
+ *
+ */
+
+#include "ctimap.h"
+#include <linux/slab.h>
+
+int input_mapper_add(struct list_head *mappers, struct imapper *entry,
+                    int (*map_op)(void *, struct imapper *), void *data)
+{
+       struct list_head *pos, *pre, *head;
+       struct imapper *pre_ent, *pos_ent;
+
+       head = mappers;
+
+       if (list_empty(head)) {
+               entry->next = entry->addr;
+               map_op(data, entry);
+               list_add(&entry->list, head);
+               return 0;
+       }
+
+       list_for_each(pos, head) {
+               pos_ent = list_entry(pos, struct imapper, list);
+               if (pos_ent->slot > entry->slot) {
+                       /* found a position in list */
+                       break;
+               }
+       }
+
+       if (pos != head) {
+               pre = pos->prev;
+               if (pre == head)
+                       pre = head->prev;
+
+               __list_add(&entry->list, pos->prev, pos);
+       } else {
+               pre = head->prev;
+               pos = head->next;
+               list_add_tail(&entry->list, head);
+       }
+
+       pre_ent = list_entry(pre, struct imapper, list);
+       pos_ent = list_entry(pos, struct imapper, list);
+
+       entry->next = pos_ent->addr;
+       map_op(data, entry);
+       pre_ent->next = entry->addr;
+       map_op(data, pre_ent);
+
+       return 0;
+}
+
+int input_mapper_delete(struct list_head *mappers, struct imapper *entry,
+                    int (*map_op)(void *, struct imapper *), void *data)
+{
+       struct list_head *next, *pre, *head;
+       struct imapper *pre_ent, *next_ent;
+
+       head = mappers;
+
+       if (list_empty(head))
+               return 0;
+
+       pre = (entry->list.prev == head) ? head->prev : entry->list.prev;
+       next = (entry->list.next == head) ? head->next : entry->list.next;
+
+       if (pre == &entry->list) {
+               /* entry is the only one node in mappers list */
+               entry->next = entry->addr = entry->user = entry->slot = 0;
+               map_op(data, entry);
+               list_del(&entry->list);
+               return 0;
+       }
+
+       pre_ent = list_entry(pre, struct imapper, list);
+       next_ent = list_entry(next, struct imapper, list);
+
+       pre_ent->next = next_ent->addr;
+       map_op(data, pre_ent);
+       list_del(&entry->list);
+
+       return 0;
+}
+
+void free_input_mapper_list(struct list_head *head)
+{
+       struct imapper *entry;
+       struct list_head *pos;
+
+       while (!list_empty(head)) {
+               pos = head->next;
+               list_del(pos);
+               entry = list_entry(pos, struct imapper, list);
+               kfree(entry);
+       }
+}
+
diff --git a/sound/pci/ctxfi/ctimap.h b/sound/pci/ctxfi/ctimap.h
new file mode 100644 (file)
index 0000000..53ccf9b
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctimap.h
+ *
+ * @Brief
+ * This file contains the definition of generic input mapper operations
+ * for input mapper management.
+ *
+ * @Author     Liu Chun
+ * @Date       May 23 2008
+ *
+ */
+
+#ifndef CTIMAP_H
+#define CTIMAP_H
+
+#include <linux/list.h>
+
+struct imapper {
+       unsigned short slot; /* the id of the slot containing input data */
+       unsigned short user; /* the id of the user resource consuming data */
+       unsigned short addr; /* the input mapper ram id */
+       unsigned short next; /* the next input mapper ram id */
+       struct list_head        list;
+};
+
+int input_mapper_add(struct list_head *mappers, struct imapper *entry,
+                    int (*map_op)(void *, struct imapper *), void *data);
+
+int input_mapper_delete(struct list_head *mappers, struct imapper *entry,
+                    int (*map_op)(void *, struct imapper *), void *data);
+
+void free_input_mapper_list(struct list_head *mappers);
+
+#endif /* CTIMAP_H */
diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c
new file mode 100644 (file)
index 0000000..666722d
--- /dev/null
@@ -0,0 +1,1123 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctmixer.c
+ *
+ * @Brief
+ * This file contains the implementation of alsa mixer device functions.
+ *
+ * @Author     Liu Chun
+ * @Date       May 28 2008
+ *
+ */
+
+
+#include "ctmixer.h"
+#include "ctamixer.h"
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/asoundef.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+
+enum CT_SUM_CTL {
+       SUM_IN_F,
+       SUM_IN_R,
+       SUM_IN_C,
+       SUM_IN_S,
+       SUM_IN_F_C,
+
+       NUM_CT_SUMS
+};
+
+enum CT_AMIXER_CTL {
+       /* volume control mixers */
+       AMIXER_MASTER_F,
+       AMIXER_MASTER_R,
+       AMIXER_MASTER_C,
+       AMIXER_MASTER_S,
+       AMIXER_PCM_F,
+       AMIXER_PCM_R,
+       AMIXER_PCM_C,
+       AMIXER_PCM_S,
+       AMIXER_SPDIFI,
+       AMIXER_LINEIN,
+       AMIXER_MIC,
+       AMIXER_SPDIFO,
+       AMIXER_WAVE_F,
+       AMIXER_WAVE_R,
+       AMIXER_WAVE_C,
+       AMIXER_WAVE_S,
+       AMIXER_MASTER_F_C,
+       AMIXER_PCM_F_C,
+       AMIXER_SPDIFI_C,
+       AMIXER_LINEIN_C,
+       AMIXER_MIC_C,
+
+       /* this should always be the last one */
+       NUM_CT_AMIXERS
+};
+
+enum CTALSA_MIXER_CTL {
+       /* volume control mixers */
+       MIXER_MASTER_P,
+       MIXER_PCM_P,
+       MIXER_LINEIN_P,
+       MIXER_MIC_P,
+       MIXER_SPDIFI_P,
+       MIXER_SPDIFO_P,
+       MIXER_WAVEF_P,
+       MIXER_WAVER_P,
+       MIXER_WAVEC_P,
+       MIXER_WAVES_P,
+       MIXER_MASTER_C,
+       MIXER_PCM_C,
+       MIXER_LINEIN_C,
+       MIXER_MIC_C,
+       MIXER_SPDIFI_C,
+
+       /* switch control mixers */
+       MIXER_PCM_C_S,
+       MIXER_LINEIN_C_S,
+       MIXER_MIC_C_S,
+       MIXER_SPDIFI_C_S,
+       MIXER_LINEIN_P_S,
+       MIXER_SPDIFO_P_S,
+       MIXER_SPDIFI_P_S,
+       MIXER_WAVEF_P_S,
+       MIXER_WAVER_P_S,
+       MIXER_WAVEC_P_S,
+       MIXER_WAVES_P_S,
+       MIXER_DIGITAL_IO_S,
+       MIXER_IEC958_MASK,
+       MIXER_IEC958_DEFAULT,
+       MIXER_IEC958_STREAM,
+
+       /* this should always be the last one */
+       NUM_CTALSA_MIXERS
+};
+
+#define VOL_MIXER_START                MIXER_MASTER_P
+#define VOL_MIXER_END          MIXER_SPDIFI_C
+#define VOL_MIXER_NUM          (VOL_MIXER_END - VOL_MIXER_START + 1)
+#define SWH_MIXER_START                MIXER_PCM_C_S
+#define SWH_MIXER_END          MIXER_DIGITAL_IO_S
+#define SWH_CAPTURE_START      MIXER_PCM_C_S
+#define SWH_CAPTURE_END                MIXER_SPDIFI_C_S
+
+#define CHN_NUM                2
+
+struct ct_kcontrol_init {
+       unsigned char ctl;
+       char *name;
+};
+
+static struct ct_kcontrol_init
+ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
+       [MIXER_MASTER_P] = {
+               .ctl = 1,
+               .name = "Master Playback Volume",
+       },
+       [MIXER_MASTER_C] = {
+               .ctl = 1,
+               .name = "Master Capture Volume",
+       },
+       [MIXER_PCM_P] = {
+               .ctl = 1,
+               .name = "PCM Playback Volume",
+       },
+       [MIXER_PCM_C] = {
+               .ctl = 1,
+               .name = "PCM Capture Volume",
+       },
+       [MIXER_LINEIN_P] = {
+               .ctl = 1,
+               .name = "Line-in Playback Volume",
+       },
+       [MIXER_LINEIN_C] = {
+               .ctl = 1,
+               .name = "Line-in Capture Volume",
+       },
+       [MIXER_MIC_P] = {
+               .ctl = 1,
+               .name = "Mic Playback Volume",
+       },
+       [MIXER_MIC_C] = {
+               .ctl = 1,
+               .name = "Mic Capture Volume",
+       },
+       [MIXER_SPDIFI_P] = {
+               .ctl = 1,
+               .name = "S/PDIF-in Playback Volume",
+       },
+       [MIXER_SPDIFI_C] = {
+               .ctl = 1,
+               .name = "S/PDIF-in Capture Volume",
+       },
+       [MIXER_SPDIFO_P] = {
+               .ctl = 1,
+               .name = "S/PDIF-out Playback Volume",
+       },
+       [MIXER_WAVEF_P] = {
+               .ctl = 1,
+               .name = "Front Playback Volume",
+       },
+       [MIXER_WAVES_P] = {
+               .ctl = 1,
+               .name = "Side Playback Volume",
+       },
+       [MIXER_WAVEC_P] = {
+               .ctl = 1,
+               .name = "Center/LFE Playback Volume",
+       },
+       [MIXER_WAVER_P] = {
+               .ctl = 1,
+               .name = "Surround Playback Volume",
+       },
+
+       [MIXER_PCM_C_S] = {
+               .ctl = 1,
+               .name = "PCM Capture Switch",
+       },
+       [MIXER_LINEIN_C_S] = {
+               .ctl = 1,
+               .name = "Line-in Capture Switch",
+       },
+       [MIXER_MIC_C_S] = {
+               .ctl = 1,
+               .name = "Mic Capture Switch",
+       },
+       [MIXER_SPDIFI_C_S] = {
+               .ctl = 1,
+               .name = "S/PDIF-in Capture Switch",
+       },
+       [MIXER_LINEIN_P_S] = {
+               .ctl = 1,
+               .name = "Line-in Playback Switch",
+       },
+       [MIXER_SPDIFO_P_S] = {
+               .ctl = 1,
+               .name = "S/PDIF-out Playback Switch",
+       },
+       [MIXER_SPDIFI_P_S] = {
+               .ctl = 1,
+               .name = "S/PDIF-in Playback Switch",
+       },
+       [MIXER_WAVEF_P_S] = {
+               .ctl = 1,
+               .name = "Front Playback Switch",
+       },
+       [MIXER_WAVES_P_S] = {
+               .ctl = 1,
+               .name = "Side Playback Switch",
+       },
+       [MIXER_WAVEC_P_S] = {
+               .ctl = 1,
+               .name = "Center/LFE Playback Switch",
+       },
+       [MIXER_WAVER_P_S] = {
+               .ctl = 1,
+               .name = "Surround Playback Switch",
+       },
+       [MIXER_DIGITAL_IO_S] = {
+               .ctl = 0,
+               .name = "Digit-IO Playback Switch",
+       },
+};
+
+static void
+ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type);
+
+static void
+ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type);
+
+static struct snd_kcontrol *kctls[2] = {NULL};
+
+static enum CT_AMIXER_CTL get_amixer_index(enum CTALSA_MIXER_CTL alsa_index)
+{
+       switch (alsa_index) {
+       case MIXER_MASTER_P:    return AMIXER_MASTER_F;
+       case MIXER_MASTER_C:    return AMIXER_MASTER_F_C;
+       case MIXER_PCM_P:       return AMIXER_PCM_F;
+       case MIXER_PCM_C:
+       case MIXER_PCM_C_S:     return AMIXER_PCM_F_C;
+       case MIXER_LINEIN_P:    return AMIXER_LINEIN;
+       case MIXER_LINEIN_C:
+       case MIXER_LINEIN_C_S:  return AMIXER_LINEIN_C;
+       case MIXER_MIC_P:       return AMIXER_MIC;
+       case MIXER_MIC_C:
+       case MIXER_MIC_C_S:     return AMIXER_MIC_C;
+       case MIXER_SPDIFI_P:    return AMIXER_SPDIFI;
+       case MIXER_SPDIFI_C:
+       case MIXER_SPDIFI_C_S:  return AMIXER_SPDIFI_C;
+       case MIXER_SPDIFO_P:    return AMIXER_SPDIFO;
+       case MIXER_WAVEF_P:     return AMIXER_WAVE_F;
+       case MIXER_WAVES_P:     return AMIXER_WAVE_S;
+       case MIXER_WAVEC_P:     return AMIXER_WAVE_C;
+       case MIXER_WAVER_P:     return AMIXER_WAVE_R;
+       default:                return NUM_CT_AMIXERS;
+       }
+}
+
+static enum CT_AMIXER_CTL get_recording_amixer(enum CT_AMIXER_CTL index)
+{
+       switch (index) {
+       case AMIXER_MASTER_F:   return AMIXER_MASTER_F_C;
+       case AMIXER_PCM_F:      return AMIXER_PCM_F_C;
+       case AMIXER_SPDIFI:     return AMIXER_SPDIFI_C;
+       case AMIXER_LINEIN:     return AMIXER_LINEIN_C;
+       case AMIXER_MIC:        return AMIXER_MIC_C;
+       default:                return NUM_CT_AMIXERS;
+       }
+}
+
+static unsigned char
+get_switch_state(struct ct_mixer *mixer, enum CTALSA_MIXER_CTL type)
+{
+       return (mixer->switch_state & (0x1 << (type - SWH_MIXER_START)))
+               ? 1 : 0;
+}
+
+static void
+set_switch_state(struct ct_mixer *mixer,
+                enum CTALSA_MIXER_CTL type, unsigned char state)
+{
+       if (state)
+               mixer->switch_state |= (0x1 << (type - SWH_MIXER_START));
+       else
+               mixer->switch_state &= ~(0x1 << (type - SWH_MIXER_START));
+}
+
+#if 0 /* not used */
+/* Map integer value ranging from 0 to 65535 to 14-bit float value ranging
+ * from 2^-6 to (1+1023/1024) */
+static unsigned int uint16_to_float14(unsigned int x)
+{
+       unsigned int i;
+
+       if (x < 17)
+               return 0;
+
+       x *= 2031;
+       x /= 65535;
+       x += 16;
+
+       /* i <= 6 */
+       for (i = 0; !(x & 0x400); i++)
+               x <<= 1;
+
+       x = (((7 - i) & 0x7) << 10) | (x & 0x3ff);
+
+       return x;
+}
+
+static unsigned int float14_to_uint16(unsigned int x)
+{
+       unsigned int e;
+
+       if (!x)
+               return x;
+
+       e = (x >> 10) & 0x7;
+       x &= 0x3ff;
+       x += 1024;
+       x >>= (7 - e);
+       x -= 16;
+       x *= 65535;
+       x /= 2031;
+
+       return x;
+}
+#endif /* not used */
+
+#define VOL_SCALE      0x1c
+#define VOL_MAX                0x100
+
+static const DECLARE_TLV_DB_SCALE(ct_vol_db_scale, -6400, 25, 1);
+
+static int ct_alsa_mix_volume_info(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 2;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = VOL_MAX;
+
+       return 0;
+}
+
+static int ct_alsa_mix_volume_get(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+       enum CT_AMIXER_CTL type = get_amixer_index(kcontrol->private_value);
+       struct amixer *amixer;
+       int i, val;
+
+       for (i = 0; i < 2; i++) {
+               amixer = ((struct ct_mixer *)atc->mixer)->
+                                               amixers[type*CHN_NUM+i];
+               val = amixer->ops->get_scale(amixer) / VOL_SCALE;
+               if (val < 0)
+                       val = 0;
+               else if (val > VOL_MAX)
+                       val = VOL_MAX;
+               ucontrol->value.integer.value[i] = val;
+       }
+
+       return 0;
+}
+
+static int ct_alsa_mix_volume_put(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+       struct ct_mixer *mixer = atc->mixer;
+       enum CT_AMIXER_CTL type = get_amixer_index(kcontrol->private_value);
+       struct amixer *amixer;
+       int i, j, val, oval, change = 0;
+
+       for (i = 0; i < 2; i++) {
+               val = ucontrol->value.integer.value[i];
+               if (val < 0)
+                       val = 0;
+               else if (val > VOL_MAX)
+                       val = VOL_MAX;
+               val *= VOL_SCALE;
+               amixer = mixer->amixers[type*CHN_NUM+i];
+               oval = amixer->ops->get_scale(amixer);
+               if (val != oval) {
+                       amixer->ops->set_scale(amixer, val);
+                       amixer->ops->commit_write(amixer);
+                       change = 1;
+                       /* Synchronize Master/PCM playback AMIXERs. */
+                       if (AMIXER_MASTER_F == type || AMIXER_PCM_F == type) {
+                               for (j = 1; j < 4; j++) {
+                                       amixer = mixer->
+                                               amixers[(type+j)*CHN_NUM+i];
+                                       amixer->ops->set_scale(amixer, val);
+                                       amixer->ops->commit_write(amixer);
+                               }
+                       }
+               }
+       }
+
+       return change;
+}
+
+static struct snd_kcontrol_new vol_ctl = {
+       .access         = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                         SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+       .iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .info           = ct_alsa_mix_volume_info,
+       .get            = ct_alsa_mix_volume_get,
+       .put            = ct_alsa_mix_volume_put,
+       .tlv            = { .p =  ct_vol_db_scale },
+};
+
+static void
+do_line_mic_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type)
+{
+
+       if (MIXER_LINEIN_C_S == type) {
+               atc->select_line_in(atc);
+               set_switch_state(atc->mixer, MIXER_MIC_C_S, 0);
+               snd_ctl_notify(atc->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                                                       &kctls[1]->id);
+       } else if (MIXER_MIC_C_S == type) {
+               atc->select_mic_in(atc);
+               set_switch_state(atc->mixer, MIXER_LINEIN_C_S, 0);
+               snd_ctl_notify(atc->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                                                       &kctls[0]->id);
+       }
+}
+
+static void
+do_digit_io_switch(struct ct_atc *atc, int state)
+{
+       struct ct_mixer *mixer = atc->mixer;
+
+       if (state) {
+               atc->select_digit_io(atc);
+               atc->spdif_out_unmute(atc,
+                               get_switch_state(mixer, MIXER_SPDIFO_P_S));
+               atc->spdif_in_unmute(atc, 1);
+               atc->line_in_unmute(atc, 0);
+               return;
+       }
+
+       if (get_switch_state(mixer, MIXER_LINEIN_C_S))
+               atc->select_line_in(atc);
+       else if (get_switch_state(mixer, MIXER_MIC_C_S))
+               atc->select_mic_in(atc);
+
+       atc->spdif_out_unmute(atc, 0);
+       atc->spdif_in_unmute(atc, 0);
+       atc->line_in_unmute(atc, 1);
+       return;
+}
+
+static int ct_alsa_mix_switch_info(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       uinfo->value.integer.step = 1;
+
+       return 0;
+}
+
+static int ct_alsa_mix_switch_get(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct ct_mixer *mixer =
+               ((struct ct_atc *)snd_kcontrol_chip(kcontrol))->mixer;
+       enum CTALSA_MIXER_CTL type = kcontrol->private_value;
+
+       ucontrol->value.integer.value[0] = get_switch_state(mixer, type);
+       return 0;
+}
+
+static int ct_alsa_mix_switch_put(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+       struct ct_mixer *mixer = atc->mixer;
+       enum CTALSA_MIXER_CTL type = kcontrol->private_value;
+       int state;
+
+       state = ucontrol->value.integer.value[0];
+       if (get_switch_state(mixer, type) == state)
+               return 0;
+
+       set_switch_state(mixer, type, state);
+       /* Do changes in mixer. */
+       if ((SWH_CAPTURE_START <= type) && (SWH_CAPTURE_END >= type)) {
+               if (state) {
+                       ct_mixer_recording_select(mixer,
+                                                 get_amixer_index(type));
+               } else {
+                       ct_mixer_recording_unselect(mixer,
+                                                   get_amixer_index(type));
+               }
+       }
+       /* Do changes out of mixer. */
+       if (state && (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type))
+               do_line_mic_switch(atc, type);
+       else if (MIXER_WAVEF_P_S == type)
+               atc->line_front_unmute(atc, state);
+       else if (MIXER_WAVES_P_S == type)
+               atc->line_surround_unmute(atc, state);
+       else if (MIXER_WAVEC_P_S == type)
+               atc->line_clfe_unmute(atc, state);
+       else if (MIXER_WAVER_P_S == type)
+               atc->line_rear_unmute(atc, state);
+       else if (MIXER_LINEIN_P_S == type)
+               atc->line_in_unmute(atc, state);
+       else if (MIXER_SPDIFO_P_S == type)
+               atc->spdif_out_unmute(atc, state);
+       else if (MIXER_SPDIFI_P_S == type)
+               atc->spdif_in_unmute(atc, state);
+       else if (MIXER_DIGITAL_IO_S == type)
+               do_digit_io_switch(atc, state);
+
+       return 1;
+}
+
+static struct snd_kcontrol_new swh_ctl = {
+       .access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .info           = ct_alsa_mix_switch_info,
+       .get            = ct_alsa_mix_switch_get,
+       .put            = ct_alsa_mix_switch_put
+};
+
+static int ct_spdif_info(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+       uinfo->count = 1;
+       return 0;
+}
+
+static int ct_spdif_get_mask(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.iec958.status[0] = 0xff;
+       ucontrol->value.iec958.status[1] = 0xff;
+       ucontrol->value.iec958.status[2] = 0xff;
+       ucontrol->value.iec958.status[3] = 0xff;
+       return 0;
+}
+
+static int ct_spdif_default_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       unsigned int status = SNDRV_PCM_DEFAULT_CON_SPDIF;
+
+       ucontrol->value.iec958.status[0] = (status >> 0) & 0xff;
+       ucontrol->value.iec958.status[1] = (status >> 8) & 0xff;
+       ucontrol->value.iec958.status[2] = (status >> 16) & 0xff;
+       ucontrol->value.iec958.status[3] = (status >> 24) & 0xff;
+
+       return 0;
+}
+
+static int ct_spdif_get(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+       unsigned int status;
+
+       atc->spdif_out_get_status(atc, &status);
+       ucontrol->value.iec958.status[0] = (status >> 0) & 0xff;
+       ucontrol->value.iec958.status[1] = (status >> 8) & 0xff;
+       ucontrol->value.iec958.status[2] = (status >> 16) & 0xff;
+       ucontrol->value.iec958.status[3] = (status >> 24) & 0xff;
+
+       return 0;
+}
+
+static int ct_spdif_put(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+       int change;
+       unsigned int status, old_status;
+
+       status = (ucontrol->value.iec958.status[0] << 0) |
+                (ucontrol->value.iec958.status[1] << 8) |
+                (ucontrol->value.iec958.status[2] << 16) |
+                (ucontrol->value.iec958.status[3] << 24);
+
+       atc->spdif_out_get_status(atc, &old_status);
+       change = (old_status != status);
+       if (change)
+               atc->spdif_out_set_status(atc, status);
+
+       return change;
+}
+
+static struct snd_kcontrol_new iec958_mask_ctl = {
+       .access         = SNDRV_CTL_ELEM_ACCESS_READ,
+       .iface          = SNDRV_CTL_ELEM_IFACE_PCM,
+       .name           = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
+       .count          = 1,
+       .info           = ct_spdif_info,
+       .get            = ct_spdif_get_mask,
+       .private_value  = MIXER_IEC958_MASK
+};
+
+static struct snd_kcontrol_new iec958_default_ctl = {
+       .iface          = SNDRV_CTL_ELEM_IFACE_PCM,
+       .name           = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+       .count          = 1,
+       .info           = ct_spdif_info,
+       .get            = ct_spdif_default_get,
+       .put            = ct_spdif_put,
+       .private_value  = MIXER_IEC958_DEFAULT
+};
+
+static struct snd_kcontrol_new iec958_ctl = {
+       .access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .iface          = SNDRV_CTL_ELEM_IFACE_PCM,
+       .name           = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
+       .count          = 1,
+       .info           = ct_spdif_info,
+       .get            = ct_spdif_get,
+       .put            = ct_spdif_put,
+       .private_value  = MIXER_IEC958_STREAM
+};
+
+#define NUM_IEC958_CTL 3
+
+static int
+ct_mixer_kcontrol_new(struct ct_mixer *mixer, struct snd_kcontrol_new *new)
+{
+       struct snd_kcontrol *kctl;
+       int err;
+
+       kctl = snd_ctl_new1(new, mixer->atc);
+       if (NULL == kctl)
+               return -ENOMEM;
+
+       if (SNDRV_CTL_ELEM_IFACE_PCM == kctl->id.iface)
+               kctl->id.device = IEC958;
+
+       err = snd_ctl_add(mixer->atc->card, kctl);
+       if (err)
+               return err;
+
+       switch (new->private_value) {
+       case MIXER_LINEIN_C_S:
+               kctls[0] = kctl; break;
+       case MIXER_MIC_C_S:
+               kctls[1] = kctl; break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
+{
+       enum CTALSA_MIXER_CTL type;
+       struct ct_atc *atc = mixer->atc;
+       int err;
+
+       /* Create snd kcontrol instances on demand */
+       for (type = VOL_MIXER_START; type <= VOL_MIXER_END; type++) {
+               if (ct_kcontrol_init_table[type].ctl) {
+                       vol_ctl.name = ct_kcontrol_init_table[type].name;
+                       vol_ctl.private_value = (unsigned long)type;
+                       err = ct_mixer_kcontrol_new(mixer, &vol_ctl);
+                       if (err)
+                               return err;
+               }
+       }
+
+       ct_kcontrol_init_table[MIXER_DIGITAL_IO_S].ctl =
+                                       atc->have_digit_io_switch(atc);
+       for (type = SWH_MIXER_START; type <= SWH_MIXER_END; type++) {
+               if (ct_kcontrol_init_table[type].ctl) {
+                       swh_ctl.name = ct_kcontrol_init_table[type].name;
+                       swh_ctl.private_value = (unsigned long)type;
+                       err = ct_mixer_kcontrol_new(mixer, &swh_ctl);
+                       if (err)
+                               return err;
+               }
+       }
+
+       err = ct_mixer_kcontrol_new(mixer, &iec958_mask_ctl);
+       if (err)
+               return err;
+
+       err = ct_mixer_kcontrol_new(mixer, &iec958_default_ctl);
+       if (err)
+               return err;
+
+       err = ct_mixer_kcontrol_new(mixer, &iec958_ctl);
+       if (err)
+               return err;
+
+       atc->line_front_unmute(atc, 1);
+       set_switch_state(mixer, MIXER_WAVEF_P_S, 1);
+       atc->line_surround_unmute(atc, 0);
+       set_switch_state(mixer, MIXER_WAVES_P_S, 0);
+       atc->line_clfe_unmute(atc, 0);
+       set_switch_state(mixer, MIXER_WAVEC_P_S, 0);
+       atc->line_rear_unmute(atc, 0);
+       set_switch_state(mixer, MIXER_WAVER_P_S, 0);
+       atc->spdif_out_unmute(atc, 0);
+       set_switch_state(mixer, MIXER_SPDIFO_P_S, 0);
+       atc->line_in_unmute(atc, 0);
+       set_switch_state(mixer, MIXER_LINEIN_P_S, 0);
+       atc->spdif_in_unmute(atc, 0);
+       set_switch_state(mixer, MIXER_SPDIFI_P_S, 0);
+
+       set_switch_state(mixer, MIXER_PCM_C_S, 1);
+       set_switch_state(mixer, MIXER_LINEIN_C_S, 1);
+       set_switch_state(mixer, MIXER_SPDIFI_C_S, 1);
+
+       return 0;
+}
+
+static void
+ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type)
+{
+       struct amixer *amix_d;
+       struct sum *sum_c;
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               amix_d = mixer->amixers[type*CHN_NUM+i];
+               sum_c = mixer->sums[SUM_IN_F_C*CHN_NUM+i];
+               amix_d->ops->set_sum(amix_d, sum_c);
+               amix_d->ops->commit_write(amix_d);
+       }
+}
+
+static void
+ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type)
+{
+       struct amixer *amix_d;
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               amix_d = mixer->amixers[type*CHN_NUM+i];
+               amix_d->ops->set_sum(amix_d, NULL);
+               amix_d->ops->commit_write(amix_d);
+       }
+}
+
+static int ct_mixer_get_resources(struct ct_mixer *mixer)
+{
+       struct sum_mgr *sum_mgr;
+       struct sum *sum;
+       struct sum_desc sum_desc = {0};
+       struct amixer_mgr *amixer_mgr;
+       struct amixer *amixer;
+       struct amixer_desc am_desc = {0};
+       int err;
+       int i;
+
+       /* Allocate sum resources for mixer obj */
+       sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM];
+       sum_desc.msr = mixer->atc->msr;
+       for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) {
+               err = sum_mgr->get_sum(sum_mgr, &sum_desc, &sum);
+               if (err) {
+                       printk(KERN_ERR "ctxfi:Failed to get sum resources for "
+                                         "front output!\n");
+                       break;
+               }
+               mixer->sums[i] = sum;
+       }
+       if (err)
+               goto error1;
+
+       /* Allocate amixer resources for mixer obj */
+       amixer_mgr = (struct amixer_mgr *)mixer->atc->rsc_mgrs[AMIXER];
+       am_desc.msr = mixer->atc->msr;
+       for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) {
+               err = amixer_mgr->get_amixer(amixer_mgr, &am_desc, &amixer);
+               if (err) {
+                       printk(KERN_ERR "ctxfi:Failed to get amixer resources "
+                              "for mixer obj!\n");
+                       break;
+               }
+               mixer->amixers[i] = amixer;
+       }
+       if (err)
+               goto error2;
+
+       return 0;
+
+error2:
+       for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) {
+               if (NULL != mixer->amixers[i]) {
+                       amixer = mixer->amixers[i];
+                       amixer_mgr->put_amixer(amixer_mgr, amixer);
+                       mixer->amixers[i] = NULL;
+               }
+       }
+error1:
+       for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) {
+               if (NULL != mixer->sums[i]) {
+                       sum_mgr->put_sum(sum_mgr, (struct sum *)mixer->sums[i]);
+                       mixer->sums[i] = NULL;
+               }
+       }
+
+       return err;
+}
+
+static int ct_mixer_get_mem(struct ct_mixer **rmixer)
+{
+       struct ct_mixer *mixer;
+       int err;
+
+       *rmixer = NULL;
+       /* Allocate mem for mixer obj */
+       mixer = kzalloc(sizeof(*mixer), GFP_KERNEL);
+       if (NULL == mixer)
+               return -ENOMEM;
+
+       mixer->amixers = kzalloc(sizeof(void *)*(NUM_CT_AMIXERS*CHN_NUM),
+                                GFP_KERNEL);
+       if (NULL == mixer->amixers) {
+               err = -ENOMEM;
+               goto error1;
+       }
+       mixer->sums = kzalloc(sizeof(void *)*(NUM_CT_SUMS*CHN_NUM), GFP_KERNEL);
+       if (NULL == mixer->sums) {
+               err = -ENOMEM;
+               goto error2;
+       }
+
+       *rmixer = mixer;
+       return 0;
+
+error2:
+       kfree(mixer->amixers);
+error1:
+       kfree(mixer);
+       return err;
+}
+
+static int ct_mixer_topology_build(struct ct_mixer *mixer)
+{
+       struct sum *sum;
+       struct amixer *amix_d, *amix_s;
+       enum CT_AMIXER_CTL i, j;
+
+       /* Build topology from destination to source */
+
+       /* Set up Master mixer */
+       for (i = AMIXER_MASTER_F, j = SUM_IN_F;
+                                       i <= AMIXER_MASTER_S; i++, j++) {
+               amix_d = mixer->amixers[i*CHN_NUM];
+               sum = mixer->sums[j*CHN_NUM];
+               amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL);
+               amix_d = mixer->amixers[i*CHN_NUM+1];
+               sum = mixer->sums[j*CHN_NUM+1];
+               amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL);
+       }
+
+       /* Set up Wave-out mixer */
+       for (i = AMIXER_WAVE_F, j = AMIXER_MASTER_F;
+                                       i <= AMIXER_WAVE_S; i++, j++) {
+               amix_d = mixer->amixers[i*CHN_NUM];
+               amix_s = mixer->amixers[j*CHN_NUM];
+               amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL);
+               amix_d = mixer->amixers[i*CHN_NUM+1];
+               amix_s = mixer->amixers[j*CHN_NUM+1];
+               amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL);
+       }
+
+       /* Set up S/PDIF-out mixer */
+       amix_d = mixer->amixers[AMIXER_SPDIFO*CHN_NUM];
+       amix_s = mixer->amixers[AMIXER_MASTER_F*CHN_NUM];
+       amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL);
+       amix_d = mixer->amixers[AMIXER_SPDIFO*CHN_NUM+1];
+       amix_s = mixer->amixers[AMIXER_MASTER_F*CHN_NUM+1];
+       amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL);
+
+       /* Set up PCM-in mixer */
+       for (i = AMIXER_PCM_F, j = SUM_IN_F; i <= AMIXER_PCM_S; i++, j++) {
+               amix_d = mixer->amixers[i*CHN_NUM];
+               sum = mixer->sums[j*CHN_NUM];
+               amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+               amix_d = mixer->amixers[i*CHN_NUM+1];
+               sum = mixer->sums[j*CHN_NUM+1];
+               amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+       }
+
+       /* Set up Line-in mixer */
+       amix_d = mixer->amixers[AMIXER_LINEIN*CHN_NUM];
+       sum = mixer->sums[SUM_IN_F*CHN_NUM];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+       amix_d = mixer->amixers[AMIXER_LINEIN*CHN_NUM+1];
+       sum = mixer->sums[SUM_IN_F*CHN_NUM+1];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+       /* Set up Mic-in mixer */
+       amix_d = mixer->amixers[AMIXER_MIC*CHN_NUM];
+       sum = mixer->sums[SUM_IN_F*CHN_NUM];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+       amix_d = mixer->amixers[AMIXER_MIC*CHN_NUM+1];
+       sum = mixer->sums[SUM_IN_F*CHN_NUM+1];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+       /* Set up S/PDIF-in mixer */
+       amix_d = mixer->amixers[AMIXER_SPDIFI*CHN_NUM];
+       sum = mixer->sums[SUM_IN_F*CHN_NUM];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+       amix_d = mixer->amixers[AMIXER_SPDIFI*CHN_NUM+1];
+       sum = mixer->sums[SUM_IN_F*CHN_NUM+1];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+       /* Set up Master recording mixer */
+       amix_d = mixer->amixers[AMIXER_MASTER_F_C*CHN_NUM];
+       sum = mixer->sums[SUM_IN_F_C*CHN_NUM];
+       amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL);
+       amix_d = mixer->amixers[AMIXER_MASTER_F_C*CHN_NUM+1];
+       sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1];
+       amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL);
+
+       /* Set up PCM-in recording mixer */
+       amix_d = mixer->amixers[AMIXER_PCM_F_C*CHN_NUM];
+       sum = mixer->sums[SUM_IN_F_C*CHN_NUM];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+       amix_d = mixer->amixers[AMIXER_PCM_F_C*CHN_NUM+1];
+       sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+       /* Set up Line-in recording mixer */
+       amix_d = mixer->amixers[AMIXER_LINEIN_C*CHN_NUM];
+       sum = mixer->sums[SUM_IN_F_C*CHN_NUM];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+       amix_d = mixer->amixers[AMIXER_LINEIN_C*CHN_NUM+1];
+       sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+       /* Set up Mic-in recording mixer */
+       amix_d = mixer->amixers[AMIXER_MIC_C*CHN_NUM];
+       sum = mixer->sums[SUM_IN_F_C*CHN_NUM];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+       amix_d = mixer->amixers[AMIXER_MIC_C*CHN_NUM+1];
+       sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+       /* Set up S/PDIF-in recording mixer */
+       amix_d = mixer->amixers[AMIXER_SPDIFI_C*CHN_NUM];
+       sum = mixer->sums[SUM_IN_F_C*CHN_NUM];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+       amix_d = mixer->amixers[AMIXER_SPDIFI_C*CHN_NUM+1];
+       sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+       return 0;
+}
+
+static int mixer_set_input_port(struct amixer *amixer, struct rsc *rsc)
+{
+       amixer->ops->set_input(amixer, rsc);
+       amixer->ops->commit_write(amixer);
+
+       return 0;
+}
+
+static enum CT_AMIXER_CTL port_to_amixer(enum MIXER_PORT_T type)
+{
+       switch (type) {
+       case MIX_WAVE_FRONT:    return AMIXER_WAVE_F;
+       case MIX_WAVE_SURROUND: return AMIXER_WAVE_S;
+       case MIX_WAVE_CENTLFE:  return AMIXER_WAVE_C;
+       case MIX_WAVE_REAR:     return AMIXER_WAVE_R;
+       case MIX_PCMO_FRONT:    return AMIXER_MASTER_F_C;
+       case MIX_SPDIF_OUT:     return AMIXER_SPDIFO;
+       case MIX_LINE_IN:       return AMIXER_LINEIN;
+       case MIX_MIC_IN:        return AMIXER_MIC;
+       case MIX_SPDIF_IN:      return AMIXER_SPDIFI;
+       case MIX_PCMI_FRONT:    return AMIXER_PCM_F;
+       case MIX_PCMI_SURROUND: return AMIXER_PCM_S;
+       case MIX_PCMI_CENTLFE:  return AMIXER_PCM_C;
+       case MIX_PCMI_REAR:     return AMIXER_PCM_R;
+       default:                return 0;
+       }
+}
+
+static int mixer_get_output_ports(struct ct_mixer *mixer,
+                                 enum MIXER_PORT_T type,
+                                 struct rsc **rleft, struct rsc **rright)
+{
+       enum CT_AMIXER_CTL amix = port_to_amixer(type);
+
+       if (NULL != rleft)
+               *rleft = &((struct amixer *)mixer->amixers[amix*CHN_NUM])->rsc;
+
+       if (NULL != rright)
+               *rright =
+                       &((struct amixer *)mixer->amixers[amix*CHN_NUM+1])->rsc;
+
+       return 0;
+}
+
+static int mixer_set_input_left(struct ct_mixer *mixer,
+                               enum MIXER_PORT_T type, struct rsc *rsc)
+{
+       enum CT_AMIXER_CTL amix = port_to_amixer(type);
+
+       mixer_set_input_port(mixer->amixers[amix*CHN_NUM], rsc);
+       amix = get_recording_amixer(amix);
+       if (amix < NUM_CT_AMIXERS)
+               mixer_set_input_port(mixer->amixers[amix*CHN_NUM], rsc);
+
+       return 0;
+}
+
+static int
+mixer_set_input_right(struct ct_mixer *mixer,
+                     enum MIXER_PORT_T type, struct rsc *rsc)
+{
+       enum CT_AMIXER_CTL amix = port_to_amixer(type);
+
+       mixer_set_input_port(mixer->amixers[amix*CHN_NUM+1], rsc);
+       amix = get_recording_amixer(amix);
+       if (amix < NUM_CT_AMIXERS)
+               mixer_set_input_port(mixer->amixers[amix*CHN_NUM+1], rsc);
+
+       return 0;
+}
+
+int ct_mixer_destroy(struct ct_mixer *mixer)
+{
+       struct sum_mgr *sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM];
+       struct amixer_mgr *amixer_mgr =
+                       (struct amixer_mgr *)mixer->atc->rsc_mgrs[AMIXER];
+       struct amixer *amixer;
+       int i = 0;
+
+       /* Release amixer resources */
+       for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) {
+               if (NULL != mixer->amixers[i]) {
+                       amixer = mixer->amixers[i];
+                       amixer_mgr->put_amixer(amixer_mgr, amixer);
+               }
+       }
+
+       /* Release sum resources */
+       for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) {
+               if (NULL != mixer->sums[i])
+                       sum_mgr->put_sum(sum_mgr, (struct sum *)mixer->sums[i]);
+       }
+
+       /* Release mem assigned to mixer object */
+       kfree(mixer->sums);
+       kfree(mixer->amixers);
+       kfree(mixer);
+
+       return 0;
+}
+
+int ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer)
+{
+       struct ct_mixer *mixer;
+       int err;
+
+       *rmixer = NULL;
+
+       /* Allocate mem for mixer obj */
+       err = ct_mixer_get_mem(&mixer);
+       if (err)
+               return err;
+
+       mixer->switch_state = 0;
+       mixer->atc = atc;
+       /* Set operations */
+       mixer->get_output_ports = mixer_get_output_ports;
+       mixer->set_input_left = mixer_set_input_left;
+       mixer->set_input_right = mixer_set_input_right;
+
+       /* Allocate chip resources for mixer obj */
+       err = ct_mixer_get_resources(mixer);
+       if (err)
+               goto error;
+
+       /* Build internal mixer topology */
+       ct_mixer_topology_build(mixer);
+
+       *rmixer = mixer;
+
+       return 0;
+
+error:
+       ct_mixer_destroy(mixer);
+       return err;
+}
+
+int ct_alsa_mix_create(struct ct_atc *atc,
+                      enum CTALSADEVS device,
+                      const char *device_name)
+{
+       int err;
+
+       /* Create snd kcontrol instances on demand */
+       /* vol_ctl.device = swh_ctl.device = device; */ /* better w/ device 0 */
+       err = ct_mixer_kcontrols_create((struct ct_mixer *)atc->mixer);
+       if (err)
+               return err;
+
+       strcpy(atc->card->mixername, device_name);
+
+       return 0;
+}
diff --git a/sound/pci/ctxfi/ctmixer.h b/sound/pci/ctxfi/ctmixer.h
new file mode 100644 (file)
index 0000000..e2d96eb
--- /dev/null
@@ -0,0 +1,67 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctmixer.h
+ *
+ * @Brief
+ * This file contains the definition of the mixer device functions.
+ *
+ * @Author     Liu Chun
+ * @Date       Mar 28 2008
+ *
+ */
+
+#ifndef CTMIXER_H
+#define CTMIXER_H
+
+#include "ctatc.h"
+#include "ctresource.h"
+
+#define INIT_VOL       0x1c00
+
+enum MIXER_PORT_T {
+       MIX_WAVE_FRONT,
+       MIX_WAVE_REAR,
+       MIX_WAVE_CENTLFE,
+       MIX_WAVE_SURROUND,
+       MIX_SPDIF_OUT,
+       MIX_PCMO_FRONT,
+       MIX_MIC_IN,
+       MIX_LINE_IN,
+       MIX_SPDIF_IN,
+       MIX_PCMI_FRONT,
+       MIX_PCMI_REAR,
+       MIX_PCMI_CENTLFE,
+       MIX_PCMI_SURROUND,
+
+       NUM_MIX_PORTS
+};
+
+/* alsa mixer descriptor */
+struct ct_mixer {
+       struct ct_atc *atc;
+
+       void **amixers;         /* amixer resources for volume control */
+       void **sums;            /* sum resources for signal collection */
+       unsigned int switch_state; /* A bit-map to indicate state of switches */
+
+       int (*get_output_ports)(struct ct_mixer *mixer, enum MIXER_PORT_T type,
+                                 struct rsc **rleft, struct rsc **rright);
+
+       int (*set_input_left)(struct ct_mixer *mixer,
+                             enum MIXER_PORT_T type, struct rsc *rsc);
+       int (*set_input_right)(struct ct_mixer *mixer,
+                              enum MIXER_PORT_T type, struct rsc *rsc);
+};
+
+int ct_alsa_mix_create(struct ct_atc *atc,
+                      enum CTALSADEVS device,
+                      const char *device_name);
+int ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer);
+int ct_mixer_destroy(struct ct_mixer *mixer);
+
+#endif /* CTMIXER_H */
diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c
new file mode 100644 (file)
index 0000000..9e5c0c4
--- /dev/null
@@ -0,0 +1,426 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctpcm.c
+ *
+ * @Brief
+ * This file contains the definition of the pcm device functions.
+ *
+ * @Author     Liu Chun
+ * @Date       Apr 2 2008
+ *
+ */
+
+#include "ctpcm.h"
+#include "cttimer.h"
+#include <sound/pcm.h>
+
+/* Hardware descriptions for playback */
+static struct snd_pcm_hardware ct_pcm_playback_hw = {
+       .info                   = (SNDRV_PCM_INFO_MMAP |
+                                  SNDRV_PCM_INFO_INTERLEAVED |
+                                  SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                  SNDRV_PCM_INFO_MMAP_VALID |
+                                  SNDRV_PCM_INFO_PAUSE),
+       .formats                = (SNDRV_PCM_FMTBIT_U8 |
+                                  SNDRV_PCM_FMTBIT_S16_LE |
+                                  SNDRV_PCM_FMTBIT_S24_3LE |
+                                  SNDRV_PCM_FMTBIT_S32_LE |
+                                  SNDRV_PCM_FMTBIT_FLOAT_LE),
+       .rates                  = (SNDRV_PCM_RATE_CONTINUOUS |
+                                  SNDRV_PCM_RATE_8000_192000),
+       .rate_min               = 8000,
+       .rate_max               = 192000,
+       .channels_min           = 1,
+       .channels_max           = 2,
+       .buffer_bytes_max       = (128*1024),
+       .period_bytes_min       = (64),
+       .period_bytes_max       = (128*1024),
+       .periods_min            = 2,
+       .periods_max            = 1024,
+       .fifo_size              = 0,
+};
+
+static struct snd_pcm_hardware ct_spdif_passthru_playback_hw = {
+       .info                   = (SNDRV_PCM_INFO_MMAP |
+                                  SNDRV_PCM_INFO_INTERLEAVED |
+                                  SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                  SNDRV_PCM_INFO_MMAP_VALID |
+                                  SNDRV_PCM_INFO_PAUSE),
+       .formats                = SNDRV_PCM_FMTBIT_S16_LE,
+       .rates                  = (SNDRV_PCM_RATE_48000 |
+                                  SNDRV_PCM_RATE_44100 |
+                                  SNDRV_PCM_RATE_32000),
+       .rate_min               = 32000,
+       .rate_max               = 48000,
+       .channels_min           = 2,
+       .channels_max           = 2,
+       .buffer_bytes_max       = (128*1024),
+       .period_bytes_min       = (64),
+       .period_bytes_max       = (128*1024),
+       .periods_min            = 2,
+       .periods_max            = 1024,
+       .fifo_size              = 0,
+};
+
+/* Hardware descriptions for capture */
+static struct snd_pcm_hardware ct_pcm_capture_hw = {
+       .info                   = (SNDRV_PCM_INFO_MMAP |
+                                  SNDRV_PCM_INFO_INTERLEAVED |
+                                  SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                  SNDRV_PCM_INFO_PAUSE |
+                                  SNDRV_PCM_INFO_MMAP_VALID),
+       .formats                = (SNDRV_PCM_FMTBIT_U8 |
+                                  SNDRV_PCM_FMTBIT_S16_LE |
+                                  SNDRV_PCM_FMTBIT_S24_3LE |
+                                  SNDRV_PCM_FMTBIT_S32_LE |
+                                  SNDRV_PCM_FMTBIT_FLOAT_LE),
+       .rates                  = (SNDRV_PCM_RATE_CONTINUOUS |
+                                  SNDRV_PCM_RATE_8000_96000),
+       .rate_min               = 8000,
+       .rate_max               = 96000,
+       .channels_min           = 1,
+       .channels_max           = 2,
+       .buffer_bytes_max       = (128*1024),
+       .period_bytes_min       = (384),
+       .period_bytes_max       = (64*1024),
+       .periods_min            = 2,
+       .periods_max            = 1024,
+       .fifo_size              = 0,
+};
+
+static void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm)
+{
+       struct ct_atc_pcm *apcm = atc_pcm;
+
+       if (NULL == apcm->substream)
+               return;
+
+       snd_pcm_period_elapsed(apcm->substream);
+}
+
+static void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime)
+{
+       struct ct_atc_pcm *apcm = runtime->private_data;
+       struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream);
+
+       atc->pcm_release_resources(atc, apcm);
+       ct_timer_instance_free(apcm->timer);
+       kfree(apcm);
+       runtime->private_data = NULL;
+}
+
+/* pcm playback operations */
+static int ct_pcm_playback_open(struct snd_pcm_substream *substream)
+{
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct ct_atc_pcm *apcm;
+       int err;
+
+       apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
+       if (NULL == apcm)
+               return -ENOMEM;
+
+       apcm->substream = substream;
+       apcm->interrupt = ct_atc_pcm_interrupt;
+       runtime->private_data = apcm;
+       runtime->private_free = ct_atc_pcm_free_substream;
+       if (IEC958 == substream->pcm->device) {
+               runtime->hw = ct_spdif_passthru_playback_hw;
+               atc->spdif_out_passthru(atc, 1);
+       } else {
+               runtime->hw = ct_pcm_playback_hw;
+               if (FRONT == substream->pcm->device)
+                       runtime->hw.channels_max = 8;
+       }
+
+       err = snd_pcm_hw_constraint_integer(runtime,
+                                           SNDRV_PCM_HW_PARAM_PERIODS);
+       if (err < 0) {
+               kfree(apcm);
+               return err;
+       }
+       err = snd_pcm_hw_constraint_minmax(runtime,
+                                          SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+                                          1024, UINT_MAX);
+       if (err < 0) {
+               kfree(apcm);
+               return err;
+       }
+
+       apcm->timer = ct_timer_instance_new(atc->timer, apcm);
+       if (!apcm->timer)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static int ct_pcm_playback_close(struct snd_pcm_substream *substream)
+{
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
+
+       /* TODO: Notify mixer inactive. */
+       if (IEC958 == substream->pcm->device)
+               atc->spdif_out_passthru(atc, 0);
+
+       /* The ct_atc_pcm object will be freed by runtime->private_free */
+
+       return 0;
+}
+
+static int ct_pcm_hw_params(struct snd_pcm_substream *substream,
+                                    struct snd_pcm_hw_params *hw_params)
+{
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
+       struct ct_atc_pcm *apcm = substream->runtime->private_data;
+       int err;
+
+       err = snd_pcm_lib_malloc_pages(substream,
+                                       params_buffer_bytes(hw_params));
+       if (err < 0)
+               return err;
+       /* clear previous resources */
+       atc->pcm_release_resources(atc, apcm);
+       return err;
+}
+
+static int ct_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
+       struct ct_atc_pcm *apcm = substream->runtime->private_data;
+
+       /* clear previous resources */
+       atc->pcm_release_resources(atc, apcm);
+       /* Free snd-allocated pages */
+       return snd_pcm_lib_free_pages(substream);
+}
+
+
+static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+       int err;
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct ct_atc_pcm *apcm = runtime->private_data;
+
+       if (IEC958 == substream->pcm->device)
+               err = atc->spdif_passthru_playback_prepare(atc, apcm);
+       else
+               err = atc->pcm_playback_prepare(atc, apcm);
+
+       if (err < 0) {
+               printk(KERN_ERR "ctxfi: Preparing pcm playback failed!!!\n");
+               return err;
+       }
+
+       return 0;
+}
+
+static int
+ct_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct ct_atc_pcm *apcm = runtime->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               atc->pcm_playback_start(atc, apcm);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               atc->pcm_playback_stop(atc, apcm);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static snd_pcm_uframes_t
+ct_pcm_playback_pointer(struct snd_pcm_substream *substream)
+{
+       unsigned long position;
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct ct_atc_pcm *apcm = runtime->private_data;
+
+       /* Read out playback position */
+       position = atc->pcm_playback_position(atc, apcm);
+       position = bytes_to_frames(runtime, position);
+       if (position >= runtime->buffer_size)
+               position = 0;
+       return position;
+}
+
+/* pcm capture operations */
+static int ct_pcm_capture_open(struct snd_pcm_substream *substream)
+{
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct ct_atc_pcm *apcm;
+       int err;
+
+       apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
+       if (NULL == apcm)
+               return -ENOMEM;
+
+       apcm->started = 0;
+       apcm->substream = substream;
+       apcm->interrupt = ct_atc_pcm_interrupt;
+       runtime->private_data = apcm;
+       runtime->private_free = ct_atc_pcm_free_substream;
+       runtime->hw = ct_pcm_capture_hw;
+       runtime->hw.rate_max = atc->rsr * atc->msr;
+
+       err = snd_pcm_hw_constraint_integer(runtime,
+                                           SNDRV_PCM_HW_PARAM_PERIODS);
+       if (err < 0) {
+               kfree(apcm);
+               return err;
+       }
+       err = snd_pcm_hw_constraint_minmax(runtime,
+                                          SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+                                          1024, UINT_MAX);
+       if (err < 0) {
+               kfree(apcm);
+               return err;
+       }
+
+       apcm->timer = ct_timer_instance_new(atc->timer, apcm);
+       if (!apcm->timer)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static int ct_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+       /* The ct_atc_pcm object will be freed by runtime->private_free */
+       /* TODO: Notify mixer inactive. */
+       return 0;
+}
+
+static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+       int err;
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct ct_atc_pcm *apcm = runtime->private_data;
+
+       err = atc->pcm_capture_prepare(atc, apcm);
+       if (err < 0) {
+               printk(KERN_ERR "ctxfi: Preparing pcm capture failed!!!\n");
+               return err;
+       }
+
+       return 0;
+}
+
+static int
+ct_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct ct_atc_pcm *apcm = runtime->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               atc->pcm_capture_start(atc, apcm);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               atc->pcm_capture_stop(atc, apcm);
+               break;
+       default:
+               atc->pcm_capture_stop(atc, apcm);
+               break;
+       }
+
+       return 0;
+}
+
+static snd_pcm_uframes_t
+ct_pcm_capture_pointer(struct snd_pcm_substream *substream)
+{
+       unsigned long position;
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct ct_atc_pcm *apcm = runtime->private_data;
+
+       /* Read out playback position */
+       position = atc->pcm_capture_position(atc, apcm);
+       position = bytes_to_frames(runtime, position);
+       if (position >= runtime->buffer_size)
+               position = 0;
+       return position;
+}
+
+/* PCM operators for playback */
+static struct snd_pcm_ops ct_pcm_playback_ops = {
+       .open           = ct_pcm_playback_open,
+       .close          = ct_pcm_playback_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = ct_pcm_hw_params,
+       .hw_free        = ct_pcm_hw_free,
+       .prepare        = ct_pcm_playback_prepare,
+       .trigger        = ct_pcm_playback_trigger,
+       .pointer        = ct_pcm_playback_pointer,
+       .page           = snd_pcm_sgbuf_ops_page,
+};
+
+/* PCM operators for capture */
+static struct snd_pcm_ops ct_pcm_capture_ops = {
+       .open           = ct_pcm_capture_open,
+       .close          = ct_pcm_capture_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = ct_pcm_hw_params,
+       .hw_free        = ct_pcm_hw_free,
+       .prepare        = ct_pcm_capture_prepare,
+       .trigger        = ct_pcm_capture_trigger,
+       .pointer        = ct_pcm_capture_pointer,
+       .page           = snd_pcm_sgbuf_ops_page,
+};
+
+/* Create ALSA pcm device */
+int ct_alsa_pcm_create(struct ct_atc *atc,
+                      enum CTALSADEVS device,
+                      const char *device_name)
+{
+       struct snd_pcm *pcm;
+       int err;
+       int playback_count, capture_count;
+
+       playback_count = (IEC958 == device) ? 1 : 8;
+       capture_count = (FRONT == device) ? 1 : 0;
+       err = snd_pcm_new(atc->card, "ctxfi", device,
+                         playback_count, capture_count, &pcm);
+       if (err < 0) {
+               printk(KERN_ERR "ctxfi: snd_pcm_new failed!! Err=%d\n", err);
+               return err;
+       }
+
+       pcm->private_data = atc;
+       pcm->info_flags = 0;
+       pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+       strlcpy(pcm->name, device_name, sizeof(pcm->name));
+
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops);
+
+       if (FRONT == device)
+               snd_pcm_set_ops(pcm,
+                               SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops);
+
+       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+                       snd_dma_pci_data(atc->pci), 128*1024, 128*1024);
+
+       return 0;
+}
diff --git a/sound/pci/ctxfi/ctpcm.h b/sound/pci/ctxfi/ctpcm.h
new file mode 100644 (file)
index 0000000..178da0d
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctpcm.h
+ *
+ * @Brief
+ * This file contains the definition of the pcm device functions.
+ *
+ * @Author     Liu Chun
+ * @Date       Mar 28 2008
+ *
+ */
+
+#ifndef CTPCM_H
+#define CTPCM_H
+
+#include "ctatc.h"
+
+int ct_alsa_pcm_create(struct ct_atc *atc,
+                      enum CTALSADEVS device,
+                      const char *device_name);
+
+#endif /* CTPCM_H */
diff --git a/sound/pci/ctxfi/ctresource.c b/sound/pci/ctxfi/ctresource.c
new file mode 100644 (file)
index 0000000..889c495
--- /dev/null
@@ -0,0 +1,301 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctresource.c
+ *
+ * @Brief
+ * This file contains the implementation of some generic helper functions.
+ *
+ * @Author     Liu Chun
+ * @Date       May 15 2008
+ *
+ */
+
+#include "ctresource.h"
+#include "cthardware.h"
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#define AUDIO_SLOT_BLOCK_NUM   256
+
+/* Resource allocation based on bit-map management mechanism */
+static int
+get_resource(u8 *rscs, unsigned int amount,
+            unsigned int multi, unsigned int *ridx)
+{
+       int i, j, k, n;
+
+       /* Check whether there are sufficient resources to meet request. */
+       for (i = 0, n = multi; i < amount; i++) {
+               j = i / 8;
+               k = i % 8;
+               if (rscs[j] & ((u8)1 << k)) {
+                       n = multi;
+                       continue;
+               }
+               if (!(--n))
+                       break; /* found sufficient contiguous resources */
+       }
+
+       if (i >= amount) {
+               /* Can not find sufficient contiguous resources */
+               return -ENOENT;
+       }
+
+       /* Mark the contiguous bits in resource bit-map as used */
+       for (n = multi; n > 0; n--) {
+               j = i / 8;
+               k = i % 8;
+               rscs[j] |= ((u8)1 << k);
+               i--;
+       }
+
+       *ridx = i + 1;
+
+       return 0;
+}
+
+static int put_resource(u8 *rscs, unsigned int multi, unsigned int idx)
+{
+       unsigned int i, j, k, n;
+
+       /* Mark the contiguous bits in resource bit-map as used */
+       for (n = multi, i = idx; n > 0; n--) {
+               j = i / 8;
+               k = i % 8;
+               rscs[j] &= ~((u8)1 << k);
+               i++;
+       }
+
+       return 0;
+}
+
+int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx)
+{
+       int err;
+
+       if (n > mgr->avail)
+               return -ENOENT;
+
+       err = get_resource(mgr->rscs, mgr->amount, n, ridx);
+       if (!err)
+               mgr->avail -= n;
+
+       return err;
+}
+
+int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx)
+{
+       put_resource(mgr->rscs, n, idx);
+       mgr->avail += n;
+
+       return 0;
+}
+
+static unsigned char offset_in_audio_slot_block[NUM_RSCTYP] = {
+       /* SRC channel is at Audio Ring slot 1 every 16 slots. */
+       [SRC]           = 0x1,
+       [AMIXER]        = 0x4,
+       [SUM]           = 0xc,
+};
+
+static int rsc_index(const struct rsc *rsc)
+{
+    return rsc->conj;
+}
+
+static int audio_ring_slot(const struct rsc *rsc)
+{
+    return (rsc->conj << 4) + offset_in_audio_slot_block[rsc->type];
+}
+
+static int rsc_next_conj(struct rsc *rsc)
+{
+       unsigned int i;
+       for (i = 0; (i < 8) && (!(rsc->msr & (0x1 << i))); )
+               i++;
+       rsc->conj += (AUDIO_SLOT_BLOCK_NUM >> i);
+       return rsc->conj;
+}
+
+static int rsc_master(struct rsc *rsc)
+{
+       return rsc->conj = rsc->idx;
+}
+
+static struct rsc_ops rsc_generic_ops = {
+       .index          = rsc_index,
+       .output_slot    = audio_ring_slot,
+       .master         = rsc_master,
+       .next_conj      = rsc_next_conj,
+};
+
+int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw)
+{
+       int err = 0;
+
+       rsc->idx = idx;
+       rsc->conj = idx;
+       rsc->type = type;
+       rsc->msr = msr;
+       rsc->hw = hw;
+       rsc->ops = &rsc_generic_ops;
+       if (NULL == hw) {
+               rsc->ctrl_blk = NULL;
+               return 0;
+       }
+
+       switch (type) {
+       case SRC:
+               err = ((struct hw *)hw)->src_rsc_get_ctrl_blk(&rsc->ctrl_blk);
+               break;
+       case AMIXER:
+               err = ((struct hw *)hw)->
+                               amixer_rsc_get_ctrl_blk(&rsc->ctrl_blk);
+               break;
+       case SRCIMP:
+       case SUM:
+       case DAIO:
+               break;
+       default:
+               printk(KERN_ERR
+                      "ctxfi: Invalid resource type value %d!\n", type);
+               return -EINVAL;
+       }
+
+       if (err) {
+               printk(KERN_ERR
+                      "ctxfi: Failed to get resource control block!\n");
+               return err;
+       }
+
+       return 0;
+}
+
+int rsc_uninit(struct rsc *rsc)
+{
+       if ((NULL != rsc->hw) && (NULL != rsc->ctrl_blk)) {
+               switch (rsc->type) {
+               case SRC:
+                       ((struct hw *)rsc->hw)->
+                               src_rsc_put_ctrl_blk(rsc->ctrl_blk);
+                       break;
+               case AMIXER:
+                       ((struct hw *)rsc->hw)->
+                               amixer_rsc_put_ctrl_blk(rsc->ctrl_blk);
+                       break;
+               case SUM:
+               case DAIO:
+                       break;
+               default:
+                       printk(KERN_ERR "ctxfi: "
+                              "Invalid resource type value %d!\n", rsc->type);
+                       break;
+               }
+
+               rsc->hw = rsc->ctrl_blk = NULL;
+       }
+
+       rsc->idx = rsc->conj = 0;
+       rsc->type = NUM_RSCTYP;
+       rsc->msr = 0;
+
+       return 0;
+}
+
+int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
+                unsigned int amount, void *hw_obj)
+{
+       int err = 0;
+       struct hw *hw = hw_obj;
+
+       mgr->type = NUM_RSCTYP;
+
+       mgr->rscs = kzalloc(((amount + 8 - 1) / 8), GFP_KERNEL);
+       if (NULL == mgr->rscs)
+               return -ENOMEM;
+
+       switch (type) {
+       case SRC:
+               err = hw->src_mgr_get_ctrl_blk(&mgr->ctrl_blk);
+               break;
+       case SRCIMP:
+               err = hw->srcimp_mgr_get_ctrl_blk(&mgr->ctrl_blk);
+               break;
+       case AMIXER:
+               err = hw->amixer_mgr_get_ctrl_blk(&mgr->ctrl_blk);
+               break;
+       case DAIO:
+               err = hw->daio_mgr_get_ctrl_blk(hw, &mgr->ctrl_blk);
+               break;
+       case SUM:
+               break;
+       default:
+               printk(KERN_ERR
+                      "ctxfi: Invalid resource type value %d!\n", type);
+               err = -EINVAL;
+               goto error;
+       }
+
+       if (err) {
+               printk(KERN_ERR
+                      "ctxfi: Failed to get manager control block!\n");
+               goto error;
+       }
+
+       mgr->type = type;
+       mgr->avail = mgr->amount = amount;
+       mgr->hw = hw;
+
+       return 0;
+
+error:
+       kfree(mgr->rscs);
+       return err;
+}
+
+int rsc_mgr_uninit(struct rsc_mgr *mgr)
+{
+       if (NULL != mgr->rscs) {
+               kfree(mgr->rscs);
+               mgr->rscs = NULL;
+       }
+
+       if ((NULL != mgr->hw) && (NULL != mgr->ctrl_blk)) {
+               switch (mgr->type) {
+               case SRC:
+                       ((struct hw *)mgr->hw)->
+                               src_mgr_put_ctrl_blk(mgr->ctrl_blk);
+                       break;
+               case SRCIMP:
+                       ((struct hw *)mgr->hw)->
+                               srcimp_mgr_put_ctrl_blk(mgr->ctrl_blk);
+                       break;
+               case AMIXER:
+                       ((struct hw *)mgr->hw)->
+                               amixer_mgr_put_ctrl_blk(mgr->ctrl_blk);
+                       break;
+               case DAIO:
+                       ((struct hw *)mgr->hw)->
+                               daio_mgr_put_ctrl_blk(mgr->ctrl_blk);
+                       break;
+               case SUM:
+                       break;
+               default:
+                       printk(KERN_ERR "ctxfi: "
+                              "Invalid resource type value %d!\n", mgr->type);
+                       break;
+               }
+
+               mgr->hw = mgr->ctrl_blk = NULL;
+       }
+
+       mgr->type = NUM_RSCTYP;
+       mgr->avail = mgr->amount = 0;
+
+       return 0;
+}
diff --git a/sound/pci/ctxfi/ctresource.h b/sound/pci/ctxfi/ctresource.h
new file mode 100644 (file)
index 0000000..0838c2e
--- /dev/null
@@ -0,0 +1,72 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctresource.h
+ *
+ * @Brief
+ * This file contains the definition of generic hardware resources for
+ * resource management.
+ *
+ * @Author     Liu Chun
+ * @Date       May 13 2008
+ *
+ */
+
+#ifndef CTRESOURCE_H
+#define CTRESOURCE_H
+
+#include <linux/types.h>
+
+enum RSCTYP {
+       SRC,
+       SRCIMP,
+       AMIXER,
+       SUM,
+       DAIO,
+       NUM_RSCTYP      /* This must be the last one and less than 16 */
+};
+
+struct rsc_ops;
+
+struct rsc {
+       u32 idx:12;     /* The index of a resource */
+       u32 type:4;     /* The type (RSCTYP) of a resource */
+       u32 conj:12;    /* Current conjugate index */
+       u32 msr:4;      /* The Master Sample Rate a resource working on */
+       void *ctrl_blk; /* Chip specific control info block for a resource */
+       void *hw;       /* Chip specific object for hardware access means */
+       struct rsc_ops *ops;    /* Generic resource operations */
+};
+
+struct rsc_ops {
+       int (*master)(struct rsc *rsc); /* Move to master resource */
+       int (*next_conj)(struct rsc *rsc); /* Move to next conjugate resource */
+       int (*index)(const struct rsc *rsc); /* Return the index of resource */
+       /* Return the output slot number */
+       int (*output_slot)(const struct rsc *rsc);
+};
+
+int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw);
+int rsc_uninit(struct rsc *rsc);
+
+struct rsc_mgr {
+       enum RSCTYP type; /* The type (RSCTYP) of resource to manage */
+       unsigned int amount; /* The total amount of a kind of resource */
+       unsigned int avail; /* The amount of currently available resources */
+       unsigned char *rscs; /* The bit-map for resource allocation */
+       void *ctrl_blk; /* Chip specific control info block */
+       void *hw; /* Chip specific object for hardware access */
+};
+
+/* Resource management is based on bit-map mechanism */
+int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
+                unsigned int amount, void *hw);
+int rsc_mgr_uninit(struct rsc_mgr *mgr);
+int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx);
+int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx);
+
+#endif /* CTRESOURCE_H */
diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c
new file mode 100644 (file)
index 0000000..e1c145d
--- /dev/null
@@ -0,0 +1,886 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctsrc.c
+ *
+ * @Brief
+ * This file contains the implementation of the Sample Rate Convertor
+ * resource management object.
+ *
+ * @Author     Liu Chun
+ * @Date       May 13 2008
+ *
+ */
+
+#include "ctsrc.h"
+#include "cthardware.h"
+#include <linux/slab.h>
+
+#define SRC_RESOURCE_NUM       64
+#define SRCIMP_RESOURCE_NUM    256
+
+static unsigned int conj_mask;
+
+static int src_default_config_memrd(struct src *src);
+static int src_default_config_memwr(struct src *src);
+static int src_default_config_arcrw(struct src *src);
+
+static int (*src_default_config[3])(struct src *) = {
+       [MEMRD] = src_default_config_memrd,
+       [MEMWR] = src_default_config_memwr,
+       [ARCRW] = src_default_config_arcrw
+};
+
+static int src_set_state(struct src *src, unsigned int state)
+{
+       struct hw *hw;
+
+       hw = src->rsc.hw;
+       hw->src_set_state(src->rsc.ctrl_blk, state);
+
+       return 0;
+}
+
+static int src_set_bm(struct src *src, unsigned int bm)
+{
+       struct hw *hw;
+
+       hw = src->rsc.hw;
+       hw->src_set_bm(src->rsc.ctrl_blk, bm);
+
+       return 0;
+}
+
+static int src_set_sf(struct src *src, unsigned int sf)
+{
+       struct hw *hw;
+
+       hw = src->rsc.hw;
+       hw->src_set_sf(src->rsc.ctrl_blk, sf);
+
+       return 0;
+}
+
+static int src_set_pm(struct src *src, unsigned int pm)
+{
+       struct hw *hw;
+
+       hw = src->rsc.hw;
+       hw->src_set_pm(src->rsc.ctrl_blk, pm);
+
+       return 0;
+}
+
+static int src_set_rom(struct src *src, unsigned int rom)
+{
+       struct hw *hw;
+
+       hw = src->rsc.hw;
+       hw->src_set_rom(src->rsc.ctrl_blk, rom);
+
+       return 0;
+}
+
+static int src_set_vo(struct src *src, unsigned int vo)
+{
+       struct hw *hw;
+
+       hw = src->rsc.hw;
+       hw->src_set_vo(src->rsc.ctrl_blk, vo);
+
+       return 0;
+}
+
+static int src_set_st(struct src *src, unsigned int st)
+{
+       struct hw *hw;
+
+       hw = src->rsc.hw;
+       hw->src_set_st(src->rsc.ctrl_blk, st);
+
+       return 0;
+}
+
+static int src_set_bp(struct src *src, unsigned int bp)
+{
+       struct hw *hw;
+
+       hw = src->rsc.hw;
+       hw->src_set_bp(src->rsc.ctrl_blk, bp);
+
+       return 0;
+}
+
+static int src_set_cisz(struct src *src, unsigned int cisz)
+{
+       struct hw *hw;
+
+       hw = src->rsc.hw;
+       hw->src_set_cisz(src->rsc.ctrl_blk, cisz);
+
+       return 0;
+}
+
+static int src_set_ca(struct src *src, unsigned int ca)
+{
+       struct hw *hw;
+
+       hw = src->rsc.hw;
+       hw->src_set_ca(src->rsc.ctrl_blk, ca);
+
+       return 0;
+}
+
+static int src_set_sa(struct src *src, unsigned int sa)
+{
+       struct hw *hw;
+
+       hw = src->rsc.hw;
+       hw->src_set_sa(src->rsc.ctrl_blk, sa);
+
+       return 0;
+}
+
+static int src_set_la(struct src *src, unsigned int la)
+{
+       struct hw *hw;
+
+       hw = src->rsc.hw;
+       hw->src_set_la(src->rsc.ctrl_blk, la);
+
+       return 0;
+}
+
+static int src_set_pitch(struct src *src, unsigned int pitch)
+{
+       struct hw *hw;
+
+       hw = src->rsc.hw;
+       hw->src_set_pitch(src->rsc.ctrl_blk, pitch);
+
+       return 0;
+}
+
+static int src_set_clear_zbufs(struct src *src)
+{
+       struct hw *hw;
+
+       hw = src->rsc.hw;
+       hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1);
+
+       return 0;
+}
+
+static int src_commit_write(struct src *src)
+{
+       struct hw *hw;
+       int i;
+       unsigned int dirty = 0;
+
+       hw = src->rsc.hw;
+       src->rsc.ops->master(&src->rsc);
+       if (src->rsc.msr > 1) {
+               /* Save dirty flags for conjugate resource programming */
+               dirty = hw->src_get_dirty(src->rsc.ctrl_blk) & conj_mask;
+       }
+       hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
+                                               src->rsc.ctrl_blk);
+
+       /* Program conjugate parameter mixer resources */
+       if (MEMWR == src->mode)
+               return 0;
+
+       for (i = 1; i < src->rsc.msr; i++) {
+               src->rsc.ops->next_conj(&src->rsc);
+               hw->src_set_dirty(src->rsc.ctrl_blk, dirty);
+               hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
+                                                       src->rsc.ctrl_blk);
+       }
+       src->rsc.ops->master(&src->rsc);
+
+       return 0;
+}
+
+static int src_get_ca(struct src *src)
+{
+       struct hw *hw;
+
+       hw = src->rsc.hw;
+       return hw->src_get_ca(hw, src->rsc.ops->index(&src->rsc),
+                                               src->rsc.ctrl_blk);
+}
+
+static int src_init(struct src *src)
+{
+       src_default_config[src->mode](src);
+
+       return 0;
+}
+
+static struct src *src_next_interleave(struct src *src)
+{
+       return src->intlv;
+}
+
+static int src_default_config_memrd(struct src *src)
+{
+       struct hw *hw = src->rsc.hw;
+       unsigned int rsr, msr;
+
+       hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF);
+       hw->src_set_bm(src->rsc.ctrl_blk, 1);
+       for (rsr = 0, msr = src->rsc.msr; msr > 1; msr >>= 1)
+               rsr++;
+
+       hw->src_set_rsr(src->rsc.ctrl_blk, rsr);
+       hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_S16);
+       hw->src_set_wr(src->rsc.ctrl_blk, 0);
+       hw->src_set_pm(src->rsc.ctrl_blk, 0);
+       hw->src_set_rom(src->rsc.ctrl_blk, 0);
+       hw->src_set_vo(src->rsc.ctrl_blk, 0);
+       hw->src_set_st(src->rsc.ctrl_blk, 0);
+       hw->src_set_ilsz(src->rsc.ctrl_blk, src->multi - 1);
+       hw->src_set_cisz(src->rsc.ctrl_blk, 0x80);
+       hw->src_set_sa(src->rsc.ctrl_blk, 0x0);
+       hw->src_set_la(src->rsc.ctrl_blk, 0x1000);
+       hw->src_set_ca(src->rsc.ctrl_blk, 0x80);
+       hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000);
+       hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1);
+
+       src->rsc.ops->master(&src->rsc);
+       hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
+                                               src->rsc.ctrl_blk);
+
+       for (msr = 1; msr < src->rsc.msr; msr++) {
+               src->rsc.ops->next_conj(&src->rsc);
+               hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000);
+               hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
+                                                       src->rsc.ctrl_blk);
+       }
+       src->rsc.ops->master(&src->rsc);
+
+       return 0;
+}
+
+static int src_default_config_memwr(struct src *src)
+{
+       struct hw *hw = src->rsc.hw;
+
+       hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF);
+       hw->src_set_bm(src->rsc.ctrl_blk, 1);
+       hw->src_set_rsr(src->rsc.ctrl_blk, 0);
+       hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_S16);
+       hw->src_set_wr(src->rsc.ctrl_blk, 1);
+       hw->src_set_pm(src->rsc.ctrl_blk, 0);
+       hw->src_set_rom(src->rsc.ctrl_blk, 0);
+       hw->src_set_vo(src->rsc.ctrl_blk, 0);
+       hw->src_set_st(src->rsc.ctrl_blk, 0);
+       hw->src_set_ilsz(src->rsc.ctrl_blk, 0);
+       hw->src_set_cisz(src->rsc.ctrl_blk, 0x80);
+       hw->src_set_sa(src->rsc.ctrl_blk, 0x0);
+       hw->src_set_la(src->rsc.ctrl_blk, 0x1000);
+       hw->src_set_ca(src->rsc.ctrl_blk, 0x80);
+       hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000);
+       hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1);
+
+       src->rsc.ops->master(&src->rsc);
+       hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
+                                               src->rsc.ctrl_blk);
+
+       return 0;
+}
+
+static int src_default_config_arcrw(struct src *src)
+{
+       struct hw *hw = src->rsc.hw;
+       unsigned int rsr, msr;
+       unsigned int dirty;
+
+       hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF);
+       hw->src_set_bm(src->rsc.ctrl_blk, 0);
+       for (rsr = 0, msr = src->rsc.msr; msr > 1; msr >>= 1)
+               rsr++;
+
+       hw->src_set_rsr(src->rsc.ctrl_blk, rsr);
+       hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_F32);
+       hw->src_set_wr(src->rsc.ctrl_blk, 0);
+       hw->src_set_pm(src->rsc.ctrl_blk, 0);
+       hw->src_set_rom(src->rsc.ctrl_blk, 0);
+       hw->src_set_vo(src->rsc.ctrl_blk, 0);
+       hw->src_set_st(src->rsc.ctrl_blk, 0);
+       hw->src_set_ilsz(src->rsc.ctrl_blk, 0);
+       hw->src_set_cisz(src->rsc.ctrl_blk, 0x80);
+       hw->src_set_sa(src->rsc.ctrl_blk, 0x0);
+       /*hw->src_set_sa(src->rsc.ctrl_blk, 0x100);*/
+       hw->src_set_la(src->rsc.ctrl_blk, 0x1000);
+       /*hw->src_set_la(src->rsc.ctrl_blk, 0x03ffffe0);*/
+       hw->src_set_ca(src->rsc.ctrl_blk, 0x80);
+       hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000);
+       hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1);
+
+       dirty = hw->src_get_dirty(src->rsc.ctrl_blk);
+       src->rsc.ops->master(&src->rsc);
+       for (msr = 0; msr < src->rsc.msr; msr++) {
+               hw->src_set_dirty(src->rsc.ctrl_blk, dirty);
+               hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
+                                                       src->rsc.ctrl_blk);
+               src->rsc.ops->next_conj(&src->rsc);
+       }
+       src->rsc.ops->master(&src->rsc);
+
+       return 0;
+}
+
+static struct src_rsc_ops src_rsc_ops = {
+       .set_state              = src_set_state,
+       .set_bm                 = src_set_bm,
+       .set_sf                 = src_set_sf,
+       .set_pm                 = src_set_pm,
+       .set_rom                = src_set_rom,
+       .set_vo                 = src_set_vo,
+       .set_st                 = src_set_st,
+       .set_bp                 = src_set_bp,
+       .set_cisz               = src_set_cisz,
+       .set_ca                 = src_set_ca,
+       .set_sa                 = src_set_sa,
+       .set_la                 = src_set_la,
+       .set_pitch              = src_set_pitch,
+       .set_clr_zbufs          = src_set_clear_zbufs,
+       .commit_write           = src_commit_write,
+       .get_ca                 = src_get_ca,
+       .init                   = src_init,
+       .next_interleave        = src_next_interleave,
+};
+
+static int
+src_rsc_init(struct src *src, u32 idx,
+            const struct src_desc *desc, struct src_mgr *mgr)
+{
+       int err;
+       int i, n;
+       struct src *p;
+
+       n = (MEMRD == desc->mode) ? desc->multi : 1;
+       for (i = 0, p = src; i < n; i++, p++) {
+               err = rsc_init(&p->rsc, idx + i, SRC, desc->msr, mgr->mgr.hw);
+               if (err)
+                       goto error1;
+
+               /* Initialize src specific rsc operations */
+               p->ops = &src_rsc_ops;
+               p->multi = (0 == i) ? desc->multi : 1;
+               p->mode = desc->mode;
+               src_default_config[desc->mode](p);
+               mgr->src_enable(mgr, p);
+               p->intlv = p + 1;
+       }
+       (--p)->intlv = NULL;    /* Set @intlv of the last SRC to NULL */
+
+       mgr->commit_write(mgr);
+
+       return 0;
+
+error1:
+       for (i--, p--; i >= 0; i--, p--) {
+               mgr->src_disable(mgr, p);
+               rsc_uninit(&p->rsc);
+       }
+       mgr->commit_write(mgr);
+       return err;
+}
+
+static int src_rsc_uninit(struct src *src, struct src_mgr *mgr)
+{
+       int i, n;
+       struct src *p;
+
+       n = (MEMRD == src->mode) ? src->multi : 1;
+       for (i = 0, p = src; i < n; i++, p++) {
+               mgr->src_disable(mgr, p);
+               rsc_uninit(&p->rsc);
+               p->multi = 0;
+               p->ops = NULL;
+               p->mode = NUM_SRCMODES;
+               p->intlv = NULL;
+       }
+       mgr->commit_write(mgr);
+
+       return 0;
+}
+
+static int
+get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc)
+{
+       unsigned int idx = SRC_RESOURCE_NUM;
+       int err;
+       struct src *src;
+       unsigned long flags;
+
+       *rsrc = NULL;
+
+       /* Check whether there are sufficient src resources to meet request. */
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       if (MEMRD == desc->mode)
+               err = mgr_get_resource(&mgr->mgr, desc->multi, &idx);
+       else
+               err = mgr_get_resource(&mgr->mgr, 1, &idx);
+
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       if (err) {
+               printk(KERN_ERR "ctxfi: Can't meet SRC resource request!\n");
+               return err;
+       }
+
+       /* Allocate mem for master src resource */
+       if (MEMRD == desc->mode)
+               src = kzalloc(sizeof(*src)*desc->multi, GFP_KERNEL);
+       else
+               src = kzalloc(sizeof(*src), GFP_KERNEL);
+
+       if (NULL == src) {
+               err = -ENOMEM;
+               goto error1;
+       }
+
+       err = src_rsc_init(src, idx, desc, mgr);
+       if (err)
+               goto error2;
+
+       *rsrc = src;
+
+       return 0;
+
+error2:
+       kfree(src);
+error1:
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       if (MEMRD == desc->mode)
+               mgr_put_resource(&mgr->mgr, desc->multi, idx);
+       else
+               mgr_put_resource(&mgr->mgr, 1, idx);
+
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       return err;
+}
+
+static int put_src_rsc(struct src_mgr *mgr, struct src *src)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       src->rsc.ops->master(&src->rsc);
+       if (MEMRD == src->mode)
+               mgr_put_resource(&mgr->mgr, src->multi,
+                                src->rsc.ops->index(&src->rsc));
+       else
+               mgr_put_resource(&mgr->mgr, 1, src->rsc.ops->index(&src->rsc));
+
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       src_rsc_uninit(src, mgr);
+       kfree(src);
+
+       return 0;
+}
+
+static int src_enable_s(struct src_mgr *mgr, struct src *src)
+{
+       struct hw *hw = mgr->mgr.hw;
+       int i;
+
+       src->rsc.ops->master(&src->rsc);
+       for (i = 0; i < src->rsc.msr; i++) {
+               hw->src_mgr_enbs_src(mgr->mgr.ctrl_blk,
+                                    src->rsc.ops->index(&src->rsc));
+               src->rsc.ops->next_conj(&src->rsc);
+       }
+       src->rsc.ops->master(&src->rsc);
+
+       return 0;
+}
+
+static int src_enable(struct src_mgr *mgr, struct src *src)
+{
+       struct hw *hw = mgr->mgr.hw;
+       int i;
+
+       src->rsc.ops->master(&src->rsc);
+       for (i = 0; i < src->rsc.msr; i++) {
+               hw->src_mgr_enb_src(mgr->mgr.ctrl_blk,
+                                   src->rsc.ops->index(&src->rsc));
+               src->rsc.ops->next_conj(&src->rsc);
+       }
+       src->rsc.ops->master(&src->rsc);
+
+       return 0;
+}
+
+static int src_disable(struct src_mgr *mgr, struct src *src)
+{
+       struct hw *hw = mgr->mgr.hw;
+       int i;
+
+       src->rsc.ops->master(&src->rsc);
+       for (i = 0; i < src->rsc.msr; i++) {
+               hw->src_mgr_dsb_src(mgr->mgr.ctrl_blk,
+                                   src->rsc.ops->index(&src->rsc));
+               src->rsc.ops->next_conj(&src->rsc);
+       }
+       src->rsc.ops->master(&src->rsc);
+
+       return 0;
+}
+
+static int src_mgr_commit_write(struct src_mgr *mgr)
+{
+       struct hw *hw = mgr->mgr.hw;
+
+       hw->src_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
+
+       return 0;
+}
+
+int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr)
+{
+       int err, i;
+       struct src_mgr *src_mgr;
+
+       *rsrc_mgr = NULL;
+       src_mgr = kzalloc(sizeof(*src_mgr), GFP_KERNEL);
+       if (NULL == src_mgr)
+               return -ENOMEM;
+
+       err = rsc_mgr_init(&src_mgr->mgr, SRC, SRC_RESOURCE_NUM, hw);
+       if (err)
+               goto error1;
+
+       spin_lock_init(&src_mgr->mgr_lock);
+       conj_mask = ((struct hw *)hw)->src_dirty_conj_mask();
+
+       src_mgr->get_src = get_src_rsc;
+       src_mgr->put_src = put_src_rsc;
+       src_mgr->src_enable_s = src_enable_s;
+       src_mgr->src_enable = src_enable;
+       src_mgr->src_disable = src_disable;
+       src_mgr->commit_write = src_mgr_commit_write;
+
+       /* Disable all SRC resources. */
+       for (i = 0; i < 256; i++)
+               ((struct hw *)hw)->src_mgr_dsb_src(src_mgr->mgr.ctrl_blk, i);
+
+       ((struct hw *)hw)->src_mgr_commit_write(hw, src_mgr->mgr.ctrl_blk);
+
+       *rsrc_mgr = src_mgr;
+
+       return 0;
+
+error1:
+       kfree(src_mgr);
+       return err;
+}
+
+int src_mgr_destroy(struct src_mgr *src_mgr)
+{
+       rsc_mgr_uninit(&src_mgr->mgr);
+       kfree(src_mgr);
+
+       return 0;
+}
+
+/* SRCIMP resource manager operations */
+
+static int srcimp_master(struct rsc *rsc)
+{
+       rsc->conj = 0;
+       return rsc->idx = container_of(rsc, struct srcimp, rsc)->idx[0];
+}
+
+static int srcimp_next_conj(struct rsc *rsc)
+{
+       rsc->conj++;
+       return container_of(rsc, struct srcimp, rsc)->idx[rsc->conj];
+}
+
+static int srcimp_index(const struct rsc *rsc)
+{
+       return container_of(rsc, struct srcimp, rsc)->idx[rsc->conj];
+}
+
+static struct rsc_ops srcimp_basic_rsc_ops = {
+       .master         = srcimp_master,
+       .next_conj      = srcimp_next_conj,
+       .index          = srcimp_index,
+       .output_slot    = NULL,
+};
+
+static int srcimp_map(struct srcimp *srcimp, struct src *src, struct rsc *input)
+{
+       struct imapper *entry;
+       int i;
+
+       srcimp->rsc.ops->master(&srcimp->rsc);
+       src->rsc.ops->master(&src->rsc);
+       input->ops->master(input);
+
+       /* Program master and conjugate resources */
+       for (i = 0; i < srcimp->rsc.msr; i++) {
+               entry = &srcimp->imappers[i];
+               entry->slot = input->ops->output_slot(input);
+               entry->user = src->rsc.ops->index(&src->rsc);
+               entry->addr = srcimp->rsc.ops->index(&srcimp->rsc);
+               srcimp->mgr->imap_add(srcimp->mgr, entry);
+               srcimp->mapped |= (0x1 << i);
+
+               srcimp->rsc.ops->next_conj(&srcimp->rsc);
+               input->ops->next_conj(input);
+       }
+
+       srcimp->rsc.ops->master(&srcimp->rsc);
+       input->ops->master(input);
+
+       return 0;
+}
+
+static int srcimp_unmap(struct srcimp *srcimp)
+{
+       int i;
+
+       /* Program master and conjugate resources */
+       for (i = 0; i < srcimp->rsc.msr; i++) {
+               if (srcimp->mapped & (0x1 << i)) {
+                       srcimp->mgr->imap_delete(srcimp->mgr,
+                                                &srcimp->imappers[i]);
+                       srcimp->mapped &= ~(0x1 << i);
+               }
+       }
+
+       return 0;
+}
+
+static struct srcimp_rsc_ops srcimp_ops = {
+       .map = srcimp_map,
+       .unmap = srcimp_unmap
+};
+
+static int srcimp_rsc_init(struct srcimp *srcimp,
+                          const struct srcimp_desc *desc,
+                          struct srcimp_mgr *mgr)
+{
+       int err;
+
+       err = rsc_init(&srcimp->rsc, srcimp->idx[0],
+                      SRCIMP, desc->msr, mgr->mgr.hw);
+       if (err)
+               return err;
+
+       /* Reserve memory for imapper nodes */
+       srcimp->imappers = kzalloc(sizeof(struct imapper)*desc->msr,
+                                  GFP_KERNEL);
+       if (NULL == srcimp->imappers) {
+               err = -ENOMEM;
+               goto error1;
+       }
+
+       /* Set srcimp specific operations */
+       srcimp->rsc.ops = &srcimp_basic_rsc_ops;
+       srcimp->ops = &srcimp_ops;
+       srcimp->mgr = mgr;
+
+       srcimp->rsc.ops->master(&srcimp->rsc);
+
+       return 0;
+
+error1:
+       rsc_uninit(&srcimp->rsc);
+       return err;
+}
+
+static int srcimp_rsc_uninit(struct srcimp *srcimp)
+{
+       if (NULL != srcimp->imappers) {
+               kfree(srcimp->imappers);
+               srcimp->imappers = NULL;
+       }
+       srcimp->ops = NULL;
+       srcimp->mgr = NULL;
+       rsc_uninit(&srcimp->rsc);
+
+       return 0;
+}
+
+static int get_srcimp_rsc(struct srcimp_mgr *mgr,
+                         const struct srcimp_desc *desc,
+                         struct srcimp **rsrcimp)
+{
+       int err, i;
+       unsigned int idx;
+       struct srcimp *srcimp;
+       unsigned long flags;
+
+       *rsrcimp = NULL;
+
+       /* Allocate mem for SRCIMP resource */
+       srcimp = kzalloc(sizeof(*srcimp), GFP_KERNEL);
+       if (NULL == srcimp) {
+               err = -ENOMEM;
+               return err;
+       }
+
+       /* Check whether there are sufficient SRCIMP resources. */
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       for (i = 0; i < desc->msr; i++) {
+               err = mgr_get_resource(&mgr->mgr, 1, &idx);
+               if (err)
+                       break;
+
+               srcimp->idx[i] = idx;
+       }
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       if (err) {
+               printk(KERN_ERR "ctxfi: Can't meet SRCIMP resource request!\n");
+               goto error1;
+       }
+
+       err = srcimp_rsc_init(srcimp, desc, mgr);
+       if (err)
+               goto error1;
+
+       *rsrcimp = srcimp;
+
+       return 0;
+
+error1:
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       for (i--; i >= 0; i--)
+               mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]);
+
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       kfree(srcimp);
+       return err;
+}
+
+static int put_srcimp_rsc(struct srcimp_mgr *mgr, struct srcimp *srcimp)
+{
+       unsigned long flags;
+       int i;
+
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       for (i = 0; i < srcimp->rsc.msr; i++)
+               mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]);
+
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       srcimp_rsc_uninit(srcimp);
+       kfree(srcimp);
+
+       return 0;
+}
+
+static int srcimp_map_op(void *data, struct imapper *entry)
+{
+       struct rsc_mgr *mgr = &((struct srcimp_mgr *)data)->mgr;
+       struct hw *hw = mgr->hw;
+
+       hw->srcimp_mgr_set_imaparc(mgr->ctrl_blk, entry->slot);
+       hw->srcimp_mgr_set_imapuser(mgr->ctrl_blk, entry->user);
+       hw->srcimp_mgr_set_imapnxt(mgr->ctrl_blk, entry->next);
+       hw->srcimp_mgr_set_imapaddr(mgr->ctrl_blk, entry->addr);
+       hw->srcimp_mgr_commit_write(mgr->hw, mgr->ctrl_blk);
+
+       return 0;
+}
+
+static int srcimp_imap_add(struct srcimp_mgr *mgr, struct imapper *entry)
+{
+       unsigned long flags;
+       int err;
+
+       spin_lock_irqsave(&mgr->imap_lock, flags);
+       if ((0 == entry->addr) && (mgr->init_imap_added)) {
+               input_mapper_delete(&mgr->imappers,
+                                   mgr->init_imap, srcimp_map_op, mgr);
+               mgr->init_imap_added = 0;
+       }
+       err = input_mapper_add(&mgr->imappers, entry, srcimp_map_op, mgr);
+       spin_unlock_irqrestore(&mgr->imap_lock, flags);
+
+       return err;
+}
+
+static int srcimp_imap_delete(struct srcimp_mgr *mgr, struct imapper *entry)
+{
+       unsigned long flags;
+       int err;
+
+       spin_lock_irqsave(&mgr->imap_lock, flags);
+       err = input_mapper_delete(&mgr->imappers, entry, srcimp_map_op, mgr);
+       if (list_empty(&mgr->imappers)) {
+               input_mapper_add(&mgr->imappers, mgr->init_imap,
+                                srcimp_map_op, mgr);
+               mgr->init_imap_added = 1;
+       }
+       spin_unlock_irqrestore(&mgr->imap_lock, flags);
+
+       return err;
+}
+
+int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr)
+{
+       int err;
+       struct srcimp_mgr *srcimp_mgr;
+       struct imapper *entry;
+
+       *rsrcimp_mgr = NULL;
+       srcimp_mgr = kzalloc(sizeof(*srcimp_mgr), GFP_KERNEL);
+       if (NULL == srcimp_mgr)
+               return -ENOMEM;
+
+       err = rsc_mgr_init(&srcimp_mgr->mgr, SRCIMP, SRCIMP_RESOURCE_NUM, hw);
+       if (err)
+               goto error1;
+
+       spin_lock_init(&srcimp_mgr->mgr_lock);
+       spin_lock_init(&srcimp_mgr->imap_lock);
+       INIT_LIST_HEAD(&srcimp_mgr->imappers);
+       entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+       if (NULL == entry) {
+               err = -ENOMEM;
+               goto error2;
+       }
+       entry->slot = entry->addr = entry->next = entry->user = 0;
+       list_add(&entry->list, &srcimp_mgr->imappers);
+       srcimp_mgr->init_imap = entry;
+       srcimp_mgr->init_imap_added = 1;
+
+       srcimp_mgr->get_srcimp = get_srcimp_rsc;
+       srcimp_mgr->put_srcimp = put_srcimp_rsc;
+       srcimp_mgr->imap_add = srcimp_imap_add;
+       srcimp_mgr->imap_delete = srcimp_imap_delete;
+
+       *rsrcimp_mgr = srcimp_mgr;
+
+       return 0;
+
+error2:
+       rsc_mgr_uninit(&srcimp_mgr->mgr);
+error1:
+       kfree(srcimp_mgr);
+       return err;
+}
+
+int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr)
+{
+       unsigned long flags;
+
+       /* free src input mapper list */
+       spin_lock_irqsave(&srcimp_mgr->imap_lock, flags);
+       free_input_mapper_list(&srcimp_mgr->imappers);
+       spin_unlock_irqrestore(&srcimp_mgr->imap_lock, flags);
+
+       rsc_mgr_uninit(&srcimp_mgr->mgr);
+       kfree(srcimp_mgr);
+
+       return 0;
+}
diff --git a/sound/pci/ctxfi/ctsrc.h b/sound/pci/ctxfi/ctsrc.h
new file mode 100644 (file)
index 0000000..259366a
--- /dev/null
@@ -0,0 +1,149 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctsrc.h
+ *
+ * @Brief
+ * This file contains the definition of the Sample Rate Convertor
+ * resource management object.
+ *
+ * @Author     Liu Chun
+ * @Date       May 13 2008
+ *
+ */
+
+#ifndef CTSRC_H
+#define CTSRC_H
+
+#include "ctresource.h"
+#include "ctimap.h"
+#include <linux/spinlock.h>
+#include <linux/list.h>
+
+#define SRC_STATE_OFF  0x0
+#define SRC_STATE_INIT 0x4
+#define SRC_STATE_RUN  0x5
+
+#define SRC_SF_U8      0x0
+#define SRC_SF_S16     0x1
+#define SRC_SF_S24     0x2
+#define SRC_SF_S32     0x3
+#define SRC_SF_F32     0x4
+
+/* Define the descriptor of a src resource */
+enum SRCMODE {
+       MEMRD,          /* Read data from host memory */
+       MEMWR,          /* Write data to host memory */
+       ARCRW,          /* Read from and write to audio ring channel */
+       NUM_SRCMODES
+};
+
+struct src_rsc_ops;
+
+struct src {
+       struct rsc rsc; /* Basic resource info */
+       struct src *intlv; /* Pointer to next interleaved SRC in a series */
+       struct src_rsc_ops *ops; /* SRC specific operations */
+       /* Number of contiguous srcs for interleaved usage */
+       unsigned char multi;
+       unsigned char mode; /* Working mode of this SRC resource */
+};
+
+struct src_rsc_ops {
+       int (*set_state)(struct src *src, unsigned int state);
+       int (*set_bm)(struct src *src, unsigned int bm);
+       int (*set_sf)(struct src *src, unsigned int sf);
+       int (*set_pm)(struct src *src, unsigned int pm);
+       int (*set_rom)(struct src *src, unsigned int rom);
+       int (*set_vo)(struct src *src, unsigned int vo);
+       int (*set_st)(struct src *src, unsigned int st);
+       int (*set_bp)(struct src *src, unsigned int bp);
+       int (*set_cisz)(struct src *src, unsigned int cisz);
+       int (*set_ca)(struct src *src, unsigned int ca);
+       int (*set_sa)(struct src *src, unsigned int sa);
+       int (*set_la)(struct src *src, unsigned int la);
+       int (*set_pitch)(struct src *src, unsigned int pitch);
+       int (*set_clr_zbufs)(struct src *src);
+       int (*commit_write)(struct src *src);
+       int (*get_ca)(struct src *src);
+       int (*init)(struct src *src);
+       struct src* (*next_interleave)(struct src *src);
+};
+
+/* Define src resource request description info */
+struct src_desc {
+       /* Number of contiguous master srcs for interleaved usage */
+       unsigned char multi;
+       unsigned char msr;
+       unsigned char mode; /* Working mode of the requested srcs */
+};
+
+/* Define src manager object */
+struct src_mgr {
+       struct rsc_mgr mgr;     /* Basic resource manager info */
+       spinlock_t mgr_lock;
+
+        /* request src resource */
+       int (*get_src)(struct src_mgr *mgr,
+                      const struct src_desc *desc, struct src **rsrc);
+       /* return src resource */
+       int (*put_src)(struct src_mgr *mgr, struct src *src);
+       int (*src_enable_s)(struct src_mgr *mgr, struct src *src);
+       int (*src_enable)(struct src_mgr *mgr, struct src *src);
+       int (*src_disable)(struct src_mgr *mgr, struct src *src);
+       int (*commit_write)(struct src_mgr *mgr);
+};
+
+/* Define the descriptor of a SRC Input Mapper resource */
+struct srcimp_mgr;
+struct srcimp_rsc_ops;
+
+struct srcimp {
+       struct rsc rsc;
+       unsigned char idx[8];
+       struct imapper *imappers;
+       unsigned int mapped; /* A bit-map indicating which conj rsc is mapped */
+       struct srcimp_mgr *mgr;
+       struct srcimp_rsc_ops *ops;
+};
+
+struct srcimp_rsc_ops {
+       int (*map)(struct srcimp *srcimp, struct src *user, struct rsc *input);
+       int (*unmap)(struct srcimp *srcimp);
+};
+
+/* Define SRCIMP resource request description info */
+struct srcimp_desc {
+       unsigned int msr;
+};
+
+struct srcimp_mgr {
+       struct rsc_mgr mgr;     /* Basic resource manager info */
+       spinlock_t mgr_lock;
+       spinlock_t imap_lock;
+       struct list_head imappers;
+       struct imapper *init_imap;
+       unsigned int init_imap_added;
+
+        /* request srcimp resource */
+       int (*get_srcimp)(struct srcimp_mgr *mgr,
+                         const struct srcimp_desc *desc,
+                         struct srcimp **rsrcimp);
+       /* return srcimp resource */
+       int (*put_srcimp)(struct srcimp_mgr *mgr, struct srcimp *srcimp);
+       int (*imap_add)(struct srcimp_mgr *mgr, struct imapper *entry);
+       int (*imap_delete)(struct srcimp_mgr *mgr, struct imapper *entry);
+};
+
+/* Constructor and destructor of SRC resource manager */
+int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr);
+int src_mgr_destroy(struct src_mgr *src_mgr);
+/* Constructor and destructor of SRCIMP resource manager */
+int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrc_mgr);
+int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr);
+
+#endif /* CTSRC_H */
diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c
new file mode 100644 (file)
index 0000000..779c6c3
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+ * PCM timer handling on ctxfi
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ */
+
+#include <linux/slab.h>
+#include <linux/math64.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "ctatc.h"
+#include "cthardware.h"
+#include "cttimer.h"
+
+static int use_system_timer;
+MODULE_PARM_DESC(use_system_timer, "Foce to use system-timer");
+module_param(use_system_timer, bool, S_IRUGO);
+
+struct ct_timer_ops {
+       void (*init)(struct ct_timer_instance *);
+       void (*prepare)(struct ct_timer_instance *);
+       void (*start)(struct ct_timer_instance *);
+       void (*stop)(struct ct_timer_instance *);
+       void (*free_instance)(struct ct_timer_instance *);
+       void (*interrupt)(struct ct_timer *);
+       void (*free_global)(struct ct_timer *);
+};
+
+/* timer instance -- assigned to each PCM stream */
+struct ct_timer_instance {
+       spinlock_t lock;
+       struct ct_timer *timer_base;
+       struct ct_atc_pcm *apcm;
+       struct snd_pcm_substream *substream;
+       struct timer_list timer;
+       struct list_head instance_list;
+       struct list_head running_list;
+       unsigned int position;
+       unsigned int frag_count;
+       unsigned int running:1;
+       unsigned int need_update:1;
+};
+
+/* timer instance manager */
+struct ct_timer {
+       spinlock_t lock;                /* global timer lock (for xfitimer) */
+       spinlock_t list_lock;           /* lock for instance list */
+       struct ct_atc *atc;
+       struct ct_timer_ops *ops;
+       struct list_head instance_head;
+       struct list_head running_head;
+       unsigned int wc;                /* current wallclock */
+       unsigned int irq_handling:1;    /* in IRQ handling */
+       unsigned int reprogram:1;       /* need to reprogram the internval */
+       unsigned int running:1;         /* global timer running */
+};
+
+
+/*
+ * system-timer-based updates
+ */
+
+static void ct_systimer_callback(unsigned long data)
+{
+       struct ct_timer_instance *ti = (struct ct_timer_instance *)data;
+       struct snd_pcm_substream *substream = ti->substream;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct ct_atc_pcm *apcm = ti->apcm;
+       unsigned int period_size = runtime->period_size;
+       unsigned int buffer_size = runtime->buffer_size;
+       unsigned long flags;
+       unsigned int position, dist, interval;
+
+       position = substream->ops->pointer(substream);
+       dist = (position + buffer_size - ti->position) % buffer_size;
+       if (dist >= period_size ||
+           position / period_size != ti->position / period_size) {
+               apcm->interrupt(apcm);
+               ti->position = position;
+       }
+       /* Add extra HZ*5/1000 to avoid overrun issue when recording
+        * at 8kHz in 8-bit format or at 88kHz in 24-bit format. */
+       interval = ((period_size - (position % period_size))
+                  * HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000;
+       spin_lock_irqsave(&ti->lock, flags);
+       if (ti->running)
+               mod_timer(&ti->timer, jiffies + interval);
+       spin_unlock_irqrestore(&ti->lock, flags);
+}
+
+static void ct_systimer_init(struct ct_timer_instance *ti)
+{
+       setup_timer(&ti->timer, ct_systimer_callback,
+                   (unsigned long)ti);
+}
+
+static void ct_systimer_start(struct ct_timer_instance *ti)
+{
+       struct snd_pcm_runtime *runtime = ti->substream->runtime;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ti->lock, flags);
+       ti->running = 1;
+       mod_timer(&ti->timer,
+                 jiffies + (runtime->period_size * HZ +
+                            (runtime->rate - 1)) / runtime->rate);
+       spin_unlock_irqrestore(&ti->lock, flags);
+}
+
+static void ct_systimer_stop(struct ct_timer_instance *ti)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&ti->lock, flags);
+       ti->running = 0;
+       del_timer(&ti->timer);
+       spin_unlock_irqrestore(&ti->lock, flags);
+}
+
+static void ct_systimer_prepare(struct ct_timer_instance *ti)
+{
+       ct_systimer_stop(ti);
+       try_to_del_timer_sync(&ti->timer);
+}
+
+#define ct_systimer_free       ct_systimer_prepare
+
+static struct ct_timer_ops ct_systimer_ops = {
+       .init = ct_systimer_init,
+       .free_instance = ct_systimer_free,
+       .prepare = ct_systimer_prepare,
+       .start = ct_systimer_start,
+       .stop = ct_systimer_stop,
+};
+
+
+/*
+ * Handling multiple streams using a global emu20k1 timer irq
+ */
+
+#define CT_TIMER_FREQ  48000
+#define MIN_TICKS      1
+#define MAX_TICKS      ((1 << 13) - 1)
+
+static void ct_xfitimer_irq_rearm(struct ct_timer *atimer, int ticks)
+{
+       struct hw *hw = atimer->atc->hw;
+       if (ticks > MAX_TICKS)
+               ticks = MAX_TICKS;
+       hw->set_timer_tick(hw, ticks);
+       if (!atimer->running)
+               hw->set_timer_irq(hw, 1);
+       atimer->running = 1;
+}
+
+static void ct_xfitimer_irq_stop(struct ct_timer *atimer)
+{
+       if (atimer->running) {
+               struct hw *hw = atimer->atc->hw;
+               hw->set_timer_irq(hw, 0);
+               hw->set_timer_tick(hw, 0);
+               atimer->running = 0;
+       }
+}
+
+static inline unsigned int ct_xfitimer_get_wc(struct ct_timer *atimer)
+{
+       struct hw *hw = atimer->atc->hw;
+       return hw->get_wc(hw);
+}
+
+/*
+ * reprogram the timer interval;
+ * checks the running instance list and determines the next timer interval.
+ * also updates the each stream position, returns the number of streams
+ * to call snd_pcm_period_elapsed() appropriately
+ *
+ * call this inside the lock and irq disabled
+ */
+static int ct_xfitimer_reprogram(struct ct_timer *atimer)
+{
+       struct ct_timer_instance *ti;
+       unsigned int min_intr = (unsigned int)-1;
+       int updates = 0;
+       unsigned int wc, diff;
+
+       if (list_empty(&atimer->running_head)) {
+               ct_xfitimer_irq_stop(atimer);
+               atimer->reprogram = 0; /* clear flag */
+               return 0;
+       }
+
+       wc = ct_xfitimer_get_wc(atimer);
+       diff = wc - atimer->wc;
+       atimer->wc = wc;
+       list_for_each_entry(ti, &atimer->running_head, running_list) {
+               if (ti->frag_count > diff)
+                       ti->frag_count -= diff;
+               else {
+                       unsigned int pos;
+                       unsigned int period_size, rate;
+
+                       period_size = ti->substream->runtime->period_size;
+                       rate = ti->substream->runtime->rate;
+                       pos = ti->substream->ops->pointer(ti->substream);
+                       if (pos / period_size != ti->position / period_size) {
+                               ti->need_update = 1;
+                               ti->position = pos;
+                               updates++;
+                       }
+                       pos %= period_size;
+                       pos = period_size - pos;
+                       ti->frag_count = div_u64((u64)pos * CT_TIMER_FREQ +
+                                                rate - 1, rate);
+               }
+               if (ti->frag_count < min_intr)
+                       min_intr = ti->frag_count;
+       }
+
+       if (min_intr < MIN_TICKS)
+               min_intr = MIN_TICKS;
+       ct_xfitimer_irq_rearm(atimer, min_intr);
+       atimer->reprogram = 0; /* clear flag */
+       return updates;
+}
+
+/* look through the instance list and call period_elapsed if needed */
+static void ct_xfitimer_check_period(struct ct_timer *atimer)
+{
+       struct ct_timer_instance *ti;
+       unsigned long flags;
+
+       spin_lock_irqsave(&atimer->list_lock, flags);
+       list_for_each_entry(ti, &atimer->instance_head, instance_list) {
+               if (ti->need_update) {
+                       ti->need_update = 0;
+                       ti->apcm->interrupt(ti->apcm);
+               }
+       }
+       spin_unlock_irqrestore(&atimer->list_lock, flags);
+}
+
+/* Handle timer-interrupt */
+static void ct_xfitimer_callback(struct ct_timer *atimer)
+{
+       int update;
+       unsigned long flags;
+
+       spin_lock_irqsave(&atimer->lock, flags);
+       atimer->irq_handling = 1;
+       do {
+               update = ct_xfitimer_reprogram(atimer);
+               spin_unlock(&atimer->lock);
+               if (update)
+                       ct_xfitimer_check_period(atimer);
+               spin_lock(&atimer->lock);
+       } while (atimer->reprogram);
+       atimer->irq_handling = 0;
+       spin_unlock_irqrestore(&atimer->lock, flags);
+}
+
+static void ct_xfitimer_prepare(struct ct_timer_instance *ti)
+{
+       ti->frag_count = ti->substream->runtime->period_size;
+       ti->need_update = 0;
+}
+
+
+/* start/stop the timer */
+static void ct_xfitimer_update(struct ct_timer *atimer)
+{
+       unsigned long flags;
+       int update;
+
+       spin_lock_irqsave(&atimer->lock, flags);
+       if (atimer->irq_handling) {
+               /* reached from IRQ handler; let it handle later */
+               atimer->reprogram = 1;
+               spin_unlock_irqrestore(&atimer->lock, flags);
+               return;
+       }
+
+       ct_xfitimer_irq_stop(atimer);
+       update = ct_xfitimer_reprogram(atimer);
+       spin_unlock_irqrestore(&atimer->lock, flags);
+       if (update)
+               ct_xfitimer_check_period(atimer);
+}
+
+static void ct_xfitimer_start(struct ct_timer_instance *ti)
+{
+       struct ct_timer *atimer = ti->timer_base;
+       unsigned long flags;
+
+       spin_lock_irqsave(&atimer->lock, flags);
+       if (list_empty(&ti->running_list))
+               atimer->wc = ct_xfitimer_get_wc(atimer);
+       list_add(&ti->running_list, &atimer->running_head);
+       spin_unlock_irqrestore(&atimer->lock, flags);
+       ct_xfitimer_update(atimer);
+}
+
+static void ct_xfitimer_stop(struct ct_timer_instance *ti)
+{
+       struct ct_timer *atimer = ti->timer_base;
+       unsigned long flags;
+
+       spin_lock_irqsave(&atimer->lock, flags);
+       list_del_init(&ti->running_list);
+       ti->need_update = 0;
+       spin_unlock_irqrestore(&atimer->lock, flags);
+       ct_xfitimer_update(atimer);
+}
+
+static void ct_xfitimer_free_global(struct ct_timer *atimer)
+{
+       ct_xfitimer_irq_stop(atimer);
+}
+
+static struct ct_timer_ops ct_xfitimer_ops = {
+       .prepare = ct_xfitimer_prepare,
+       .start = ct_xfitimer_start,
+       .stop = ct_xfitimer_stop,
+       .interrupt = ct_xfitimer_callback,
+       .free_global = ct_xfitimer_free_global,
+};
+
+/*
+ * timer instance
+ */
+
+struct ct_timer_instance *
+ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm)
+{
+       struct ct_timer_instance *ti;
+
+       ti = kzalloc(sizeof(*ti), GFP_KERNEL);
+       if (!ti)
+               return NULL;
+       spin_lock_init(&ti->lock);
+       INIT_LIST_HEAD(&ti->instance_list);
+       INIT_LIST_HEAD(&ti->running_list);
+       ti->timer_base = atimer;
+       ti->apcm = apcm;
+       ti->substream = apcm->substream;
+       if (atimer->ops->init)
+               atimer->ops->init(ti);
+
+       spin_lock_irq(&atimer->list_lock);
+       list_add(&ti->instance_list, &atimer->instance_head);
+       spin_unlock_irq(&atimer->list_lock);
+
+       return ti;
+}
+
+void ct_timer_prepare(struct ct_timer_instance *ti)
+{
+       if (ti->timer_base->ops->prepare)
+               ti->timer_base->ops->prepare(ti);
+       ti->position = 0;
+       ti->running = 0;
+}
+
+void ct_timer_start(struct ct_timer_instance *ti)
+{
+       struct ct_timer *atimer = ti->timer_base;
+       atimer->ops->start(ti);
+}
+
+void ct_timer_stop(struct ct_timer_instance *ti)
+{
+       struct ct_timer *atimer = ti->timer_base;
+       atimer->ops->stop(ti);
+}
+
+void ct_timer_instance_free(struct ct_timer_instance *ti)
+{
+       struct ct_timer *atimer = ti->timer_base;
+
+       atimer->ops->stop(ti); /* to be sure */
+       if (atimer->ops->free_instance)
+               atimer->ops->free_instance(ti);
+
+       spin_lock_irq(&atimer->list_lock);
+       list_del(&ti->instance_list);
+       spin_unlock_irq(&atimer->list_lock);
+
+       kfree(ti);
+}
+
+/*
+ * timer manager
+ */
+
+static void ct_timer_interrupt(void *data, unsigned int status)
+{
+       struct ct_timer *timer = data;
+
+       /* Interval timer interrupt */
+       if ((status & IT_INT) && timer->ops->interrupt)
+               timer->ops->interrupt(timer);
+}
+
+struct ct_timer *ct_timer_new(struct ct_atc *atc)
+{
+       struct ct_timer *atimer;
+       struct hw *hw;
+
+       atimer = kzalloc(sizeof(*atimer), GFP_KERNEL);
+       if (!atimer)
+               return NULL;
+       spin_lock_init(&atimer->lock);
+       spin_lock_init(&atimer->list_lock);
+       INIT_LIST_HEAD(&atimer->instance_head);
+       INIT_LIST_HEAD(&atimer->running_head);
+       atimer->atc = atc;
+       hw = atc->hw;
+       if (!use_system_timer && hw->set_timer_irq) {
+               snd_printd(KERN_INFO "ctxfi: Use xfi-native timer\n");
+               atimer->ops = &ct_xfitimer_ops;
+               hw->irq_callback_data = atimer;
+               hw->irq_callback = ct_timer_interrupt;
+       } else {
+               snd_printd(KERN_INFO "ctxfi: Use system timer\n");
+               atimer->ops = &ct_systimer_ops;
+       }
+       return atimer;
+}
+
+void ct_timer_free(struct ct_timer *atimer)
+{
+       struct hw *hw = atimer->atc->hw;
+       hw->irq_callback = NULL;
+       if (atimer->ops->free_global)
+               atimer->ops->free_global(atimer);
+       kfree(atimer);
+}
+
diff --git a/sound/pci/ctxfi/cttimer.h b/sound/pci/ctxfi/cttimer.h
new file mode 100644 (file)
index 0000000..9793482
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Timer handling
+ */
+
+#ifndef __CTTIMER_H
+#define __CTTIMER_H
+
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+
+struct snd_pcm_substream;
+struct ct_atc;
+struct ct_atc_pcm;
+
+struct ct_timer;
+struct ct_timer_instance;
+
+struct ct_timer *ct_timer_new(struct ct_atc *atc);
+void ct_timer_free(struct ct_timer *atimer);
+
+struct ct_timer_instance *
+ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm);
+void ct_timer_instance_free(struct ct_timer_instance *ti);
+void ct_timer_start(struct ct_timer_instance *ti);
+void ct_timer_stop(struct ct_timer_instance *ti);
+void ct_timer_prepare(struct ct_timer_instance *ti);
+
+#endif /* __CTTIMER_H */
diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c
new file mode 100644 (file)
index 0000000..67665a7
--- /dev/null
@@ -0,0 +1,250 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File    ctvmem.c
+ *
+ * @Brief
+ * This file contains the implementation of virtual memory management object
+ * for card device.
+ *
+ * @Author Liu Chun
+ * @Date Apr 1 2008
+ */
+
+#include "ctvmem.h"
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <sound/pcm.h>
+
+#define CT_PTES_PER_PAGE (CT_PAGE_SIZE / sizeof(void *))
+#define CT_ADDRS_PER_PAGE (CT_PTES_PER_PAGE * CT_PAGE_SIZE)
+
+/* *
+ * Find or create vm block based on requested @size.
+ * @size must be page aligned.
+ * */
+static struct ct_vm_block *
+get_vm_block(struct ct_vm *vm, unsigned int size)
+{
+       struct ct_vm_block *block = NULL, *entry;
+       struct list_head *pos;
+
+       size = CT_PAGE_ALIGN(size);
+       if (size > vm->size) {
+               printk(KERN_ERR "ctxfi: Fail! No sufficient device virtural "
+                                 "memory space available!\n");
+               return NULL;
+       }
+
+       mutex_lock(&vm->lock);
+       list_for_each(pos, &vm->unused) {
+               entry = list_entry(pos, struct ct_vm_block, list);
+               if (entry->size >= size)
+                       break; /* found a block that is big enough */
+       }
+       if (pos == &vm->unused)
+               goto out;
+
+       if (entry->size == size) {
+               /* Move the vm node from unused list to used list directly */
+               list_del(&entry->list);
+               list_add(&entry->list, &vm->used);
+               vm->size -= size;
+               block = entry;
+               goto out;
+       }
+
+       block = kzalloc(sizeof(*block), GFP_KERNEL);
+       if (NULL == block)
+               goto out;
+
+       block->addr = entry->addr;
+       block->size = size;
+       list_add(&block->list, &vm->used);
+       entry->addr += size;
+       entry->size -= size;
+       vm->size -= size;
+
+ out:
+       mutex_unlock(&vm->lock);
+       return block;
+}
+
+static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block)
+{
+       struct ct_vm_block *entry, *pre_ent;
+       struct list_head *pos, *pre;
+
+       block->size = CT_PAGE_ALIGN(block->size);
+
+       mutex_lock(&vm->lock);
+       list_del(&block->list);
+       vm->size += block->size;
+
+       list_for_each(pos, &vm->unused) {
+               entry = list_entry(pos, struct ct_vm_block, list);
+               if (entry->addr >= (block->addr + block->size))
+                       break; /* found a position */
+       }
+       if (pos == &vm->unused) {
+               list_add_tail(&block->list, &vm->unused);
+               entry = block;
+       } else {
+               if ((block->addr + block->size) == entry->addr) {
+                       entry->addr = block->addr;
+                       entry->size += block->size;
+                       kfree(block);
+               } else {
+                       __list_add(&block->list, pos->prev, pos);
+                       entry = block;
+               }
+       }
+
+       pos = &entry->list;
+       pre = pos->prev;
+       while (pre != &vm->unused) {
+               entry = list_entry(pos, struct ct_vm_block, list);
+               pre_ent = list_entry(pre, struct ct_vm_block, list);
+               if ((pre_ent->addr + pre_ent->size) > entry->addr)
+                       break;
+
+               pre_ent->size += entry->size;
+               list_del(pos);
+               kfree(entry);
+               pos = pre;
+               pre = pos->prev;
+       }
+       mutex_unlock(&vm->lock);
+}
+
+/* Map host addr (kmalloced/vmalloced) to device logical addr. */
+static struct ct_vm_block *
+ct_vm_map(struct ct_vm *vm, struct snd_pcm_substream *substream, int size)
+{
+       struct ct_vm_block *block;
+       unsigned int pte_start;
+       unsigned i, pages;
+       unsigned long *ptp;
+
+       block = get_vm_block(vm, size);
+       if (block == NULL) {
+               printk(KERN_ERR "ctxfi: No virtual memory block that is big "
+                                 "enough to allocate!\n");
+               return NULL;
+       }
+
+       ptp = vm->ptp[0];
+       pte_start = (block->addr >> CT_PAGE_SHIFT);
+       pages = block->size >> CT_PAGE_SHIFT;
+       for (i = 0; i < pages; i++) {
+               unsigned long addr;
+               addr = snd_pcm_sgbuf_get_addr(substream, i << CT_PAGE_SHIFT);
+               ptp[pte_start + i] = addr;
+       }
+
+       block->size = size;
+       return block;
+}
+
+static void ct_vm_unmap(struct ct_vm *vm, struct ct_vm_block *block)
+{
+       /* do unmapping */
+       put_vm_block(vm, block);
+}
+
+/* *
+ * return the host (kmalloced) addr of the @index-th device
+ * page talbe page on success, or NULL on failure.
+ * The first returned NULL indicates the termination.
+ * */
+static void *
+ct_get_ptp_virt(struct ct_vm *vm, int index)
+{
+       void *addr;
+
+       addr = (index >= CT_PTP_NUM) ? NULL : vm->ptp[index];
+
+       return addr;
+}
+
+int ct_vm_create(struct ct_vm **rvm)
+{
+       struct ct_vm *vm;
+       struct ct_vm_block *block;
+       int i;
+
+       *rvm = NULL;
+
+       vm = kzalloc(sizeof(*vm), GFP_KERNEL);
+       if (NULL == vm)
+               return -ENOMEM;
+
+       mutex_init(&vm->lock);
+
+       /* Allocate page table pages */
+       for (i = 0; i < CT_PTP_NUM; i++) {
+               vm->ptp[i] = kmalloc(PAGE_SIZE, GFP_KERNEL);
+               if (NULL == vm->ptp[i])
+                       break;
+       }
+       if (!i) {
+               /* no page table pages are allocated */
+               kfree(vm);
+               return -ENOMEM;
+       }
+       vm->size = CT_ADDRS_PER_PAGE * i;
+       /* Initialise remaining ptps */
+       for (; i < CT_PTP_NUM; i++)
+               vm->ptp[i] = NULL;
+
+       vm->map = ct_vm_map;
+       vm->unmap = ct_vm_unmap;
+       vm->get_ptp_virt = ct_get_ptp_virt;
+       INIT_LIST_HEAD(&vm->unused);
+       INIT_LIST_HEAD(&vm->used);
+       block = kzalloc(sizeof(*block), GFP_KERNEL);
+       if (NULL != block) {
+               block->addr = 0;
+               block->size = vm->size;
+               list_add(&block->list, &vm->unused);
+       }
+
+       *rvm = vm;
+       return 0;
+}
+
+/* The caller must ensure no mapping pages are being used
+ * by hardware before calling this function */
+void ct_vm_destroy(struct ct_vm *vm)
+{
+       int i;
+       struct list_head *pos;
+       struct ct_vm_block *entry;
+
+       /* free used and unused list nodes */
+       while (!list_empty(&vm->used)) {
+               pos = vm->used.next;
+               list_del(pos);
+               entry = list_entry(pos, struct ct_vm_block, list);
+               kfree(entry);
+       }
+       while (!list_empty(&vm->unused)) {
+               pos = vm->unused.next;
+               list_del(pos);
+               entry = list_entry(pos, struct ct_vm_block, list);
+               kfree(entry);
+       }
+
+       /* free allocated page table pages */
+       for (i = 0; i < CT_PTP_NUM; i++)
+               kfree(vm->ptp[i]);
+
+       vm->size = 0;
+
+       kfree(vm);
+}
diff --git a/sound/pci/ctxfi/ctvmem.h b/sound/pci/ctxfi/ctvmem.h
new file mode 100644 (file)
index 0000000..01e4fd0
--- /dev/null
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File    ctvmem.h
+ *
+ * @Brief
+ * This file contains the definition of virtual memory management object
+ * for card device.
+ *
+ * @Author Liu Chun
+ * @Date Mar 28 2008
+ */
+
+#ifndef CTVMEM_H
+#define CTVMEM_H
+
+#define CT_PTP_NUM     1       /* num of device page table pages */
+
+#include <linux/mutex.h>
+#include <linux/list.h>
+
+/* The chip can handle the page table of 4k pages
+ * (emu20k1 can handle even 8k pages, but we don't use it right now)
+ */
+#define CT_PAGE_SIZE   4096
+#define CT_PAGE_SHIFT  12
+#define CT_PAGE_MASK   (~(PAGE_SIZE - 1))
+#define CT_PAGE_ALIGN(addr)    ALIGN(addr, CT_PAGE_SIZE)
+
+struct ct_vm_block {
+       unsigned int addr;      /* starting logical addr of this block */
+       unsigned int size;      /* size of this device virtual mem block */
+       struct list_head list;
+};
+
+struct snd_pcm_substream;
+
+/* Virtual memory management object for card device */
+struct ct_vm {
+       void *ptp[CT_PTP_NUM];          /* Device page table pages */
+       unsigned int size;              /* Available addr space in bytes */
+       struct list_head unused;        /* List of unused blocks */
+       struct list_head used;          /* List of used blocks */
+       struct mutex lock;
+
+       /* Map host addr (kmalloced/vmalloced) to device logical addr. */
+       struct ct_vm_block *(*map)(struct ct_vm *, struct snd_pcm_substream *,
+                                  int size);
+       /* Unmap device logical addr area. */
+       void (*unmap)(struct ct_vm *, struct ct_vm_block *block);
+       void *(*get_ptp_virt)(struct ct_vm *vm, int index);
+};
+
+int ct_vm_create(struct ct_vm **rvm);
+void ct_vm_destroy(struct ct_vm *vm);
+
+#endif /* CTVMEM_H */
diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c
new file mode 100644 (file)
index 0000000..2d3dd89
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * xfi linux driver.
+ *
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ */
+
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/moduleparam.h>
+#include <linux/pci_ids.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include "ctatc.h"
+#include "cthardware.h"
+
+MODULE_AUTHOR("Creative Technology Ltd");
+MODULE_DESCRIPTION("X-Fi driver version 1.03");
+MODULE_LICENSE("GPL v2");
+MODULE_SUPPORTED_DEVICE("{{Creative Labs, Sound Blaster X-Fi}");
+
+static unsigned int reference_rate = 48000;
+static unsigned int multiple = 2;
+MODULE_PARM_DESC(reference_rate, "Reference rate (default=48000)");
+module_param(reference_rate, uint, S_IRUGO);
+MODULE_PARM_DESC(multiple, "Rate multiplier (default=2)");
+module_param(multiple, uint, S_IRUGO);
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Creative X-Fi driver");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Creative X-Fi driver");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Creative X-Fi driver");
+
+static struct pci_device_id ct_pci_dev_ids[] = {
+       /* only X-Fi is supported, so... */
+       { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K1),
+         .driver_data = ATC20K1,
+       },
+       { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K2),
+         .driver_data = ATC20K2,
+       },
+       { 0, }
+};
+MODULE_DEVICE_TABLE(pci, ct_pci_dev_ids);
+
+static int __devinit
+ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+       static int dev;
+       struct snd_card *card;
+       struct ct_atc *atc;
+       int err;
+
+       if (dev >= SNDRV_CARDS)
+               return -ENODEV;
+
+       if (!enable[dev]) {
+               dev++;
+               return -ENOENT;
+       }
+       err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+       if (err)
+               return err;
+       if ((reference_rate != 48000) && (reference_rate != 44100)) {
+               printk(KERN_ERR "ctxfi: Invalid reference_rate value %u!!!\n",
+                      reference_rate);
+               printk(KERN_ERR "ctxfi: The valid values for reference_rate "
+                      "are 48000 and 44100, Value 48000 is assumed.\n");
+               reference_rate = 48000;
+       }
+       if ((multiple != 1) && (multiple != 2)) {
+               printk(KERN_ERR "ctxfi: Invalid multiple value %u!!!\n",
+                      multiple);
+               printk(KERN_ERR "ctxfi: The valid values for multiple are "
+                      "1 and 2, Value 2 is assumed.\n");
+               multiple = 2;
+       }
+       err = ct_atc_create(card, pci, reference_rate, multiple,
+                           pci_id->driver_data, &atc);
+       if (err < 0)
+               goto error;
+
+       card->private_data = atc;
+
+       /* Create alsa devices supported by this card */
+       err = ct_atc_create_alsa_devs(atc);
+       if (err < 0)
+               goto error;
+
+       strcpy(card->driver, "SB-XFi");
+       strcpy(card->shortname, "Creative X-Fi");
+       snprintf(card->longname, sizeof(card->longname), "%s %s %s",
+                card->shortname, atc->chip_name, atc->model_name);
+
+       err = snd_card_register(card);
+       if (err < 0)
+               goto error;
+
+       pci_set_drvdata(pci, card);
+       dev++;
+
+       return 0;
+
+error:
+       snd_card_free(card);
+       return err;
+}
+
+static void __devexit ct_card_remove(struct pci_dev *pci)
+{
+       snd_card_free(pci_get_drvdata(pci));
+       pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver ct_driver = {
+       .name = "SB-XFi",
+       .id_table = ct_pci_dev_ids,
+       .probe = ct_card_probe,
+       .remove = __devexit_p(ct_card_remove),
+};
+
+static int __init ct_card_init(void)
+{
+       return pci_register_driver(&ct_driver);
+}
+
+static void __exit ct_card_exit(void)
+{
+       pci_unregister_driver(&ct_driver);
+}
+
+module_init(ct_card_init)
+module_exit(ct_card_exit)
index cf2d5636d8be895412e978190a365c63ff7c2bd6..fc5591e7777e3a5f914ae990d89b1c8c60b63562 100644 (file)
@@ -9,15 +9,7 @@ snd-emu10k1-objs := emu10k1.o emu10k1_main.o \
 snd-emu10k1-synth-objs := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o
 snd-emu10k1x-objs := emu10k1x.o
 
-#
-# this function returns:
-#   "m" - CONFIG_SND_SEQUENCER is m
-#   <empty string> - CONFIG_SND_SEQUENCER is undefined
-#   otherwise parameter #1 value
-#
-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
-
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1.o
-obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-emu10k1-synth.o
+obj-$(CONFIG_SND_EMU10K1_SEQ) += snd-emu10k1-synth.o
 obj-$(CONFIG_SND_EMU10K1X) += snd-emu10k1x.o
index 1970f0e70f37ddcb7a19efaa00e912ae94f2521b..4d3ad793e98fb1618048ebf84d47374695604558 100644 (file)
@@ -858,7 +858,6 @@ static int __devinit snd_emu10k1x_pcm(struct emu10k1x *emu, int device, struct s
        }
 
        pcm->info_flags = 0;
-       pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
        switch(device) {
        case 0:
                strcpy(pcm->name, "EMU10K1X Front");
index 78f62fd404c29613c802d4c812fb3d639c28e107..55b83ef73c630e83b2d098a45cce1f570923df7f 100644 (file)
@@ -1736,7 +1736,7 @@ static struct snd_pcm_hardware snd_emu10k1_fx8010_playback =
        .buffer_bytes_max =     (128*1024),
        .period_bytes_min =     1024,
        .period_bytes_max =     (128*1024),
-       .periods_min =          1,
+       .periods_min =          2,
        .periods_max =          1024,
        .fifo_size =            0,
 };
index eb2a19b894a0e650c7a6b79705208667a42f62ec..c710150d5065d8257c52d38a096e0e80e4d97a7e 100644 (file)
@@ -139,6 +139,19 @@ config SND_HDA_CODEC_CONEXANT
          snd-hda-codec-conexant.
          This module is automatically loaded at probing.
 
+config SND_HDA_CODEC_CA0110
+       bool "Build Creative CA0110-IBG codec support"
+       depends on SND_HDA_INTEL
+       default y
+       help
+         Say Y here to include Creative CA0110-IBG codec support in
+         snd-hda-intel driver, found on some Creative X-Fi cards.
+
+         When the HD-audio driver is built as a module, the codec
+         support code is also built as another module,
+         snd-hda-codec-ca0110.
+         This module is automatically loaded at probing.
+
 config SND_HDA_CODEC_CMEDIA
        bool "Build C-Media HD-audio codec support"
        default y
index 50f9d0967251296807caa86eea762f80c56fe3ff..e3081d4586cc4b7779056d654b9f126fb987fc2e 100644 (file)
@@ -13,6 +13,7 @@ snd-hda-codec-analog-objs :=  patch_analog.o
 snd-hda-codec-idt-objs :=      patch_sigmatel.o
 snd-hda-codec-si3054-objs :=   patch_si3054.o
 snd-hda-codec-atihdmi-objs :=  patch_atihdmi.o
+snd-hda-codec-ca0110-objs :=   patch_ca0110.o
 snd-hda-codec-conexant-objs := patch_conexant.o
 snd-hda-codec-via-objs :=      patch_via.o
 snd-hda-codec-nvhdmi-objs :=   patch_nvhdmi.o
@@ -40,6 +41,9 @@ endif
 ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o
 endif
+ifdef CONFIG_SND_HDA_CODEC_CA0110
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o
+endif
 ifdef CONFIG_SND_HDA_CODEC_CONEXANT
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o
 endif
index 4de5bacd392924d94482a45b8885182e966780ab..29272f2e95a07945cf805e6c75867f0f1e2cb762 100644 (file)
@@ -45,6 +45,46 @@ static void snd_hda_generate_beep(struct work_struct *work)
                        AC_VERB_SET_BEEP_CONTROL, beep->tone);
 }
 
+/* (non-standard) Linear beep tone calculation for IDT/STAC codecs 
+ *
+ * The tone frequency of beep generator on IDT/STAC codecs is
+ * defined from the 8bit tone parameter, in Hz,
+ *    freq = 48000 * (257 - tone) / 1024
+ * that is from 12kHz to 93.75kHz in step of 46.875 hz
+ */
+static int beep_linear_tone(struct hda_beep *beep, int hz)
+{
+       hz *= 1000; /* fixed point */
+       hz = hz - DIGBEEP_HZ_MIN;
+       if (hz < 0)
+               hz = 0; /* turn off PC beep*/
+       else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
+               hz = 0xff;
+       else {
+               hz /= DIGBEEP_HZ_STEP;
+               hz++;
+       }
+       return hz;
+}
+
+/* HD-audio standard beep tone parameter calculation
+ *
+ * The tone frequency in Hz is calculated as
+ *   freq = 48000 / (tone * 4)
+ * from 47Hz to 12kHz
+ */
+static int beep_standard_tone(struct hda_beep *beep, int hz)
+{
+       if (hz <= 0)
+               return 0; /* disabled */
+       hz = 12000 / hz;
+       if (hz > 0xff)
+               return 0xff;
+       if (hz <= 0)
+               return 1;
+       return hz;
+}
+
 static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
                                unsigned int code, int hz)
 {
@@ -55,21 +95,14 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
                if (hz)
                        hz = 1000;
        case SND_TONE:
-               hz *= 1000; /* fixed point */
-               hz = hz - DIGBEEP_HZ_MIN;
-               if (hz < 0)
-                       hz = 0; /* turn off PC beep*/
-               else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
-                       hz = 0xff;
-               else {
-                       hz /= DIGBEEP_HZ_STEP;
-                       hz++;
-               }
+               if (beep->linear_tone)
+                       beep->tone = beep_linear_tone(beep, hz);
+               else
+                       beep->tone = beep_standard_tone(beep, hz);
                break;
        default:
                return -1;
        }
-       beep->tone = hz;
 
        /* schedule beep event */
        schedule_work(&beep->beep_work);
index 51bf6a5daf39bd32bf9f1be6c6f94631426878fa..0c3de787c7171a63e31c85fa8d1aa88117a35c48 100644 (file)
@@ -30,8 +30,9 @@ struct hda_beep {
        struct hda_codec *codec;
        char phys[32];
        int tone;
-       int nid;
-       int enabled;
+       hda_nid_t nid;
+       unsigned int enabled:1;
+       unsigned int linear_tone:1;     /* linear tone for IDT/STAC codec */
        struct work_struct beep_work; /* scheduled task for beep event */
 };
 
index 8820faf6c9d82cd556c7de92ec55cb11efa56ebd..562403a2348801aa9f6ba0dfc5b9a1f653347aef 100644 (file)
@@ -48,6 +48,7 @@ static struct hda_vendor_id hda_vendor_ids[] = {
        { 0x1095, "Silicon Image" },
        { 0x10de, "Nvidia" },
        { 0x10ec, "Realtek" },
+       { 0x1102, "Creative" },
        { 0x1106, "VIA" },
        { 0x111d, "IDT" },
        { 0x11c1, "LSI" },
@@ -157,6 +158,39 @@ make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
        return val;
 }
 
+/*
+ * Send and receive a verb
+ */
+static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
+                          unsigned int *res)
+{
+       struct hda_bus *bus = codec->bus;
+       int err;
+
+       if (res)
+               *res = -1;
+ again:
+       snd_hda_power_up(codec);
+       mutex_lock(&bus->cmd_mutex);
+       err = bus->ops.command(bus, cmd);
+       if (!err && res)
+               *res = bus->ops.get_response(bus);
+       mutex_unlock(&bus->cmd_mutex);
+       snd_hda_power_down(codec);
+       if (res && *res == -1 && bus->rirb_error) {
+               if (bus->response_reset) {
+                       snd_printd("hda_codec: resetting BUS due to "
+                                  "fatal communication error\n");
+                       bus->ops.bus_reset(bus);
+               }
+               goto again;
+       }
+       /* clear reset-flag when the communication gets recovered */
+       if (!err)
+               bus->response_reset = 0;
+       return err;
+}
+
 /**
  * snd_hda_codec_read - send a command and get the response
  * @codec: the HDA codec
@@ -173,18 +207,9 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
                                int direct,
                                unsigned int verb, unsigned int parm)
 {
-       struct hda_bus *bus = codec->bus;
+       unsigned cmd = make_codec_cmd(codec, nid, direct, verb, parm);
        unsigned int res;
-
-       res = make_codec_cmd(codec, nid, direct, verb, parm);
-       snd_hda_power_up(codec);
-       mutex_lock(&bus->cmd_mutex);
-       if (!bus->ops.command(bus, res))
-               res = bus->ops.get_response(bus);
-       else
-               res = (unsigned int)-1;
-       mutex_unlock(&bus->cmd_mutex);
-       snd_hda_power_down(codec);
+       codec_exec_verb(codec, cmd, &res);
        return res;
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_read);
@@ -204,17 +229,10 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_read);
 int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
                         unsigned int verb, unsigned int parm)
 {
-       struct hda_bus *bus = codec->bus;
+       unsigned int cmd = make_codec_cmd(codec, nid, direct, verb, parm);
        unsigned int res;
-       int err;
-
-       res = make_codec_cmd(codec, nid, direct, verb, parm);
-       snd_hda_power_up(codec);
-       mutex_lock(&bus->cmd_mutex);
-       err = bus->ops.command(bus, res);
-       mutex_unlock(&bus->cmd_mutex);
-       snd_hda_power_down(codec);
-       return err;
+       return codec_exec_verb(codec, cmd,
+                              codec->bus->sync_write ? &res : NULL);
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_write);
 
@@ -613,7 +631,10 @@ static int get_codec_name(struct hda_codec *codec)
        const struct hda_vendor_id *c;
        const char *vendor = NULL;
        u16 vendor_id = codec->vendor_id >> 16;
-       char tmp[16], name[32];
+       char tmp[16];
+
+       if (codec->vendor_name)
+               goto get_chip_name;
 
        for (c = hda_vendor_ids; c->id; c++) {
                if (c->id == vendor_id) {
@@ -625,14 +646,21 @@ static int get_codec_name(struct hda_codec *codec)
                sprintf(tmp, "Generic %04x", vendor_id);
                vendor = tmp;
        }
+       codec->vendor_name = kstrdup(vendor, GFP_KERNEL);
+       if (!codec->vendor_name)
+               return -ENOMEM;
+
+ get_chip_name:
+       if (codec->chip_name)
+               return 0;
+
        if (codec->preset && codec->preset->name)
-               snprintf(name, sizeof(name), "%s %s", vendor,
-                        codec->preset->name);
-       else
-               snprintf(name, sizeof(name), "%s ID %x", vendor,
-                        codec->vendor_id & 0xffff);
-       codec->name = kstrdup(name, GFP_KERNEL);
-       if (!codec->name)
+               codec->chip_name = kstrdup(codec->preset->name, GFP_KERNEL);
+       else {
+               sprintf(tmp, "ID %x", codec->vendor_id & 0xffff);
+               codec->chip_name = kstrdup(tmp, GFP_KERNEL);
+       }
+       if (!codec->chip_name)
                return -ENOMEM;
        return 0;
 }
@@ -838,7 +866,8 @@ static void snd_hda_codec_free(struct hda_codec *codec)
        module_put(codec->owner);
        free_hda_cache(&codec->amp_cache);
        free_hda_cache(&codec->cmd_cache);
-       kfree(codec->name);
+       kfree(codec->vendor_name);
+       kfree(codec->chip_name);
        kfree(codec->modelname);
        kfree(codec->wcaps);
        kfree(codec);
@@ -979,15 +1008,16 @@ int snd_hda_codec_configure(struct hda_codec *codec)
        int err;
 
        codec->preset = find_codec_preset(codec);
-       if (!codec->name) {
+       if (!codec->vendor_name || !codec->chip_name) {
                err = get_codec_name(codec);
                if (err < 0)
                        return err;
        }
        /* audio codec should override the mixer name */
        if (codec->afg || !*codec->bus->card->mixername)
-               strlcpy(codec->bus->card->mixername, codec->name,
-                       sizeof(codec->bus->card->mixername));
+               snprintf(codec->bus->card->mixername,
+                        sizeof(codec->bus->card->mixername),
+                        "%s %s", codec->vendor_name, codec->chip_name);
 
        if (is_generic_config(codec)) {
                err = snd_hda_parse_generic_codec(codec);
@@ -1055,6 +1085,8 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream);
 /* FIXME: more better hash key? */
 #define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
 #define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24))
+#define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24))
+#define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24))
 #define INFO_AMP_CAPS  (1<<0)
 #define INFO_AMP_VOL(ch)       (1 << (1 + (ch)))
 
@@ -1145,19 +1177,32 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
 }
 EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
 
-u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
+static unsigned int
+query_caps_hash(struct hda_codec *codec, hda_nid_t nid, u32 key,
+               unsigned int (*func)(struct hda_codec *, hda_nid_t))
 {
        struct hda_amp_info *info;
 
-       info = get_alloc_amp_hash(codec, HDA_HASH_PINCAP_KEY(nid));
+       info = get_alloc_amp_hash(codec, key);
        if (!info)
                return 0;
        if (!info->head.val) {
-               info->amp_caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
                info->head.val |= INFO_AMP_CAPS;
+               info->amp_caps = func(codec, nid);
        }
        return info->amp_caps;
 }
+
+static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
+{
+       return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+}
+
+u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
+{
+       return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid),
+                              read_pin_cap);
+}
 EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
 
 /*
@@ -1432,6 +1477,8 @@ _snd_hda_find_mixer_ctl(struct hda_codec *codec,
        memset(&id, 0, sizeof(id));
        id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
        id.index = idx;
+       if (snd_BUG_ON(strlen(name) >= sizeof(id.name)))
+               return NULL;
        strcpy(id.name, name);
        return snd_ctl_find_id(codec->bus->card, &id);
 }
@@ -2242,28 +2289,22 @@ EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
 int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
                              int direct, unsigned int verb, unsigned int parm)
 {
-       struct hda_bus *bus = codec->bus;
-       unsigned int res;
-       int err;
+       int err = snd_hda_codec_write(codec, nid, direct, verb, parm);
+       struct hda_cache_head *c;
+       u32 key;
 
-       res = make_codec_cmd(codec, nid, direct, verb, parm);
-       snd_hda_power_up(codec);
-       mutex_lock(&bus->cmd_mutex);
-       err = bus->ops.command(bus, res);
-       if (!err) {
-               struct hda_cache_head *c;
-               u32 key;
-               /* parm may contain the verb stuff for get/set amp */
-               verb = verb | (parm >> 8);
-               parm &= 0xff;
-               key = build_cmd_cache_key(nid, verb);
-               c = get_alloc_hash(&codec->cmd_cache, key);
-               if (c)
-                       c->val = parm;
-       }
-       mutex_unlock(&bus->cmd_mutex);
-       snd_hda_power_down(codec);
-       return err;
+       if (err < 0)
+               return err;
+       /* parm may contain the verb stuff for get/set amp */
+       verb = verb | (parm >> 8);
+       parm &= 0xff;
+       key = build_cmd_cache_key(nid, verb);
+       mutex_lock(&codec->bus->cmd_mutex);
+       c = get_alloc_hash(&codec->cmd_cache, key);
+       if (c)
+               c->val = parm;
+       mutex_unlock(&codec->bus->cmd_mutex);
+       return 0;
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
 
@@ -2321,7 +2362,8 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
                if (wcaps & AC_WCAP_POWER) {
                        unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >>
                                AC_WCAP_TYPE_SHIFT;
-                       if (wid_type == AC_WID_PIN) {
+                       if (power_state == AC_PWRST_D3 &&
+                           wid_type == AC_WID_PIN) {
                                unsigned int pincap;
                                /*
                                 * don't power down the widget if it controls
@@ -2333,7 +2375,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
                                                nid, 0,
                                                AC_VERB_GET_EAPD_BTLENABLE, 0);
                                        eapd &= 0x02;
-                                       if (power_state == AC_PWRST_D3 && eapd)
+                                       if (eapd)
                                                continue;
                                }
                        }
@@ -2544,6 +2586,41 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
 }
 EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
 
+static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int val = 0;
+       if (nid != codec->afg &&
+           (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD))
+               val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
+       if (!val || val == -1)
+               val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
+       if (!val || val == -1)
+               return 0;
+       return val;
+}
+
+static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid)
+{
+       return query_caps_hash(codec, nid, HDA_HASH_PARPCM_KEY(nid),
+                              get_pcm_param);
+}
+
+static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
+       if (!streams || streams == -1)
+               streams = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
+       if (!streams || streams == -1)
+               return 0;
+       return streams;
+}
+
+static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
+{
+       return query_caps_hash(codec, nid, HDA_HASH_PARSTR_KEY(nid),
+                              get_stream_param);
+}
+
 /**
  * snd_hda_query_supported_pcm - query the supported PCM rates and formats
  * @codec: the HDA codec
@@ -2562,15 +2639,8 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
 {
        unsigned int i, val, wcaps;
 
-       val = 0;
        wcaps = get_wcaps(codec, nid);
-       if (nid != codec->afg && (wcaps & 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);
+       val = query_pcm_param(codec, nid);
 
        if (ratesp) {
                u32 rates = 0;
@@ -2592,15 +2662,9 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
                u64 formats = 0;
                unsigned int streams, bps;
 
-               streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
-               if (streams == -1)
+               streams = query_stream_param(codec, nid);
+               if (!streams)
                        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) {
@@ -2674,17 +2738,9 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
        int i;
        unsigned int val = 0, rate, stream;
 
-       if (nid != codec->afg &&
-           (get_wcaps(codec, nid) & 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;
-       }
+       val = query_pcm_param(codec, nid);
+       if (!val)
+               return 0;
 
        rate = format & 0xff00;
        for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++)
@@ -2696,12 +2752,8 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
        if (i >= AC_PAR_PCM_RATE_BITS)
                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)
+       stream = query_stream_param(codec, nid);
+       if (!stream)
                return 0;
 
        if (stream & AC_SUPFMT_PCM) {
@@ -3835,11 +3887,10 @@ EXPORT_SYMBOL_HDA(auto_pin_cfg_labels);
 /**
  * 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)
+int snd_hda_suspend(struct hda_bus *bus)
 {
        struct hda_codec *codec;
 
index 2fdecf4b0eb631b89ef15f47ef1f37c3ac2e13ed..cad79efaabc95dfdfa480153fbe7d1dcc076c382 100644 (file)
@@ -574,6 +574,8 @@ struct hda_bus_ops {
        /* attach a PCM stream */
        int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec,
                          struct hda_pcm *pcm);
+       /* reset bus for retry verb */
+       void (*bus_reset)(struct hda_bus *bus);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        /* notify power-up/down from codec to controller */
        void (*pm_notify)(struct hda_bus *bus);
@@ -622,7 +624,13 @@ struct hda_bus {
 
        /* misc op flags */
        unsigned int needs_damn_long_delay :1;
+       unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */
+       unsigned int sync_write:1;      /* sync after verb write */
+       /* status for codec/controller */
        unsigned int shutdown :1;       /* being unloaded */
+       unsigned int rirb_error:1;      /* error in codec communication */
+       unsigned int response_reset:1;  /* controller was reset */
+       unsigned int in_reset:1;        /* during reset operation */
 };
 
 /*
@@ -747,7 +755,8 @@ struct hda_codec {
        /* detected preset */
        const struct hda_codec_preset *preset;
        struct module *owner;
-       const char *name;       /* codec name */
+       const char *vendor_name;        /* codec vendor name */
+       const char *chip_name;          /* codec chip name */
        const char *modelname;  /* model name for preset */
 
        /* set by patch */
@@ -905,7 +914,7 @@ 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_suspend(struct hda_bus *bus);
 int snd_hda_resume(struct hda_bus *bus);
 #endif
 
index 1c57505c2874d152ca4e76e3fbdefae5ecd81383..6812fbe80fa4d4504d46e0ea3de9424ecc9c7ee0 100644 (file)
@@ -242,7 +242,8 @@ CODEC_INFO_SHOW(subsystem_id);
 CODEC_INFO_SHOW(revision_id);
 CODEC_INFO_SHOW(afg);
 CODEC_INFO_SHOW(mfg);
-CODEC_INFO_STR_SHOW(name);
+CODEC_INFO_STR_SHOW(vendor_name);
+CODEC_INFO_STR_SHOW(chip_name);
 CODEC_INFO_STR_SHOW(modelname);
 
 #define CODEC_INFO_STORE(type)                                 \
@@ -275,7 +276,8 @@ static ssize_t type##_store(struct device *dev,                     \
 CODEC_INFO_STORE(vendor_id);
 CODEC_INFO_STORE(subsystem_id);
 CODEC_INFO_STORE(revision_id);
-CODEC_INFO_STR_STORE(name);
+CODEC_INFO_STR_STORE(vendor_name);
+CODEC_INFO_STR_STORE(chip_name);
 CODEC_INFO_STR_STORE(modelname);
 
 #define CODEC_ACTION_STORE(type)                               \
@@ -499,7 +501,8 @@ static struct device_attribute codec_attrs[] = {
        CODEC_ATTR_RW(revision_id),
        CODEC_ATTR_RO(afg),
        CODEC_ATTR_RO(mfg),
-       CODEC_ATTR_RW(name),
+       CODEC_ATTR_RW(vendor_name),
+       CODEC_ATTR_RW(chip_name),
        CODEC_ATTR_RW(modelname),
        CODEC_ATTR_RW(init_verbs),
        CODEC_ATTR_RW(hints),
index 3128e1a6bc65ded0dfbf62c3d48e8b78ecd97b1b..4e9ea70802701247ef09f6d1a6ce4a68164a216f 100644 (file)
@@ -128,21 +128,33 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
                         "{ULI, M5461}}");
 MODULE_DESCRIPTION("Intel HDA driver");
 
+#ifdef CONFIG_SND_VERBOSE_PRINTK
+#define SFX    /* nop */
+#else
 #define SFX    "hda-intel: "
-
+#endif
 
 /*
  * registers
  */
 #define ICH6_REG_GCAP                  0x00
+#define   ICH6_GCAP_64OK       (1 << 0)   /* 64bit address support */
+#define   ICH6_GCAP_NSDO       (3 << 1)   /* # of serial data out signals */
+#define   ICH6_GCAP_BSS                (31 << 3)  /* # of bidirectional streams */
+#define   ICH6_GCAP_ISS                (15 << 8)  /* # of input streams */
+#define   ICH6_GCAP_OSS                (15 << 12) /* # of output streams */
 #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_GCTL_RESET      (1 << 0)   /* controller reset */
+#define   ICH6_GCTL_FCNTRL     (1 << 1)   /* flush control */
+#define   ICH6_GCTL_UNSOL      (1 << 8)   /* accept unsol. response enable */
 #define ICH6_REG_WAKEEN                        0x0c
 #define ICH6_REG_STATESTS              0x0e
 #define ICH6_REG_GSTS                  0x10
+#define   ICH6_GSTS_FSTS       (1 << 1)   /* flush status */
 #define ICH6_REG_INTCTL                        0x20
 #define ICH6_REG_INTSTS                        0x24
 #define ICH6_REG_WALCLK                        0x30
@@ -150,17 +162,27 @@ MODULE_DESCRIPTION("Intel HDA driver");
 #define ICH6_REG_CORBLBASE             0x40
 #define ICH6_REG_CORBUBASE             0x44
 #define ICH6_REG_CORBWP                        0x48
-#define ICH6_REG_CORBRP                        0x4A
+#define ICH6_REG_CORBRP                        0x4a
+#define   ICH6_CORBRP_RST      (1 << 15)  /* read pointer reset */
 #define ICH6_REG_CORBCTL               0x4c
+#define   ICH6_CORBCTL_RUN     (1 << 1)   /* enable DMA */
+#define   ICH6_CORBCTL_CMEIE   (1 << 0)   /* enable memory error irq */
 #define ICH6_REG_CORBSTS               0x4d
+#define   ICH6_CORBSTS_CMEI    (1 << 0)   /* memory error indication */
 #define ICH6_REG_CORBSIZE              0x4e
 
 #define ICH6_REG_RIRBLBASE             0x50
 #define ICH6_REG_RIRBUBASE             0x54
 #define ICH6_REG_RIRBWP                        0x58
+#define   ICH6_RIRBWP_RST      (1 << 15)  /* write pointer reset */
 #define ICH6_REG_RINTCNT               0x5a
 #define ICH6_REG_RIRBCTL               0x5c
+#define   ICH6_RBCTL_IRQ_EN    (1 << 0)   /* enable IRQ */
+#define   ICH6_RBCTL_DMA_EN    (1 << 1)   /* enable DMA */
+#define   ICH6_RBCTL_OVERRUN_EN        (1 << 2)   /* enable overrun irq */
 #define ICH6_REG_RIRBSTS               0x5d
+#define   ICH6_RBSTS_IRQ       (1 << 0)   /* response irq */
+#define   ICH6_RBSTS_OVERRUN   (1 << 2)   /* overrun irq */
 #define ICH6_REG_RIRBSIZE              0x5e
 
 #define ICH6_REG_IC                    0x60
@@ -257,16 +279,6 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
 #define ICH6_INT_CTRL_EN       0x40000000 /* controller interrupt enable bit */
 #define ICH6_INT_GLOBAL_EN     0x80000000 /* global interrupt enable bit */
 
-/* GCTL unsolicited response enable bit */
-#define ICH6_GCTL_UREN         (1<<8)
-
-/* 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
@@ -512,25 +524,25 @@ static void azx_init_cmd_io(struct azx *chip)
        /* 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);
+       azx_writew(chip, CORBRP, ICH6_CORBRP_RST);
        /* enable corb dma */
-       azx_writeb(chip, CORBCTL, ICH6_RBCTL_DMA_EN);
+       azx_writeb(chip, CORBCTL, ICH6_CORBCTL_RUN);
 
        /* RIRB set up */
        chip->rirb.addr = chip->rb.addr + 2048;
        chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
+       chip->rirb.wp = chip->rirb.rp = chip->rirb.cmds = 0;
        azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr);
        azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr));
 
        /* set the rirb size to 256 entries (ULI requires explicitly) */
        azx_writeb(chip, RIRBSIZE, 0x02);
        /* reset the rirb hw write pointer */
-       azx_writew(chip, RIRBWP, ICH6_RBRWP_CLR);
+       azx_writew(chip, RIRBWP, ICH6_RIRBWP_RST);
        /* set N=1, get RIRB response interrupt for new entry */
        azx_writew(chip, RINTCNT, 1);
        /* enable rirb dma and response irq */
        azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN);
-       chip->rirb.rp = chip->rirb.cmds = 0;
 }
 
 static void azx_free_cmd_io(struct azx *chip)
@@ -606,6 +618,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
                }
                if (!chip->rirb.cmds) {
                        smp_rmb();
+                       bus->rirb_error = 0;
                        return chip->rirb.res; /* the last value */
                }
                if (time_after(jiffies, timeout))
@@ -619,19 +632,21 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
        }
 
        if (chip->msi) {
-               snd_printk(KERN_WARNING "hda_intel: No response from codec, "
+               snd_printk(KERN_WARNING SFX "No response from codec, "
                           "disabling MSI: last cmd=0x%08x\n", chip->last_cmd);
                free_irq(chip->irq, chip);
                chip->irq = -1;
                pci_disable_msi(chip->pci);
                chip->msi = 0;
-               if (azx_acquire_irq(chip, 1) < 0)
+               if (azx_acquire_irq(chip, 1) < 0) {
+                       bus->rirb_error = 1;
                        return -1;
+               }
                goto again;
        }
 
        if (!chip->polling_mode) {
-               snd_printk(KERN_WARNING "hda_intel: azx_get_response timeout, "
+               snd_printk(KERN_WARNING SFX "azx_get_response timeout, "
                           "switching to polling mode: last cmd=0x%08x\n",
                           chip->last_cmd);
                chip->polling_mode = 1;
@@ -646,14 +661,23 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
                return -1;
        }
 
+       /* a fatal communication error; need either to reset or to fallback
+        * to the single_cmd mode
+        */
+       bus->rirb_error = 1;
+       if (bus->allow_bus_reset && !bus->response_reset && !bus->in_reset) {
+               bus->response_reset = 1;
+               return -1; /* give a chance to retry */
+       }
+
        snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
                   "switching to single_cmd mode: last cmd=0x%08x\n",
                   chip->last_cmd);
-       chip->rirb.rp = azx_readb(chip, RIRBWP);
-       chip->rirb.cmds = 0;
-       /* switch to single_cmd mode */
        chip->single_cmd = 1;
+       bus->response_reset = 0;
+       /* re-initialize CORB/RIRB */
        azx_free_cmd_io(chip);
+       azx_init_cmd_io(chip);
        return -1;
 }
 
@@ -667,12 +691,34 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
  *       I left the codes, however, for debugging/testing purposes.
  */
 
+/* receive a response */
+static int azx_single_wait_for_response(struct azx *chip)
+{
+       int timeout = 50;
+
+       while (timeout--) {
+               /* check IRV busy bit */
+               if (azx_readw(chip, IRS) & ICH6_IRS_VALID) {
+                       /* reuse rirb.res as the response return value */
+                       chip->rirb.res = azx_readl(chip, IR);
+                       return 0;
+               }
+               udelay(1);
+       }
+       if (printk_ratelimit())
+               snd_printd(SFX "get_response timeout: IRS=0x%x\n",
+                          azx_readw(chip, IRS));
+       chip->rirb.res = -1;
+       return -EIO;
+}
+
 /* send a command */
 static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
 {
        struct azx *chip = bus->private_data;
        int timeout = 50;
 
+       bus->rirb_error = 0;
        while (timeout--) {
                /* check ICB busy bit */
                if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) {
@@ -682,7 +728,7 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
                        azx_writel(chip, IC, val);
                        azx_writew(chip, IRS, azx_readw(chip, IRS) |
                                   ICH6_IRS_BUSY);
-                       return 0;
+                       return azx_single_wait_for_response(chip);
                }
                udelay(1);
        }
@@ -696,18 +742,7 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
 static unsigned int azx_single_get_response(struct hda_bus *bus)
 {
        struct azx *chip = 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);
-       }
-       if (printk_ratelimit())
-               snd_printd(SFX "get_response timeout: IRS=0x%x\n",
-                          azx_readw(chip, IRS));
-       return (unsigned int)-1;
+       return chip->rirb.res;
 }
 
 /*
@@ -775,17 +810,17 @@ static int azx_reset(struct azx *chip)
 
        /* check to see if controller is ready */
        if (!azx_readb(chip, GCTL)) {
-               snd_printd("azx_reset: controller not ready!\n");
+               snd_printd(SFX "azx_reset: controller not ready!\n");
                return -EBUSY;
        }
 
        /* Accept unsolicited responses */
-       azx_writel(chip, GCTL, azx_readl(chip, GCTL) | ICH6_GCTL_UREN);
+       azx_writel(chip, GCTL, azx_readl(chip, GCTL) | ICH6_GCTL_UNSOL);
 
        /* detect codecs */
        if (!chip->codec_mask) {
                chip->codec_mask = azx_readw(chip, STATESTS);
-               snd_printdd("codec_mask = 0x%x\n", chip->codec_mask);
+               snd_printdd(SFX "codec_mask = 0x%x\n", chip->codec_mask);
        }
 
        return 0;
@@ -895,8 +930,7 @@ static void azx_init_chip(struct azx *chip)
        azx_int_enable(chip);
 
        /* initialize the codec command I/O */
-       if (!chip->single_cmd)
-               azx_init_cmd_io(chip);
+       azx_init_cmd_io(chip);
 
        /* program the position buffer */
        azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
@@ -953,12 +987,12 @@ static void azx_init_pci(struct azx *chip)
        case AZX_DRIVER_SCH:
                pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop);
                if (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) {
-                       pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC, \
+                       pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC,
                                snoop & (~INTEL_SCH_HDA_DEVC_NOSNOOP));
                        pci_read_config_word(chip->pci,
                                INTEL_SCH_HDA_DEVC, &snoop);
-                       snd_printdd("HDA snoop disabled, enabling ... %s\n",\
-                               (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) \
+                       snd_printdd(SFX "HDA snoop disabled, enabling ... %s\n",
+                               (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP)
                                ? "Failed" : "OK");
                }
                break;
@@ -1012,7 +1046,7 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
        /* clear rirb int */
        status = azx_readb(chip, RIRBSTS);
        if (status & RIRB_INT_MASK) {
-               if (!chip->single_cmd && (status & RIRB_INT_RESPONSE))
+               if (status & RIRB_INT_RESPONSE)
                        azx_update_rirb(chip);
                azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
        }
@@ -1098,7 +1132,7 @@ static int azx_setup_periods(struct azx *chip,
                                pos_align;
                pos_adj = frames_to_bytes(runtime, pos_adj);
                if (pos_adj >= period_bytes) {
-                       snd_printk(KERN_WARNING "Too big adjustment %d\n",
+                       snd_printk(KERN_WARNING SFX "Too big adjustment %d\n",
                                   bdl_pos_adj[chip->dev_index]);
                        pos_adj = 0;
                } else {
@@ -1122,7 +1156,7 @@ static int azx_setup_periods(struct azx *chip,
        return 0;
 
  error:
-       snd_printk(KERN_ERR "Too many BDL entries: buffer=%d, period=%d\n",
+       snd_printk(KERN_ERR SFX "Too many BDL entries: buffer=%d, period=%d\n",
                   azx_dev->bufsize, period_bytes);
        return -EINVAL;
 }
@@ -1215,7 +1249,7 @@ static int probe_codec(struct azx *chip, int addr)
        chip->probing = 0;
        if (res == -1)
                return -EIO;
-       snd_printdd("hda_intel: codec #%d probed OK\n", addr);
+       snd_printdd(SFX "codec #%d probed OK\n", addr);
        return 0;
 }
 
@@ -1223,6 +1257,26 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
                                 struct hda_pcm *cpcm);
 static void azx_stop_chip(struct azx *chip);
 
+static void azx_bus_reset(struct hda_bus *bus)
+{
+       struct azx *chip = bus->private_data;
+
+       bus->in_reset = 1;
+       azx_stop_chip(chip);
+       azx_init_chip(chip);
+#ifdef CONFIG_PM
+       if (chip->initialized) {
+               int i;
+
+               for (i = 0; i < AZX_MAX_PCMS; i++)
+                       snd_pcm_suspend_all(chip->pcm[i]);
+               snd_hda_suspend(chip->bus);
+               snd_hda_resume(chip->bus);
+       }
+#endif
+       bus->in_reset = 0;
+}
+
 /*
  * Codec initialization
  */
@@ -1246,6 +1300,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
        bus_temp.ops.command = azx_send_cmd;
        bus_temp.ops.get_response = azx_get_response;
        bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
+       bus_temp.ops.bus_reset = azx_bus_reset;
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        bus_temp.power_save = &power_save;
        bus_temp.ops.pm_notify = azx_power_notify;
@@ -1270,8 +1325,8 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
                                /* Some BIOSen give you wrong codec addresses
                                 * that don't exist
                                 */
-                               snd_printk(KERN_WARNING
-                                          "hda_intel: Codec #%d probe error; "
+                               snd_printk(KERN_WARNING SFX
+                                          "Codec #%d probe error; "
                                           "disabling it...\n", c);
                                chip->codec_mask &= ~(1 << c);
                                /* More badly, accessing to a non-existing
@@ -1487,7 +1542,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
        bufsize = snd_pcm_lib_buffer_bytes(substream);
        period_bytes = snd_pcm_lib_period_bytes(substream);
 
-       snd_printdd("azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
+       snd_printdd(SFX "azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
                    bufsize, format_val);
 
        if (bufsize != azx_dev->bufsize ||
@@ -1830,7 +1885,7 @@ azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
                          &pcm);
        if (err < 0)
                return err;
-       strcpy(pcm->name, cpcm->name);
+       strlcpy(pcm->name, cpcm->name, sizeof(pcm->name));
        apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
        if (apcm == NULL)
                return -ENOMEM;
@@ -1973,7 +2028,7 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state)
        for (i = 0; i < AZX_MAX_PCMS; i++)
                snd_pcm_suspend_all(chip->pcm[i]);
        if (chip->initialized)
-               snd_hda_suspend(chip->bus, state);
+               snd_hda_suspend(chip->bus);
        azx_stop_chip(chip);
        if (chip->irq >= 0) {
                free_irq(chip->irq, chip);
@@ -2265,14 +2320,14 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
        synchronize_irq(chip->irq);
 
        gcap = azx_readw(chip, GCAP);
-       snd_printdd("chipset global capabilities = 0x%x\n", gcap);
+       snd_printdd(SFX "chipset global capabilities = 0x%x\n", gcap);
 
        /* ATI chips seems buggy about 64bit DMA addresses */
        if (chip->driver_type == AZX_DRIVER_ATI)
-               gcap &= ~0x01;
+               gcap &= ~ICH6_GCAP_64OK;
 
        /* allow 64bit DMA address if supported by H/W */
-       if ((gcap & 0x01) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
+       if ((gcap & ICH6_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
                pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64));
        else {
                pci_set_dma_mask(pci, DMA_BIT_MASK(32));
@@ -2309,7 +2364,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
        chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev),
                                GFP_KERNEL);
        if (!chip->azx_dev) {
-               snd_printk(KERN_ERR "cannot malloc azx_dev\n");
+               snd_printk(KERN_ERR SFX "cannot malloc azx_dev\n");
                goto errout;
        }
 
@@ -2332,11 +2387,9 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
                goto errout;
        }
        /* allocate CORB/RIRB */
-       if (!chip->single_cmd) {
-               err = azx_alloc_cmd_io(chip);
-               if (err < 0)
-                       goto errout;
-       }
+       err = azx_alloc_cmd_io(chip);
+       if (err < 0)
+               goto errout;
 
        /* initialize streams */
        azx_init_stream(chip);
@@ -2359,9 +2412,11 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
        }
 
        strcpy(card->driver, "HDA-Intel");
-       strcpy(card->shortname, driver_short_names[chip->driver_type]);
-       sprintf(card->longname, "%s at 0x%lx irq %i",
-               card->shortname, chip->addr, chip->irq);
+       strlcpy(card->shortname, driver_short_names[chip->driver_type],
+               sizeof(card->shortname));
+       snprintf(card->longname, sizeof(card->longname),
+                "%s at 0x%lx irq %i",
+                card->shortname, chip->addr, chip->irq);
 
        *rchip = chip;
        return 0;
@@ -2514,6 +2569,20 @@ static struct pci_device_id azx_ids[] = {
        { PCI_DEVICE(0x10de, 0x0d97), .driver_data = AZX_DRIVER_NVIDIA },
        /* Teradici */
        { PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
+       /* Creative X-Fi (CA0110-IBG) */
+#if !defined(CONFIG_SND_CTXFI) && !defined(CONFIG_SND_CTXFI_MODULE)
+       /* the following entry conflicts with snd-ctxfi driver,
+        * as ctxfi driver mutates from HD-audio to native mode with
+        * a special command sequence.
+        */
+       { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID),
+         .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
+         .class_mask = 0xffffff,
+         .driver_data = AZX_DRIVER_GENERIC },
+#else
+       /* this entry seems still valid -- i.e. without emu20kx chip */
+       { PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_GENERIC },
+#endif
        /* AMD Generic, PCI class code and Vendor ID for HD Audio */
        { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
          .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
index 93d7499350c6609f3e4a5a9af6396bf0ac6c8447..418c5d1badaa56a8ea8c1d64b79bb986c6b2ff10 100644 (file)
@@ -466,8 +466,12 @@ static void print_codec_info(struct snd_info_entry *entry,
        hda_nid_t nid;
        int i, nodes;
 
-       snd_iprintf(buffer, "Codec: %s\n",
-                   codec->name ? codec->name : "Not Set");
+       snd_iprintf(buffer, "Codec: ");
+       if (codec->vendor_name && codec->chip_name)
+               snd_iprintf(buffer, "%s %s\n",
+                           codec->vendor_name, codec->chip_name);
+       else
+               snd_iprintf(buffer, "Not Set\n");
        snd_iprintf(buffer, "Address: %d\n", codec->addr);
        snd_iprintf(buffer, "Function Id: 0x%x\n", codec->function_id);
        snd_iprintf(buffer, "Vendor Id: 0x%08x\n", codec->vendor_id);
diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c
new file mode 100644 (file)
index 0000000..392d108
--- /dev/null
@@ -0,0 +1,573 @@
+/*
+ * HD audio interface patch for Creative X-Fi CA0110-IBG chip
+ *
+ * Copyright (c) 2008 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 <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 ca0110_spec {
+       struct auto_pin_cfg autocfg;
+       struct hda_multi_out multiout;
+       hda_nid_t out_pins[AUTO_CFG_MAX_OUTS];
+       hda_nid_t dacs[AUTO_CFG_MAX_OUTS];
+       hda_nid_t hp_dac;
+       hda_nid_t input_pins[AUTO_PIN_LAST];
+       hda_nid_t adcs[AUTO_PIN_LAST];
+       hda_nid_t dig_out;
+       hda_nid_t dig_in;
+       unsigned int num_inputs;
+       const char *input_labels[AUTO_PIN_LAST];
+       struct hda_pcm pcm_rec[2];      /* PCM information */
+};
+
+/*
+ * PCM callbacks
+ */
+static int ca0110_playback_pcm_open(struct hda_pcm_stream *hinfo,
+                                   struct hda_codec *codec,
+                                   struct snd_pcm_substream *substream)
+{
+       struct ca0110_spec *spec = codec->spec;
+       return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+                                            hinfo);
+}
+
+static int ca0110_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                      struct hda_codec *codec,
+                                      unsigned int stream_tag,
+                                      unsigned int format,
+                                      struct snd_pcm_substream *substream)
+{
+       struct ca0110_spec *spec = codec->spec;
+       return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+                                               stream_tag, format, substream);
+}
+
+static int ca0110_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                      struct hda_codec *codec,
+                                      struct snd_pcm_substream *substream)
+{
+       struct ca0110_spec *spec = codec->spec;
+       return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital out
+ */
+static int ca0110_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+                                       struct hda_codec *codec,
+                                       struct snd_pcm_substream *substream)
+{
+       struct ca0110_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int ca0110_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+                                        struct hda_codec *codec,
+                                        struct snd_pcm_substream *substream)
+{
+       struct ca0110_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int ca0110_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                          struct hda_codec *codec,
+                                          unsigned int stream_tag,
+                                          unsigned int format,
+                                          struct snd_pcm_substream *substream)
+{
+       struct ca0110_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
+                                            format, substream);
+}
+
+/*
+ * Analog capture
+ */
+static int ca0110_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                     struct hda_codec *codec,
+                                     unsigned int stream_tag,
+                                     unsigned int format,
+                                     struct snd_pcm_substream *substream)
+{
+       struct ca0110_spec *spec = codec->spec;
+
+       snd_hda_codec_setup_stream(codec, spec->adcs[substream->number],
+                                  stream_tag, 0, format);
+       return 0;
+}
+
+static int ca0110_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                     struct hda_codec *codec,
+                                     struct snd_pcm_substream *substream)
+{
+       struct ca0110_spec *spec = codec->spec;
+
+       snd_hda_codec_cleanup_stream(codec, spec->adcs[substream->number]);
+       return 0;
+}
+
+/*
+ */
+
+static char *dirstr[2] = { "Playback", "Capture" };
+
+static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
+                      int chan, int dir)
+{
+       char namestr[44];
+       int type = dir ? HDA_INPUT : HDA_OUTPUT;
+       struct snd_kcontrol_new knew =
+               HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
+       sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
+       return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+}
+
+static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
+                      int chan, int dir)
+{
+       char namestr[44];
+       int type = dir ? HDA_INPUT : HDA_OUTPUT;
+       struct snd_kcontrol_new knew =
+               HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
+       sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
+       return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+}
+
+#define add_out_switch(codec, nid, pfx)        _add_switch(codec, nid, pfx, 3, 0)
+#define add_out_volume(codec, nid, pfx)        _add_volume(codec, nid, pfx, 3, 0)
+#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1)
+#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1)
+#define add_mono_switch(codec, nid, pfx, chan) \
+       _add_switch(codec, nid, pfx, chan, 0)
+#define add_mono_volume(codec, nid, pfx, chan) \
+       _add_volume(codec, nid, pfx, chan, 0)
+
+static int ca0110_build_controls(struct hda_codec *codec)
+{
+       struct ca0110_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       static char *prefix[AUTO_CFG_MAX_OUTS] = {
+               "Front", "Surround", NULL, "Side", "Multi"
+       };
+       hda_nid_t mutenid;
+       int i, err;
+
+       for (i = 0; i < spec->multiout.num_dacs; i++) {
+               if (get_wcaps(codec, spec->out_pins[i]) & AC_WCAP_OUT_AMP)
+                       mutenid = spec->out_pins[i];
+               else
+                       mutenid = spec->multiout.dac_nids[i];
+               if (!prefix[i]) {
+                       err = add_mono_switch(codec, mutenid,
+                                             "Center", 1);
+                       if (err < 0)
+                               return err;
+                       err = add_mono_switch(codec, mutenid,
+                                             "LFE", 1);
+                       if (err < 0)
+                               return err;
+                       err = add_mono_volume(codec, spec->multiout.dac_nids[i],
+                                             "Center", 1);
+                       if (err < 0)
+                               return err;
+                       err = add_mono_volume(codec, spec->multiout.dac_nids[i],
+                                             "LFE", 1);
+                       if (err < 0)
+                               return err;
+               } else {
+                       err = add_out_switch(codec, mutenid,
+                                            prefix[i]);
+                       if (err < 0)
+                               return err;
+                       err = add_out_volume(codec, spec->multiout.dac_nids[i],
+                                        prefix[i]);
+                       if (err < 0)
+                               return err;
+               }
+       }
+       if (cfg->hp_outs) {
+               if (get_wcaps(codec, cfg->hp_pins[0]) & AC_WCAP_OUT_AMP)
+                       mutenid = cfg->hp_pins[0];
+               else
+                       mutenid = spec->multiout.dac_nids[i];
+
+               err = add_out_switch(codec, mutenid, "Headphone");
+               if (err < 0)
+                       return err;
+               if (spec->hp_dac) {
+                       err = add_out_volume(codec, spec->hp_dac, "Headphone");
+                       if (err < 0)
+                               return err;
+               }
+       }
+       for (i = 0; i < spec->num_inputs; i++) {
+               const char *label = spec->input_labels[i];
+               if (get_wcaps(codec, spec->input_pins[i]) & AC_WCAP_IN_AMP)
+                       mutenid = spec->input_pins[i];
+               else
+                       mutenid = spec->adcs[i];
+               err = add_in_switch(codec, mutenid, label);
+               if (err < 0)
+                       return err;
+               err = add_in_volume(codec, spec->adcs[i], label);
+               if (err < 0)
+                       return err;
+       }
+
+       if (spec->dig_out) {
+               err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out);
+               if (err < 0)
+                       return err;
+               err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
+               if (err < 0)
+                       return err;
+               spec->multiout.share_spdif = 1;
+       }
+       if (spec->dig_in) {
+               err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
+               if (err < 0)
+                       return err;
+               err = add_in_volume(codec, spec->dig_in, "IEC958");
+       }
+       return 0;
+}
+
+/*
+ */
+static struct hda_pcm_stream ca0110_pcm_analog_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 8,
+       .ops = {
+               .open = ca0110_playback_pcm_open,
+               .prepare = ca0110_playback_pcm_prepare,
+               .cleanup = ca0110_playback_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream ca0110_pcm_analog_capture = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .ops = {
+               .prepare = ca0110_capture_pcm_prepare,
+               .cleanup = ca0110_capture_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream ca0110_pcm_digital_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .ops = {
+               .open = ca0110_dig_playback_pcm_open,
+               .close = ca0110_dig_playback_pcm_close,
+               .prepare = ca0110_dig_playback_pcm_prepare
+       },
+};
+
+static struct hda_pcm_stream ca0110_pcm_digital_capture = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+};
+
+static int ca0110_build_pcms(struct hda_codec *codec)
+{
+       struct ca0110_spec *spec = codec->spec;
+       struct hda_pcm *info = spec->pcm_rec;
+
+       codec->pcm_info = info;
+       codec->num_pcms = 0;
+
+       info->name = "CA0110 Analog";
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0110_pcm_analog_playback;
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+               spec->multiout.max_channels;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0110_pcm_analog_capture;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
+       codec->num_pcms++;
+
+       if (!spec->dig_out && !spec->dig_in)
+               return 0;
+
+       info++;
+       info->name = "CA0110 Digital";
+       info->pcm_type = HDA_PCM_TYPE_SPDIF;
+       if (spec->dig_out) {
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+                       ca0110_pcm_digital_playback;
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out;
+       }
+       if (spec->dig_in) {
+               info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+                       ca0110_pcm_digital_capture;
+               info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
+       }
+       codec->num_pcms++;
+
+       return 0;
+}
+
+static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
+{
+       if (pin) {
+               snd_hda_codec_write(codec, pin, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+               if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
+                       snd_hda_codec_write(codec, pin, 0,
+                                           AC_VERB_SET_AMP_GAIN_MUTE,
+                                           AMP_OUT_UNMUTE);
+       }
+       if (dac)
+               snd_hda_codec_write(codec, dac, 0,
+                                   AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
+}
+
+static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
+{
+       if (pin) {
+               snd_hda_codec_write(codec, pin, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80);
+               if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
+                       snd_hda_codec_write(codec, pin, 0,
+                                           AC_VERB_SET_AMP_GAIN_MUTE,
+                                           AMP_IN_UNMUTE(0));
+       }
+       if (adc)
+               snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                                   AMP_IN_UNMUTE(0));
+}
+
+static int ca0110_init(struct hda_codec *codec)
+{
+       struct ca0110_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i;
+
+       for (i = 0; i < spec->multiout.num_dacs; i++)
+               init_output(codec, spec->out_pins[i],
+                           spec->multiout.dac_nids[i]);
+       init_output(codec, cfg->hp_pins[0], spec->hp_dac);
+       init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
+
+       for (i = 0; i < spec->num_inputs; i++)
+               init_input(codec, spec->input_pins[i], spec->adcs[i]);
+       init_input(codec, cfg->dig_in_pin, spec->dig_in);
+       return 0;
+}
+
+static void ca0110_free(struct hda_codec *codec)
+{
+       kfree(codec->spec);
+}
+
+static struct hda_codec_ops ca0110_patch_ops = {
+       .build_controls = ca0110_build_controls,
+       .build_pcms = ca0110_build_pcms,
+       .init = ca0110_init,
+       .free = ca0110_free,
+};
+
+
+static void parse_line_outs(struct hda_codec *codec)
+{
+       struct ca0110_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i, n;
+       unsigned int def_conf;
+       hda_nid_t nid;
+
+       n = 0;
+       for (i = 0; i < cfg->line_outs; i++) {
+               nid = cfg->line_out_pins[i];
+               def_conf = snd_hda_codec_get_pincfg(codec, nid);
+               if (!def_conf)
+                       continue; /* invalid pin */
+               if (snd_hda_get_connections(codec, nid, &spec->dacs[i], 1) != 1)
+                       continue;
+               spec->out_pins[n++] = nid;
+       }
+       spec->multiout.dac_nids = spec->dacs;
+       spec->multiout.num_dacs = n;
+       spec->multiout.max_channels = n * 2;
+}
+
+static void parse_hp_out(struct hda_codec *codec)
+{
+       struct ca0110_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i;
+       unsigned int def_conf;
+       hda_nid_t nid, dac;
+
+       if (!cfg->hp_outs)
+               return;
+       nid = cfg->hp_pins[0];
+       def_conf = snd_hda_codec_get_pincfg(codec, nid);
+       if (!def_conf) {
+               cfg->hp_outs = 0;
+               return;
+       }
+       if (snd_hda_get_connections(codec, nid, &dac, 1) != 1)
+               return;
+
+       for (i = 0; i < cfg->line_outs; i++)
+               if (dac == spec->dacs[i])
+                       break;
+       if (i >= cfg->line_outs) {
+               spec->hp_dac = dac;
+               spec->multiout.hp_nid = dac;
+       }
+}
+
+static void parse_input(struct hda_codec *codec)
+{
+       struct ca0110_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       hda_nid_t nid, pin;
+       int n, i, j;
+
+       n = 0;
+       nid = codec->start_nid;
+       for (i = 0; i < codec->num_nodes; i++, nid++) {
+               unsigned int wcaps = get_wcaps(codec, nid);
+               unsigned int type = (wcaps & AC_WCAP_TYPE) >>
+                       AC_WCAP_TYPE_SHIFT;
+               if (type != AC_WID_AUD_IN)
+                       continue;
+               if (snd_hda_get_connections(codec, nid, &pin, 1) != 1)
+                       continue;
+               if (pin == cfg->dig_in_pin) {
+                       spec->dig_in = nid;
+                       continue;
+               }
+               for (j = 0; j < AUTO_PIN_LAST; j++)
+                       if (cfg->input_pins[j] == pin)
+                               break;
+               if (j >= AUTO_PIN_LAST)
+                       continue;
+               spec->input_pins[n] = pin;
+               spec->input_labels[n] = auto_pin_cfg_labels[j];
+               spec->adcs[n] = nid;
+               n++;
+       }
+       spec->num_inputs = n;
+}
+
+static void parse_digital(struct hda_codec *codec)
+{
+       struct ca0110_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+
+       if (cfg->dig_outs &&
+           snd_hda_get_connections(codec, cfg->dig_out_pins[0],
+                                   &spec->dig_out, 1) == 1)
+               spec->multiout.dig_out_nid = cfg->dig_out_pins[0];
+}
+
+static int ca0110_parse_auto_config(struct hda_codec *codec)
+{
+       struct ca0110_spec *spec = codec->spec;
+       int err;
+
+       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+       if (err < 0)
+               return err;
+
+       parse_line_outs(codec);
+       parse_hp_out(codec);
+       parse_digital(codec);
+       parse_input(codec);
+       return 0;
+}
+
+
+int patch_ca0110(struct hda_codec *codec)
+{
+       struct ca0110_spec *spec;
+       int err;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       codec->spec = spec;
+
+       codec->bus->needs_damn_long_delay = 1;
+
+       err = ca0110_parse_auto_config(codec);
+       if (err < 0)
+               goto error;
+
+       codec->patch_ops = ca0110_patch_ops;
+
+       return 0;
+
+ error:
+       kfree(codec->spec);
+       codec->spec = NULL;
+       return err;
+}
+
+
+/*
+ * patch entries
+ */
+static struct hda_codec_preset snd_hda_preset_ca0110[] = {
+       { .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 },
+       { .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 },
+       { .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 },
+       {} /* terminator */
+};
+
+MODULE_ALIAS("snd-hda-codec-id:1102000a");
+MODULE_ALIAS("snd-hda-codec-id:1102000b");
+MODULE_ALIAS("snd-hda-codec-id:1102000d");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec");
+
+static struct hda_codec_preset_list ca0110_list = {
+       .preset = snd_hda_preset_ca0110,
+       .owner = THIS_MODULE,
+};
+
+static int __init patch_ca0110_init(void)
+{
+       return snd_hda_add_codec_preset(&ca0110_list);
+}
+
+static void __exit patch_ca0110_exit(void)
+{
+       snd_hda_delete_codec_preset(&ca0110_list);
+}
+
+module_init(patch_ca0110_init)
+module_exit(patch_ca0110_exit)
index d57d8132a06ebedfc6855a4c5e12507aac50651a..f5792e2eea82dd412dd18f7aca3b6f328027444b 100644 (file)
@@ -35,9 +35,28 @@ struct nvhdmi_spec {
        struct hda_pcm pcm_rec;
 };
 
+#define Nv_VERB_SET_Channel_Allocation          0xF79
+#define Nv_VERB_SET_Info_Frame_Checksum         0xF7A
+#define Nv_VERB_SET_Audio_Protection_On         0xF98
+#define Nv_VERB_SET_Audio_Protection_Off        0xF99
+
+#define Nv_Master_Convert_nid   0x04
+#define Nv_Master_Pin_nid       0x05
+
+static hda_nid_t nvhdmi_convert_nids[4] = {
+       /*front, rear, clfe, rear_surr */
+       0x6, 0x8, 0xa, 0xc,
+};
+
 static struct hda_verb nvhdmi_basic_init[] = {
+       /* set audio protect on */
+       { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
        /* enable digital output on pin widget */
-       { 0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+       { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+       { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+       { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+       { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+       { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
        {} /* terminator */
 };
 
@@ -66,48 +85,205 @@ static int nvhdmi_init(struct hda_codec *codec)
  * Digital out
  */
 static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
-                                    struct hda_codec *codec,
-                                    struct snd_pcm_substream *substream)
+                                       struct hda_codec *codec,
+                                       struct snd_pcm_substream *substream)
 {
        struct nvhdmi_spec *spec = codec->spec;
        return snd_hda_multi_out_dig_open(codec, &spec->multiout);
 }
 
-static int nvhdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
-                                     struct hda_codec *codec,
-                                     struct snd_pcm_substream *substream)
+static int nvhdmi_dig_playback_pcm_close_8ch(struct hda_pcm_stream *hinfo,
+                                       struct hda_codec *codec,
+                                       struct snd_pcm_substream *substream)
 {
        struct nvhdmi_spec *spec = codec->spec;
+       int i;
+
+       snd_hda_codec_write(codec, Nv_Master_Convert_nid,
+                       0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+       for (i = 0; i < 4; i++) {
+               /* set the stream id */
+               snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0,
+                               AC_VERB_SET_CHANNEL_STREAMID, 0);
+               /* set the stream format */
+               snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0,
+                               AC_VERB_SET_STREAM_FORMAT, 0);
+       }
+
        return snd_hda_multi_out_dig_close(codec, &spec->multiout);
 }
 
-static int nvhdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
-                                           struct hda_codec *codec,
-                                           unsigned int stream_tag,
-                                           unsigned int format,
-                                           struct snd_pcm_substream *substream)
+static int nvhdmi_dig_playback_pcm_close_2ch(struct hda_pcm_stream *hinfo,
+                                       struct hda_codec *codec,
+                                       struct snd_pcm_substream *substream)
+{
+       struct nvhdmi_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
+                                       struct hda_codec *codec,
+                                       unsigned int stream_tag,
+                                       unsigned int format,
+                                       struct snd_pcm_substream *substream)
+{
+       int chs;
+       unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id;
+       int i;
+
+       mutex_lock(&codec->spdif_mutex);
+
+       chs = substream->runtime->channels;
+       chan = chs ? (chs - 1) : 1;
+
+       switch (chs) {
+       default:
+       case 0:
+       case 2:
+               chanmask = 0x00;
+               break;
+       case 4:
+               chanmask = 0x08;
+               break;
+       case 6:
+               chanmask = 0x0b;
+               break;
+       case 8:
+               chanmask = 0x13;
+               break;
+       }
+       dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT;
+       dataDCC2 = 0x2;
+
+       /* set the Audio InforFrame Channel Allocation */
+       snd_hda_codec_write(codec, 0x1, 0,
+                       Nv_VERB_SET_Channel_Allocation, chanmask);
+
+       /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
+       if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+               snd_hda_codec_write(codec,
+                               Nv_Master_Convert_nid,
+                               0,
+                               AC_VERB_SET_DIGI_CONVERT_1,
+                               codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+
+       /* set the stream id */
+       snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0,
+                       AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
+
+       /* set the stream format */
+       snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0,
+                       AC_VERB_SET_STREAM_FORMAT, format);
+
+       /* turn on again (if needed) */
+       /* enable and set the channel status audio/data flag */
+       if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
+               snd_hda_codec_write(codec,
+                               Nv_Master_Convert_nid,
+                               0,
+                               AC_VERB_SET_DIGI_CONVERT_1,
+                               codec->spdif_ctls & 0xff);
+               snd_hda_codec_write(codec,
+                               Nv_Master_Convert_nid,
+                               0,
+                               AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
+       }
+
+       for (i = 0; i < 4; i++) {
+               if (chs == 2)
+                       channel_id = 0;
+               else
+                       channel_id = i * 2;
+
+               /* turn off SPDIF once;
+                *otherwise the IEC958 bits won't be updated
+                */
+               if (codec->spdif_status_reset &&
+               (codec->spdif_ctls & AC_DIG1_ENABLE))
+                       snd_hda_codec_write(codec,
+                               nvhdmi_convert_nids[i],
+                               0,
+                               AC_VERB_SET_DIGI_CONVERT_1,
+                               codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+               /* set the stream id */
+               snd_hda_codec_write(codec,
+                               nvhdmi_convert_nids[i],
+                               0,
+                               AC_VERB_SET_CHANNEL_STREAMID,
+                               (stream_tag << 4) | channel_id);
+               /* set the stream format */
+               snd_hda_codec_write(codec,
+                               nvhdmi_convert_nids[i],
+                               0,
+                               AC_VERB_SET_STREAM_FORMAT,
+                               format);
+               /* turn on again (if needed) */
+               /* enable and set the channel status audio/data flag */
+               if (codec->spdif_status_reset &&
+               (codec->spdif_ctls & AC_DIG1_ENABLE)) {
+                       snd_hda_codec_write(codec,
+                                       nvhdmi_convert_nids[i],
+                                       0,
+                                       AC_VERB_SET_DIGI_CONVERT_1,
+                                       codec->spdif_ctls & 0xff);
+                       snd_hda_codec_write(codec,
+                                       nvhdmi_convert_nids[i],
+                                       0,
+                                       AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
+               }
+       }
+
+       /* set the Audio Info Frame Checksum */
+       snd_hda_codec_write(codec, 0x1, 0,
+                       Nv_VERB_SET_Info_Frame_Checksum,
+                       (0x71 - chan - chanmask));
+
+       mutex_unlock(&codec->spdif_mutex);
+       return 0;
+}
+
+static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo,
+                                       struct hda_codec *codec,
+                                       unsigned int stream_tag,
+                                       unsigned int format,
+                                       struct snd_pcm_substream *substream)
 {
        struct nvhdmi_spec *spec = codec->spec;
        return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
-                                            format, substream);
+                                       format, substream);
 }
 
-static struct hda_pcm_stream nvhdmi_pcm_digital_playback = {
+static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 8,
+       .nid = Nv_Master_Convert_nid,
+       .rates = SNDRV_PCM_RATE_48000,
+       .maxbps = 16,
+       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       .ops = {
+               .open = nvhdmi_dig_playback_pcm_open,
+               .close = nvhdmi_dig_playback_pcm_close_8ch,
+               .prepare = nvhdmi_dig_playback_pcm_prepare_8ch
+       },
+};
+
+static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = {
        .substreams = 1,
        .channels_min = 2,
        .channels_max = 2,
-       .nid = 0x4, /* NID to query formats and rates and setup streams */
+       .nid = Nv_Master_Convert_nid,
        .rates = SNDRV_PCM_RATE_48000,
        .maxbps = 16,
        .formats = SNDRV_PCM_FMTBIT_S16_LE,
        .ops = {
                .open = nvhdmi_dig_playback_pcm_open,
-               .close = nvhdmi_dig_playback_pcm_close,
-               .prepare = nvhdmi_dig_playback_pcm_prepare
+               .close = nvhdmi_dig_playback_pcm_close_2ch,
+               .prepare = nvhdmi_dig_playback_pcm_prepare_2ch
        },
 };
 
-static int nvhdmi_build_pcms(struct hda_codec *codec)
+static int nvhdmi_build_pcms_8ch(struct hda_codec *codec)
 {
        struct nvhdmi_spec *spec = codec->spec;
        struct hda_pcm *info = &spec->pcm_rec;
@@ -117,7 +293,24 @@ static int nvhdmi_build_pcms(struct hda_codec *codec)
 
        info->name = "NVIDIA HDMI";
        info->pcm_type = HDA_PCM_TYPE_HDMI;
-       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = nvhdmi_pcm_digital_playback;
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK]
+                                       = nvhdmi_pcm_digital_playback_8ch;
+
+       return 0;
+}
+
+static int nvhdmi_build_pcms_2ch(struct hda_codec *codec)
+{
+       struct nvhdmi_spec *spec = codec->spec;
+       struct hda_pcm *info = &spec->pcm_rec;
+
+       codec->num_pcms = 1;
+       codec->pcm_info = info;
+
+       info->name = "NVIDIA HDMI";
+       info->pcm_type = HDA_PCM_TYPE_HDMI;
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK]
+                                       = nvhdmi_pcm_digital_playback_2ch;
 
        return 0;
 }
@@ -127,14 +320,40 @@ static void nvhdmi_free(struct hda_codec *codec)
        kfree(codec->spec);
 }
 
-static struct hda_codec_ops nvhdmi_patch_ops = {
+static struct hda_codec_ops nvhdmi_patch_ops_8ch = {
+       .build_controls = nvhdmi_build_controls,
+       .build_pcms = nvhdmi_build_pcms_8ch,
+       .init = nvhdmi_init,
+       .free = nvhdmi_free,
+};
+
+static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
        .build_controls = nvhdmi_build_controls,
-       .build_pcms = nvhdmi_build_pcms,
+       .build_pcms = nvhdmi_build_pcms_2ch,
        .init = nvhdmi_init,
        .free = nvhdmi_free,
 };
 
-static int patch_nvhdmi(struct hda_codec *codec)
+static int patch_nvhdmi_8ch(struct hda_codec *codec)
+{
+       struct nvhdmi_spec *spec;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+
+       spec->multiout.num_dacs = 0;  /* no analog */
+       spec->multiout.max_channels = 8;
+       spec->multiout.dig_out_nid = Nv_Master_Convert_nid;
+
+       codec->patch_ops = nvhdmi_patch_ops_8ch;
+
+       return 0;
+}
+
+static int patch_nvhdmi_2ch(struct hda_codec *codec)
 {
        struct nvhdmi_spec *spec;
 
@@ -144,13 +363,11 @@ static int patch_nvhdmi(struct hda_codec *codec)
 
        codec->spec = spec;
 
-       spec->multiout.num_dacs = 0;      /* no analog */
+       spec->multiout.num_dacs = 0;  /* no analog */
        spec->multiout.max_channels = 2;
-       spec->multiout.dig_out_nid = 0x4; /* NID for copying analog to digital,
-                                          * seems to be unused in pure-digital
-                                          * case. */
+       spec->multiout.dig_out_nid = Nv_Master_Convert_nid;
 
-       codec->patch_ops = nvhdmi_patch_ops;
+       codec->patch_ops = nvhdmi_patch_ops_2ch;
 
        return 0;
 }
@@ -159,11 +376,11 @@ static int patch_nvhdmi(struct hda_codec *codec)
  * patch entries
  */
 static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
-       { .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi },
-       { .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi },
-       { .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi },
-       { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi },
-       { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi },
+       { .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
+       { .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
+       { .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi_8ch },
+       { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
+       { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
        {} /* terminator */
 };
 
index 0fd258eba3a57e917c07cc88fc18096e0b6bc1e0..337d2a59c67e5da26127d35b82cc5ce9460d6cfc 100644 (file)
@@ -190,6 +190,7 @@ enum {
        ALC663_ASUS_MODE6,
        ALC272_DELL,
        ALC272_DELL_ZM1,
+       ALC272_SAMSUNG_NC10,
        ALC662_AUTO,
        ALC662_MODEL_LAST,
 };
@@ -205,6 +206,7 @@ enum {
        ALC882_ASUS_A7M,
        ALC885_MACPRO,
        ALC885_MBP3,
+       ALC885_MB5,
        ALC885_IMAC24,
        ALC882_AUTO,
        ALC882_MODEL_LAST,
@@ -218,9 +220,11 @@ enum {
        ALC883_6ST_DIG,
        ALC883_TARGA_DIG,
        ALC883_TARGA_2ch_DIG,
+       ALC883_TARGA_8ch_DIG,
        ALC883_ACER,
        ALC883_ACER_ASPIRE,
        ALC888_ACER_ASPIRE_4930G,
+       ALC888_ACER_ASPIRE_8930G,
        ALC883_MEDION,
        ALC883_MEDION_MD2,
        ALC883_LAPTOP_EAPD,
@@ -238,7 +242,9 @@ enum {
        ALC883_3ST_6ch_INTEL,
        ALC888_ASUS_M90V,
        ALC888_ASUS_EEE1601,
+       ALC889A_MB31,
        ALC1200_ASUS_P5Q,
+       ALC883_SONY_VAIO_TT,
        ALC883_AUTO,
        ALC883_MODEL_LAST,
 };
@@ -253,6 +259,15 @@ enum {
 /* for GPIO Poll */
 #define GPIO_MASK      0x03
 
+/* extra amp-initialization sequence types */
+enum {
+       ALC_INIT_NONE,
+       ALC_INIT_DEFAULT,
+       ALC_INIT_GPIO1,
+       ALC_INIT_GPIO2,
+       ALC_INIT_GPIO3,
+};
+
 struct alc_spec {
        /* codec parameterization */
        struct snd_kcontrol_new *mixers[5];     /* mixer arrays */
@@ -266,13 +281,13 @@ struct alc_spec {
                                                 */
        unsigned int num_init_verbs;
 
-       char *stream_name_analog;       /* analog PCM stream */
+       char stream_name_analog[16];    /* analog PCM stream */
        struct hda_pcm_stream *stream_analog_playback;
        struct hda_pcm_stream *stream_analog_capture;
        struct hda_pcm_stream *stream_analog_alt_playback;
        struct hda_pcm_stream *stream_analog_alt_capture;
 
-       char *stream_name_digital;      /* digital PCM stream */
+       char stream_name_digital[16];   /* digital PCM stream */
        struct hda_pcm_stream *stream_digital_playback;
        struct hda_pcm_stream *stream_digital_capture;
 
@@ -301,6 +316,8 @@ struct alc_spec {
        const struct hda_channel_mode *channel_mode;
        int num_channel_mode;
        int need_dac_fix;
+       int const_channel_count;
+       int ext_channel_count;
 
        /* PCM information */
        struct hda_pcm pcm_rec[3];      /* used in alc_build_pcms() */
@@ -322,6 +339,7 @@ struct alc_spec {
 
        /* other flags */
        unsigned int no_analog :1; /* digital I/O only */
+       int init_amp;
 
        /* for virtual master */
        hda_nid_t vmaster_nid;
@@ -355,6 +373,7 @@ struct alc_config_preset {
        unsigned int num_channel_mode;
        const struct hda_channel_mode *channel_mode;
        int need_dac_fix;
+       int const_channel_count;
        unsigned int num_mux_defs;
        const struct hda_input_mux *input_mux;
        void (*unsol_event)(struct hda_codec *, unsigned int);
@@ -449,7 +468,7 @@ static int alc_ch_mode_get(struct snd_kcontrol *kcontrol,
        struct alc_spec *spec = codec->spec;
        return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
                                   spec->num_channel_mode,
-                                  spec->multiout.max_channels);
+                                  spec->ext_channel_count);
 }
 
 static int alc_ch_mode_put(struct snd_kcontrol *kcontrol,
@@ -459,9 +478,12 @@ static int alc_ch_mode_put(struct snd_kcontrol *kcontrol,
        struct alc_spec *spec = codec->spec;
        int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
                                      spec->num_channel_mode,
-                                     &spec->multiout.max_channels);
-       if (err >= 0 && spec->need_dac_fix)
-               spec->multiout.num_dacs = spec->multiout.max_channels / 2;
+                                     &spec->ext_channel_count);
+       if (err >= 0 && !spec->const_channel_count) {
+               spec->multiout.max_channels = spec->ext_channel_count;
+               if (spec->need_dac_fix)
+                       spec->multiout.num_dacs = spec->multiout.max_channels / 2;
+       }
        return err;
 }
 
@@ -841,8 +863,13 @@ static void setup_preset(struct alc_spec *spec,
        spec->channel_mode = preset->channel_mode;
        spec->num_channel_mode = preset->num_channel_mode;
        spec->need_dac_fix = preset->need_dac_fix;
+       spec->const_channel_count = preset->const_channel_count;
 
-       spec->multiout.max_channels = spec->channel_mode[0].channels;
+       if (preset->const_channel_count)
+               spec->multiout.max_channels = preset->const_channel_count;
+       else
+               spec->multiout.max_channels = spec->channel_mode[0].channels;
+       spec->ext_channel_count = spec->channel_mode[0].channels;
 
        spec->multiout.num_dacs = preset->num_dacs;
        spec->multiout.dac_nids = preset->dac_nids;
@@ -921,20 +948,26 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
        alc_fix_pll(codec);
 }
 
-static void alc_sku_automute(struct hda_codec *codec)
+static void alc_automute_pin(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
        unsigned int present;
-       unsigned int hp_nid = spec->autocfg.hp_pins[0];
-       unsigned int sp_nid = spec->autocfg.speaker_pins[0];
+       unsigned int nid = spec->autocfg.hp_pins[0];
+       int i;
 
        /* need to execute and sync at first */
-       snd_hda_codec_read(codec, hp_nid, 0, AC_VERB_SET_PIN_SENSE, 0);
-       present = snd_hda_codec_read(codec, hp_nid, 0,
+       snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0);
+       present = snd_hda_codec_read(codec, nid, 0,
                                     AC_VERB_GET_PIN_SENSE, 0);
-       spec->jack_present = (present & 0x80000000) != 0;
-       snd_hda_codec_write(codec, sp_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           spec->jack_present ? 0 : PIN_OUT);
+       spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
+       for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
+               nid = spec->autocfg.speaker_pins[i];
+               if (!nid)
+                       break;
+               snd_hda_codec_write(codec, nid, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                   spec->jack_present ? 0 : PIN_OUT);
+       }
 }
 
 #if 0 /* it's broken in some acses -- temporarily disabled */
@@ -969,16 +1002,19 @@ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
                res >>= 28;
        else
                res >>= 26;
-       if (res == ALC880_HP_EVENT)
-               alc_sku_automute(codec);
-
-       if (res == ALC880_MIC_EVENT)
+       switch (res) {
+       case ALC880_HP_EVENT:
+               alc_automute_pin(codec);
+               break;
+       case ALC880_MIC_EVENT:
                alc_mic_automute(codec);
+               break;
+       }
 }
 
 static void alc_inithook(struct hda_codec *codec)
 {
-       alc_sku_automute(codec);
+       alc_automute_pin(codec);
        alc_mic_automute(codec);
 }
 
@@ -1000,69 +1036,21 @@ static void alc888_coef_init(struct hda_codec *codec)
                                    AC_VERB_SET_PROC_COEF, 0x3030);
 }
 
-/* 32-bit subsystem ID for BIOS loading in HD Audio codec.
- *     31 ~ 16 :       Manufacture ID
- *     15 ~ 8  :       SKU ID
- *     7  ~ 0  :       Assembly ID
- *     port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36
- */
-static void alc_subsystem_id(struct hda_codec *codec,
-                            unsigned int porta, unsigned int porte,
-                            unsigned int portd)
+static void alc_auto_init_amp(struct hda_codec *codec, int type)
 {
-       unsigned int ass, tmp, i;
-       unsigned nid;
-       struct alc_spec *spec = codec->spec;
-
-       ass = codec->subsystem_id & 0xffff;
-       if ((ass != codec->bus->pci->subsystem_device) && (ass & 1))
-               goto do_sku;
-
-       /*
-        * 31~30        : port conetcivity
-        * 29~21        : reserve
-        * 20           : PCBEEP input
-        * 19~16        : Check sum (15:1)
-        * 15~1         : Custom
-        * 0            : override
-       */
-       nid = 0x1d;
-       if (codec->vendor_id == 0x10ec0260)
-               nid = 0x17;
-       ass = snd_hda_codec_get_pincfg(codec, nid);
-       if (!(ass & 1) && !(ass & 0x100000))
-               return;
-       if ((ass >> 30) != 1)   /* no physical connection */
-               return;
+       unsigned int tmp;
 
-       /* check sum */
-       tmp = 0;
-       for (i = 1; i < 16; i++) {
-               if ((ass >> i) & 1)
-                       tmp++;
-       }
-       if (((ass >> 16) & 0xf) != tmp)
-               return;
-do_sku:
-       /*
-        * 0 : override
-        * 1 :  Swap Jack
-        * 2 : 0 --> Desktop, 1 --> Laptop
-        * 3~5 : External Amplifier control
-        * 7~6 : Reserved
-       */
-       tmp = (ass & 0x38) >> 3;        /* external Amp control */
-       switch (tmp) {
-       case 1:
+       switch (type) {
+       case ALC_INIT_GPIO1:
                snd_hda_sequence_write(codec, alc_gpio1_init_verbs);
                break;
-       case 3:
+       case ALC_INIT_GPIO2:
                snd_hda_sequence_write(codec, alc_gpio2_init_verbs);
                break;
-       case 7:
+       case ALC_INIT_GPIO3:
                snd_hda_sequence_write(codec, alc_gpio3_init_verbs);
                break;
-       case 5: /* set EAPD output high */
+       case ALC_INIT_DEFAULT:
                switch (codec->vendor_id) {
                case 0x10ec0260:
                        snd_hda_codec_write(codec, 0x0f, 0,
@@ -1116,7 +1104,7 @@ do_sku:
                                            tmp | 0x2010);
                        break;
                case 0x10ec0888:
-                       /*alc888_coef_init(codec);*/ /* called in alc_init() */
+                       alc888_coef_init(codec);
                        break;
                case 0x10ec0267:
                case 0x10ec0268:
@@ -1131,7 +1119,107 @@ do_sku:
                                            tmp | 0x3000);
                        break;
                }
-       default:
+               break;
+       }
+}
+
+static void alc_init_auto_hp(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       if (!spec->autocfg.hp_pins[0])
+               return;
+
+       if (!spec->autocfg.speaker_pins[0]) {
+               if (spec->autocfg.line_out_pins[0] &&
+                   spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
+                       spec->autocfg.speaker_pins[0] =
+                               spec->autocfg.line_out_pins[0];
+               else
+                       return;
+       }
+
+       snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
+                   spec->autocfg.hp_pins[0]);
+       snd_hda_codec_write_cache(codec, spec->autocfg.hp_pins[0], 0,
+                                 AC_VERB_SET_UNSOLICITED_ENABLE,
+                                 AC_USRSP_EN | ALC880_HP_EVENT);
+       spec->unsol_event = alc_sku_unsol_event;
+}
+
+/* check subsystem ID and set up device-specific initialization;
+ * return 1 if initialized, 0 if invalid SSID
+ */
+/* 32-bit subsystem ID for BIOS loading in HD Audio codec.
+ *     31 ~ 16 :       Manufacture ID
+ *     15 ~ 8  :       SKU ID
+ *     7  ~ 0  :       Assembly ID
+ *     port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36
+ */
+static int alc_subsystem_id(struct hda_codec *codec,
+                           hda_nid_t porta, hda_nid_t porte,
+                           hda_nid_t portd)
+{
+       unsigned int ass, tmp, i;
+       unsigned nid;
+       struct alc_spec *spec = codec->spec;
+
+       ass = codec->subsystem_id & 0xffff;
+       if ((ass != codec->bus->pci->subsystem_device) && (ass & 1))
+               goto do_sku;
+
+       /* invalid SSID, check the special NID pin defcfg instead */
+       /*
+        * 31~30        : port conetcivity
+        * 29~21        : reserve
+        * 20           : PCBEEP input
+        * 19~16        : Check sum (15:1)
+        * 15~1         : Custom
+        * 0            : override
+       */
+       nid = 0x1d;
+       if (codec->vendor_id == 0x10ec0260)
+               nid = 0x17;
+       ass = snd_hda_codec_get_pincfg(codec, nid);
+       snd_printd("realtek: No valid SSID, "
+                  "checking pincfg 0x%08x for NID 0x%x\n",
+                  ass, nid);
+       if (!(ass & 1) && !(ass & 0x100000))
+               return 0;
+       if ((ass >> 30) != 1)   /* no physical connection */
+               return 0;
+
+       /* check sum */
+       tmp = 0;
+       for (i = 1; i < 16; i++) {
+               if ((ass >> i) & 1)
+                       tmp++;
+       }
+       if (((ass >> 16) & 0xf) != tmp)
+               return 0;
+do_sku:
+       snd_printd("realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n",
+                  ass & 0xffff, codec->vendor_id);
+       /*
+        * 0 : override
+        * 1 :  Swap Jack
+        * 2 : 0 --> Desktop, 1 --> Laptop
+        * 3~5 : External Amplifier control
+        * 7~6 : Reserved
+       */
+       tmp = (ass & 0x38) >> 3;        /* external Amp control */
+       switch (tmp) {
+       case 1:
+               spec->init_amp = ALC_INIT_GPIO1;
+               break;
+       case 3:
+               spec->init_amp = ALC_INIT_GPIO2;
+               break;
+       case 7:
+               spec->init_amp = ALC_INIT_GPIO3;
+               break;
+       case 5:
+               spec->init_amp = ALC_INIT_DEFAULT;
                break;
        }
 
@@ -1139,7 +1227,7 @@ do_sku:
         * when the external headphone out jack is plugged"
         */
        if (!(ass & 0x8000))
-               return;
+               return 1;
        /*
         * 10~8 : Jack location
         * 12~11: Headphone out -> 00: PortA, 01: PortE, 02: PortD, 03: Resvered
@@ -1147,14 +1235,6 @@ do_sku:
         * 15   : 1 --> enable the function "Mute internal speaker
         *              when the external headphone out jack is plugged"
         */
-       if (!spec->autocfg.speaker_pins[0]) {
-               if (spec->autocfg.line_out_pins[0])
-                       spec->autocfg.speaker_pins[0] =
-                               spec->autocfg.line_out_pins[0];
-               else
-                       return;
-       }
-
        if (!spec->autocfg.hp_pins[0]) {
                tmp = (ass >> 11) & 0x3;        /* HP to chassis */
                if (tmp == 0)
@@ -1164,23 +1244,23 @@ do_sku:
                else if (tmp == 2)
                        spec->autocfg.hp_pins[0] = portd;
                else
-                       return;
+                       return 1;
        }
-       if (spec->autocfg.hp_pins[0])
-               snd_hda_codec_write(codec, spec->autocfg.hp_pins[0], 0,
-                       AC_VERB_SET_UNSOLICITED_ENABLE,
-                       AC_USRSP_EN | ALC880_HP_EVENT);
 
-#if 0 /* it's broken in some acses -- temporarily disabled */
-       if (spec->autocfg.input_pins[AUTO_PIN_MIC] &&
-               spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC])
-               snd_hda_codec_write(codec,
-                       spec->autocfg.input_pins[AUTO_PIN_MIC], 0,
-                       AC_VERB_SET_UNSOLICITED_ENABLE,
-                       AC_USRSP_EN | ALC880_MIC_EVENT);
-#endif /* disabled */
+       alc_init_auto_hp(codec);
+       return 1;
+}
 
-       spec->unsol_event = alc_sku_unsol_event;
+static void alc_ssid_check(struct hda_codec *codec,
+                          hda_nid_t porta, hda_nid_t porte, hda_nid_t portd)
+{
+       if (!alc_subsystem_id(codec, porta, porte, portd)) {
+               struct alc_spec *spec = codec->spec;
+               snd_printd("realtek: "
+                          "Enable default setup for auto mode as fallback\n");
+               spec->init_amp = ALC_INIT_DEFAULT;
+               alc_init_auto_hp(codec);
+       }
 }
 
 /*
@@ -1315,32 +1395,58 @@ static struct hda_verb alc888_fujitsu_xa3530_verbs[] = {
        {}
 };
 
-static void alc888_fujitsu_xa3530_automute(struct hda_codec *codec)
+static void alc_automute_amp(struct hda_codec *codec)
 {
-       unsigned int present;
-       unsigned int bits;
-       /* Line out presence */
-       present = snd_hda_codec_read(codec, 0x17, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       /* HP out presence */
-       present = present || snd_hda_codec_read(codec, 0x1b, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       bits = present ? HDA_AMP_MUTE : 0;
+       struct alc_spec *spec = codec->spec;
+       unsigned int val, mute;
+       hda_nid_t nid;
+       int i;
+
+       spec->jack_present = 0;
+       for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
+               nid = spec->autocfg.hp_pins[i];
+               if (!nid)
+                       break;
+               val = snd_hda_codec_read(codec, nid, 0,
+                                        AC_VERB_GET_PIN_SENSE, 0);
+               if (val & AC_PINSENSE_PRESENCE) {
+                       spec->jack_present = 1;
+                       break;
+               }
+       }
+
+       mute = spec->jack_present ? HDA_AMP_MUTE : 0;
        /* Toggle internal speakers muting */
-       snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-       /* Toggle internal bass muting */
-       snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
+       for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
+               nid = spec->autocfg.speaker_pins[i];
+               if (!nid)
+                       break;
+               snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+                                        HDA_AMP_MUTE, mute);
+       }
 }
 
-static void alc888_fujitsu_xa3530_unsol_event(struct hda_codec *codec,
-               unsigned int res)
+static void alc_automute_amp_unsol_event(struct hda_codec *codec,
+                                        unsigned int res)
 {
-       if (res >> 26 == ALC880_HP_EVENT)
-               alc888_fujitsu_xa3530_automute(codec);
+       if (codec->vendor_id == 0x10ec0880)
+               res >>= 28;
+       else
+               res >>= 26;
+       if (res == ALC880_HP_EVENT)
+               alc_automute_amp(codec);
 }
 
+static void alc888_fujitsu_xa3530_init_hook(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       spec->autocfg.hp_pins[0] = 0x17; /* line-out */
+       spec->autocfg.hp_pins[1] = 0x1b; /* hp */
+       spec->autocfg.speaker_pins[0] = 0x14; /* speaker */
+       spec->autocfg.speaker_pins[1] = 0x15; /* bass */
+       alc_automute_amp(codec);
+}
 
 /*
  * ALC888 Acer Aspire 4930G model
@@ -1364,6 +1470,59 @@ static struct hda_verb alc888_acer_aspire_4930g_verbs[] = {
        { }
 };
 
+/*
+ * ALC889 Acer Aspire 8930G model
+ */
+
+static struct hda_verb alc889_acer_aspire_8930g_verbs[] = {
+/* Front Mic: set to PIN_IN (empty by default) */
+       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+/* Unselect Front Mic by default in input mixer 3 */
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)},
+/* Enable unsolicited event for HP jack */
+       {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+/* Connect Internal Front to Front */
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+/* Connect Internal Rear to Rear */
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01},
+/* Connect Internal CLFE to CLFE */
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
+/* Connect HP out to Front */
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+/* Enable all DACs */
+/*  DAC DISABLE/MUTE 1? */
+/*  setting bits 1-5 disables DAC nids 0x02-0x06 apparently. Init=0x38 */
+       {0x20, AC_VERB_SET_COEF_INDEX, 0x03},
+       {0x20, AC_VERB_SET_PROC_COEF, 0x0000},
+/*  DAC DISABLE/MUTE 2? */
+/*  some bit here disables the other DACs. Init=0x4900 */
+       {0x20, AC_VERB_SET_COEF_INDEX, 0x08},
+       {0x20, AC_VERB_SET_PROC_COEF, 0x0000},
+/* Enable amplifiers */
+       {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
+       {0x15, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
+/* DMIC fix
+ * This laptop has a stereo digital microphone. The mics are only 1cm apart
+ * which makes the stereo useless. However, either the mic or the ALC889
+ * makes the signal become a difference/sum signal instead of standard
+ * stereo, which is annoying. So instead we flip this bit which makes the
+ * codec replicate the sum signal to both channels, turning it into a
+ * normal mono mic.
+ */
+/*  DMIC_CONTROL? Init value = 0x0001 */
+       {0x20, AC_VERB_SET_COEF_INDEX, 0x0b},
+       {0x20, AC_VERB_SET_PROC_COEF, 0x0003},
+       { }
+};
+
 static struct hda_input_mux alc888_2_capture_sources[2] = {
        /* Front mic only available on one ADC */
        {
@@ -1385,6 +1544,38 @@ static struct hda_input_mux alc888_2_capture_sources[2] = {
        }
 };
 
+static struct hda_input_mux alc889_capture_sources[3] = {
+       /* Digital mic only available on first "ADC" */
+       {
+               .num_items = 5,
+               .items = {
+                       { "Mic", 0x0 },
+                       { "Line", 0x2 },
+                       { "CD", 0x4 },
+                       { "Front Mic", 0xb },
+                       { "Input Mix", 0xa },
+               },
+       },
+       {
+               .num_items = 4,
+               .items = {
+                       { "Mic", 0x0 },
+                       { "Line", 0x2 },
+                       { "CD", 0x4 },
+                       { "Input Mix", 0xa },
+               },
+       },
+       {
+               .num_items = 4,
+               .items = {
+                       { "Mic", 0x0 },
+                       { "Line", 0x2 },
+                       { "CD", 0x4 },
+                       { "Input Mix", 0xa },
+               },
+       }
+};
+
 static struct snd_kcontrol_new alc888_base_mixer[] = {
        HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
        HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
@@ -1407,22 +1598,24 @@ static struct snd_kcontrol_new alc888_base_mixer[] = {
        { } /* end */
 };
 
-static void alc888_acer_aspire_4930g_automute(struct hda_codec *codec)
+static void alc888_acer_aspire_4930g_init_hook(struct hda_codec *codec)
 {
-       unsigned int present;
-       unsigned int bits;
-       present = snd_hda_codec_read(codec, 0x15, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       bits = present ? HDA_AMP_MUTE : 0;
-       snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
+       struct alc_spec *spec = codec->spec;
+
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       alc_automute_amp(codec);
 }
 
-static void alc888_acer_aspire_4930g_unsol_event(struct hda_codec *codec,
-               unsigned int res)
+static void alc889_acer_aspire_8930g_init_hook(struct hda_codec *codec)
 {
-       if (res >> 26 == ALC880_HP_EVENT)
-               alc888_acer_aspire_4930g_automute(codec);
+       struct alc_spec *spec = codec->spec;
+
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[1] = 0x16;
+       spec->autocfg.speaker_pins[2] = 0x1b;
+       alc_automute_amp(codec);
 }
 
 /*
@@ -2390,21 +2583,6 @@ static struct hda_verb alc880_beep_init_verbs[] = {
        { }
 };
 
-/* toggle speaker-output according to the hp-jack state */
-static void alc880_uniwill_hp_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_codec_read(codec, 0x14, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       bits = present ? HDA_AMP_MUTE : 0;
-       snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-       snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-}
-
 /* auto-toggle front mic */
 static void alc880_uniwill_mic_automute(struct hda_codec *codec)
 {
@@ -2417,9 +2595,14 @@ static void alc880_uniwill_mic_automute(struct hda_codec *codec)
        snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
 }
 
-static void alc880_uniwill_automute(struct hda_codec *codec)
+static void alc880_uniwill_init_hook(struct hda_codec *codec)
 {
-       alc880_uniwill_hp_automute(codec);
+       struct alc_spec *spec = codec->spec;
+
+       spec->autocfg.hp_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x16;
+       alc_automute_amp(codec);
        alc880_uniwill_mic_automute(codec);
 }
 
@@ -2430,24 +2613,22 @@ static void alc880_uniwill_unsol_event(struct hda_codec *codec,
         * definition.  4bit tag is placed at 28 bit!
         */
        switch (res >> 28) {
-       case ALC880_HP_EVENT:
-               alc880_uniwill_hp_automute(codec);
-               break;
        case ALC880_MIC_EVENT:
                alc880_uniwill_mic_automute(codec);
                break;
+       default:
+               alc_automute_amp_unsol_event(codec, res);
+               break;
        }
 }
 
-static void alc880_uniwill_p53_hp_automute(struct hda_codec *codec)
+static void alc880_uniwill_p53_init_hook(struct hda_codec *codec)
 {
-       unsigned int present;
-       unsigned char bits;
+       struct alc_spec *spec = codec->spec;
 
-       present = snd_hda_codec_read(codec, 0x14, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       bits = present ? HDA_AMP_MUTE : 0;
-       snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, HDA_AMP_MUTE, bits);
+       spec->autocfg.hp_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[0] = 0x15;
+       alc_automute_amp(codec);
 }
 
 static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec)
@@ -2469,10 +2650,10 @@ static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec,
        /* Looks like the unsol event is incompatible with the standard
         * definition.  4bit tag is placed at 28 bit!
         */
-       if ((res >> 28) == ALC880_HP_EVENT)
-               alc880_uniwill_p53_hp_automute(codec);
        if ((res >> 28) == ALC880_DCVOL_EVENT)
                alc880_uniwill_p53_dcvol_automute(codec);
+       else
+               alc_automute_amp_unsol_event(codec, res);
 }
 
 /*
@@ -2542,6 +2723,7 @@ static struct hda_verb alc880_pin_asus_init_verbs[] = {
 /* Enable GPIO mask and set output */
 #define alc880_gpio1_init_verbs        alc_gpio1_init_verbs
 #define alc880_gpio2_init_verbs        alc_gpio2_init_verbs
+#define alc880_gpio3_init_verbs        alc_gpio3_init_verbs
 
 /* Clevo m520g init */
 static struct hda_verb alc880_pin_clevo_init_verbs[] = {
@@ -2704,30 +2886,18 @@ static struct hda_verb alc880_lg_init_verbs[] = {
        {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
        {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
        /* jack sense */
-       {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | 0x1},
+       {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
        { }
 };
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc880_lg_automute(struct hda_codec *codec)
+static void alc880_lg_init_hook(struct hda_codec *codec)
 {
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_codec_read(codec, 0x1b, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       bits = present ? HDA_AMP_MUTE : 0;
-       snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-}
+       struct alc_spec *spec = codec->spec;
 
-static void alc880_lg_unsol_event(struct hda_codec *codec, unsigned int res)
-{
-       /* Looks like the unsol event is incompatible with the standard
-        * definition.  4bit tag is placed at 28 bit!
-        */
-       if ((res >> 28) == 0x01)
-               alc880_lg_automute(codec);
+       spec->autocfg.hp_pins[0] = 0x1b;
+       spec->autocfg.speaker_pins[0] = 0x17;
+       alc_automute_amp(codec);
 }
 
 /*
@@ -2801,30 +2971,18 @@ static struct hda_verb alc880_lg_lw_init_verbs[] = {
        {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
        {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
        /* jack sense */
-       {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | 0x1},
+       {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
        { }
 };
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc880_lg_lw_automute(struct hda_codec *codec)
+static void alc880_lg_lw_init_hook(struct hda_codec *codec)
 {
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_codec_read(codec, 0x1b, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       bits = present ? HDA_AMP_MUTE : 0;
-       snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-}
+       struct alc_spec *spec = codec->spec;
 
-static void alc880_lg_lw_unsol_event(struct hda_codec *codec, unsigned int res)
-{
-       /* Looks like the unsol event is incompatible with the standard
-        * definition.  4bit tag is placed at 28 bit!
-        */
-       if ((res >> 28) == 0x01)
-               alc880_lg_lw_automute(codec);
+       spec->autocfg.hp_pins[0] = 0x1b;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       alc_automute_amp(codec);
 }
 
 static struct snd_kcontrol_new alc880_medion_rim_mixer[] = {
@@ -2871,16 +3029,10 @@ static struct hda_verb alc880_medion_rim_init_verbs[] = {
 /* toggle speaker-output according to the hp-jack state */
 static void alc880_medion_rim_automute(struct hda_codec *codec)
 {
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_codec_read(codec, 0x14, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0)
-               & AC_PINSENSE_PRESENCE;
-       bits = present ? HDA_AMP_MUTE : 0;
-       snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-       if (present)
+       struct alc_spec *spec = codec->spec;
+       alc_automute_amp(codec);
+       /* toggle EAPD */
+       if (spec->jack_present)
                snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0);
        else
                snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 2);
@@ -2896,6 +3048,15 @@ static void alc880_medion_rim_unsol_event(struct hda_codec *codec,
                alc880_medion_rim_automute(codec);
 }
 
+static void alc880_medion_rim_init_hook(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       spec->autocfg.hp_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[0] = 0x1b;
+       alc880_medion_rim_automute(codec);
+}
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 static struct hda_amp_list alc880_loopbacks[] = {
        { 0x0b, HDA_INPUT, 0 },
@@ -2924,8 +3085,7 @@ static int alc_init(struct hda_codec *codec)
        unsigned int i;
 
        alc_fix_pll(codec);
-       if (codec->vendor_id == 0x10ec0888)
-               alc888_coef_init(codec);
+       alc_auto_init_amp(codec, spec->init_amp);
 
        for (i = 0; i < spec->num_init_verbs; i++)
                snd_hda_sequence_write(codec, spec->init_verbs[i]);
@@ -3127,7 +3287,10 @@ static int alc_build_pcms(struct hda_codec *codec)
        if (spec->no_analog)
                goto skip_analog;
 
+       snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
+                "%s Analog", codec->chip_name);
        info->name = spec->stream_name_analog;
+       
        if (spec->stream_analog_playback) {
                if (snd_BUG_ON(!spec->multiout.dac_nids))
                        return -EINVAL;
@@ -3153,6 +3316,9 @@ static int alc_build_pcms(struct hda_codec *codec)
  skip_analog:
        /* SPDIF for stream index #1 */
        if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
+               snprintf(spec->stream_name_digital,
+                        sizeof(spec->stream_name_digital),
+                        "%s Digital", codec->chip_name);
                codec->num_pcms = 2;
                codec->slave_dig_outs = spec->multiout.slave_dig_outs;
                info = spec->pcm_rec + 1;
@@ -3755,7 +3921,7 @@ static struct alc_config_preset alc880_presets[] = {
                .channel_mode = alc880_2_jack_modes,
                .input_mux = &alc880_f1734_capture_source,
                .unsol_event = alc880_uniwill_p53_unsol_event,
-               .init_hook = alc880_uniwill_p53_hp_automute,
+               .init_hook = alc880_uniwill_p53_init_hook,
        },
        [ALC880_ASUS] = {
                .mixers = { alc880_asus_mixer },
@@ -3832,7 +3998,7 @@ static struct alc_config_preset alc880_presets[] = {
                .need_dac_fix = 1,
                .input_mux = &alc880_capture_source,
                .unsol_event = alc880_uniwill_unsol_event,
-               .init_hook = alc880_uniwill_automute,
+               .init_hook = alc880_uniwill_init_hook,
        },
        [ALC880_UNIWILL_P53] = {
                .mixers = { alc880_uniwill_p53_mixer },
@@ -3844,7 +4010,7 @@ static struct alc_config_preset alc880_presets[] = {
                .channel_mode = alc880_threestack_modes,
                .input_mux = &alc880_capture_source,
                .unsol_event = alc880_uniwill_p53_unsol_event,
-               .init_hook = alc880_uniwill_p53_hp_automute,
+               .init_hook = alc880_uniwill_p53_init_hook,
        },
        [ALC880_FUJITSU] = {
                .mixers = { alc880_fujitsu_mixer },
@@ -3858,7 +4024,7 @@ static struct alc_config_preset alc880_presets[] = {
                .channel_mode = alc880_2_jack_modes,
                .input_mux = &alc880_capture_source,
                .unsol_event = alc880_uniwill_p53_unsol_event,
-               .init_hook = alc880_uniwill_p53_hp_automute,
+               .init_hook = alc880_uniwill_p53_init_hook,
        },
        [ALC880_CLEVO] = {
                .mixers = { alc880_three_stack_mixer },
@@ -3883,8 +4049,8 @@ static struct alc_config_preset alc880_presets[] = {
                .channel_mode = alc880_lg_ch_modes,
                .need_dac_fix = 1,
                .input_mux = &alc880_lg_capture_source,
-               .unsol_event = alc880_lg_unsol_event,
-               .init_hook = alc880_lg_automute,
+               .unsol_event = alc_automute_amp_unsol_event,
+               .init_hook = alc880_lg_init_hook,
 #ifdef CONFIG_SND_HDA_POWER_SAVE
                .loopbacks = alc880_lg_loopbacks,
 #endif
@@ -3899,8 +4065,8 @@ static struct alc_config_preset alc880_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc880_lg_lw_modes),
                .channel_mode = alc880_lg_lw_modes,
                .input_mux = &alc880_lg_lw_capture_source,
-               .unsol_event = alc880_lg_lw_unsol_event,
-               .init_hook = alc880_lg_lw_automute,
+               .unsol_event = alc_automute_amp_unsol_event,
+               .init_hook = alc880_lg_lw_init_hook,
        },
        [ALC880_MEDION_RIM] = {
                .mixers = { alc880_medion_rim_mixer },
@@ -3914,7 +4080,7 @@ static struct alc_config_preset alc880_presets[] = {
                .channel_mode = alc880_2_jack_modes,
                .input_mux = &alc880_medion_rim_capture_source,
                .unsol_event = alc880_medion_rim_unsol_event,
-               .init_hook = alc880_medion_rim_automute,
+               .init_hook = alc880_medion_rim_init_hook,
        },
 #ifdef CONFIG_SND_DEBUG
        [ALC880_TEST] = {
@@ -4199,7 +4365,6 @@ static void alc880_auto_init_multi_out(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        int i;
 
-       alc_subsystem_id(codec, 0x15, 0x1b, 0x14);
        for (i = 0; i < spec->autocfg.line_outs; i++) {
                hda_nid_t nid = spec->autocfg.line_out_pins[i];
                int pin_type = get_pin_type(spec->autocfg.line_out_type);
@@ -4304,6 +4469,8 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux[0];
 
+       alc_ssid_check(codec, 0x15, 0x1b, 0x14);
+
        return 1;
 }
 
@@ -4361,8 +4528,8 @@ static int patch_alc880(struct hda_codec *codec)
                                                  alc880_models,
                                                  alc880_cfg_tbl);
        if (board_config < 0) {
-               printk(KERN_INFO "hda_codec: Unknown model for ALC880, "
-                      "trying auto-probe from BIOS...\n");
+               printk(KERN_INFO "hda_codec: Unknown model for %s, "
+                      "trying auto-probe from BIOS...\n", codec->chip_name);
                board_config = ALC880_AUTO;
        }
 
@@ -4389,12 +4556,10 @@ static int patch_alc880(struct hda_codec *codec)
        if (board_config != ALC880_AUTO)
                setup_preset(spec, &alc880_presets[board_config]);
 
-       spec->stream_name_analog = "ALC880 Analog";
        spec->stream_analog_playback = &alc880_pcm_analog_playback;
        spec->stream_analog_capture = &alc880_pcm_analog_capture;
        spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
 
-       spec->stream_name_digital = "ALC880 Digital";
        spec->stream_digital_playback = &alc880_pcm_digital_playback;
        spec->stream_digital_capture = &alc880_pcm_digital_capture;
 
@@ -5679,7 +5844,6 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        hda_nid_t nid;
 
-       alc_subsystem_id(codec, 0x10, 0x15, 0x0f);
        nid = spec->autocfg.line_out_pins[0];
        if (nid) {
                int pin_type = get_pin_type(spec->autocfg.line_out_type);
@@ -5789,6 +5953,8 @@ static int alc260_parse_auto_config(struct hda_codec *codec)
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux[0];
 
+       alc_ssid_check(codec, 0x10, 0x15, 0x0f);
+
        return 1;
 }
 
@@ -6006,8 +6172,9 @@ static int patch_alc260(struct hda_codec *codec)
                                                  alc260_models,
                                                  alc260_cfg_tbl);
        if (board_config < 0) {
-               snd_printd(KERN_INFO "hda_codec: Unknown model for ALC260, "
-                          "trying auto-probe from BIOS...\n");
+               snd_printd(KERN_INFO "hda_codec: Unknown model for %s, "
+                          "trying auto-probe from BIOS...\n",
+                          codec->chip_name);
                board_config = ALC260_AUTO;
        }
 
@@ -6034,11 +6201,9 @@ static int patch_alc260(struct hda_codec *codec)
        if (board_config != ALC260_AUTO)
                setup_preset(spec, &alc260_presets[board_config]);
 
-       spec->stream_name_analog = "ALC260 Analog";
        spec->stream_analog_playback = &alc260_pcm_analog_playback;
        spec->stream_analog_capture = &alc260_pcm_analog_capture;
 
-       spec->stream_name_digital = "ALC260 Digital";
        spec->stream_digital_playback = &alc260_pcm_digital_playback;
        spec->stream_digital_capture = &alc260_pcm_digital_capture;
 
@@ -6115,6 +6280,16 @@ static struct hda_input_mux alc882_capture_source = {
                { "CD", 0x4 },
        },
 };
+
+static struct hda_input_mux mb5_capture_source = {
+       .num_items = 3,
+       .items = {
+               { "Mic", 0x1 },
+               { "Line", 0x2 },
+               { "CD", 0x4 },
+       },
+};
+
 /*
  * 2ch mode
  */
@@ -6202,6 +6377,34 @@ static struct hda_channel_mode alc885_mbp_6ch_modes[2] = {
        { 6, alc885_mbp_ch6_init },
 };
 
+/*
+ * 2ch
+ * Speakers/Woofer/HP = Front
+ * LineIn = Input
+ */
+static struct hda_verb alc885_mb5_ch2_init[] = {
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       { } /* end */
+};
+
+/*
+ * 6ch mode
+ * Speakers/HP = Front
+ * Woofer = LFE
+ * LineIn = Surround
+ */
+static struct hda_verb alc885_mb5_ch6_init[] = {
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+       { } /* end */
+};
+
+static struct hda_channel_mode alc885_mb5_6ch_modes[2] = {
+       { 2, alc885_mb5_ch2_init },
+       { 6, alc885_mb5_ch6_init },
+};
 
 /* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
  *                 Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
@@ -6244,6 +6447,25 @@ static struct snd_kcontrol_new alc885_mbp3_mixer[] = {
        HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x00, HDA_INPUT),
        { } /* end */
 };
+
+static struct snd_kcontrol_new alc885_mb5_mixer[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
+       HDA_BIND_MUTE   ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
+       HDA_BIND_MUTE   ("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME("LFE Playback Volume", 0x0e, 0x00, HDA_OUTPUT),
+       HDA_BIND_MUTE   ("LFE Playback Switch", 0x0e, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME("HP Playback Volume", 0x0f, 0x00, HDA_OUTPUT),
+       HDA_BIND_MUTE   ("HP Playback Switch", 0x0f, 0x02, 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, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE  ("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line Boost", 0x15, 0x00, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x19, 0x00, HDA_INPUT),
+       { } /* end */
+};
+
 static struct snd_kcontrol_new alc882_w2jc_mixer[] = {
        HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
        HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
@@ -6471,6 +6693,55 @@ static struct hda_verb alc882_macpro_init_verbs[] = {
        { }
 };
 
+/* Macbook 5,1 */
+static struct hda_verb alc885_mb5_init_verbs[] = {
+       /* DACs */
+       {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* Front mixer */
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       /* Surround mixer */
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       /* LFE mixer */
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       /* HP mixer */
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       /* Front Pin (0x0c) */
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* LFE Pin (0x0e) */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01},
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02},
+       /* HP Pin (0x0f) */
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x14, AC_VERB_SET_CONNECT_SEL, 0x03},
+       /* Front Mic pin: input vref at 80% */
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* Line In pin */
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+       { }
+};
+
 /* Macbook Pro rev3 */
 static struct hda_verb alc885_mbp3_init_verbs[] = {
        /* Front mixer: unmute input/output amp left and right (volume = 0) */
@@ -6560,45 +6831,23 @@ static struct hda_verb alc885_imac24_init_verbs[] = {
 };
 
 /* Toggle speaker-output according to the hp-jack state */
-static void alc885_imac24_automute(struct hda_codec *codec)
+static void alc885_imac24_automute_init_hook(struct hda_codec *codec)
 {
-       unsigned int present;
-
-       present = snd_hda_codec_read(codec, 0x14, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       snd_hda_codec_amp_stereo(codec, 0x18, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-       snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
+       struct alc_spec *spec = codec->spec;
 
-/* Processes unsolicited events. */
-static void alc885_imac24_unsol_event(struct hda_codec *codec,
-                                     unsigned int res)
-{
-       /* Headphone insertion or removal. */
-       if ((res >> 26) == ALC880_HP_EVENT)
-               alc885_imac24_automute(codec);
+       spec->autocfg.hp_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[0] = 0x18;
+       spec->autocfg.speaker_pins[1] = 0x1a;
+       alc_automute_amp(codec);
 }
 
-static void alc885_mbp3_automute(struct hda_codec *codec)
+static void alc885_mbp3_init_hook(struct hda_codec *codec)
 {
-       unsigned int present;
-
-       present = snd_hda_codec_read(codec, 0x15, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       snd_hda_codec_amp_stereo(codec, 0x14,  HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-       snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, present ? 0 : HDA_AMP_MUTE);
+       struct alc_spec *spec = codec->spec;
 
-}
-static void alc885_mbp3_unsol_event(struct hda_codec *codec,
-                                   unsigned int res)
-{
-       /* Headphone insertion or removal. */
-       if ((res >> 26) == ALC880_HP_EVENT)
-               alc885_mbp3_automute(codec);
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       alc_automute_amp(codec);
 }
 
 
@@ -6623,24 +6872,25 @@ static struct hda_verb alc882_targa_verbs[] = {
 /* toggle speaker-output according to the hp-jack state */
 static void alc882_targa_automute(struct hda_codec *codec)
 {
-       unsigned int present;
-
-       present = snd_hda_codec_read(codec, 0x14, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+       struct alc_spec *spec = codec->spec;
+       alc_automute_amp(codec);
        snd_hda_codec_write_cache(codec, 1, 0, AC_VERB_SET_GPIO_DATA,
-                                 present ? 1 : 3);
+                                 spec->jack_present ? 1 : 3);
+}
+
+static void alc882_targa_init_hook(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       spec->autocfg.hp_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[0] = 0x1b;
+       alc882_targa_automute(codec);
 }
 
 static void alc882_targa_unsol_event(struct hda_codec *codec, unsigned int res)
 {
-       /* Looks like the unsol event is incompatible with the standard
-        * definition.  4bit tag is placed at 26 bit!
-        */
-       if (((res >> 26) == ALC880_HP_EVENT)) {
+       if ((res >> 26) == ALC880_HP_EVENT)
                alc882_targa_automute(codec);
-       }
 }
 
 static struct hda_verb alc882_asus_a7j_verbs[] = {
@@ -6722,7 +6972,7 @@ static void alc885_macpro_init_hook(struct hda_codec *codec)
 static void alc885_imac24_init_hook(struct hda_codec *codec)
 {
        alc885_macpro_init_hook(codec);
-       alc885_imac24_automute(codec);
+       alc885_imac24_automute_init_hook(codec);
 }
 
 /*
@@ -6815,6 +7065,7 @@ static const char *alc882_models[ALC882_MODEL_LAST] = {
        [ALC882_ASUS_A7J]       = "asus-a7j",
        [ALC882_ASUS_A7M]       = "asus-a7m",
        [ALC885_MACPRO]         = "macpro",
+       [ALC885_MB5]            = "mb5",
        [ALC885_MBP3]           = "mbp3",
        [ALC885_IMAC24]         = "imac24",
        [ALC882_AUTO]           = "auto",
@@ -6892,8 +7143,20 @@ static struct alc_config_preset alc882_presets[] = {
                .input_mux = &alc882_capture_source,
                .dig_out_nid = ALC882_DIGOUT_NID,
                .dig_in_nid = ALC882_DIGIN_NID,
-               .unsol_event = alc885_mbp3_unsol_event,
-               .init_hook = alc885_mbp3_automute,
+               .unsol_event = alc_automute_amp_unsol_event,
+               .init_hook = alc885_mbp3_init_hook,
+       },
+       [ALC885_MB5] = {
+               .mixers = { alc885_mb5_mixer, alc882_chmode_mixer },
+               .init_verbs = { alc885_mb5_init_verbs,
+                               alc880_gpio1_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc882_dac_nids),
+               .dac_nids = alc882_dac_nids,
+               .channel_mode = alc885_mb5_6ch_modes,
+               .num_channel_mode = ARRAY_SIZE(alc885_mb5_6ch_modes),
+               .input_mux = &mb5_capture_source,
+               .dig_out_nid = ALC882_DIGOUT_NID,
+               .dig_in_nid = ALC882_DIGIN_NID,
        },
        [ALC885_MACPRO] = {
                .mixers = { alc882_macpro_mixer },
@@ -6917,7 +7180,7 @@ static struct alc_config_preset alc882_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
                .channel_mode = alc882_ch_modes,
                .input_mux = &alc882_capture_source,
-               .unsol_event = alc885_imac24_unsol_event,
+               .unsol_event = alc_automute_amp_unsol_event,
                .init_hook = alc885_imac24_init_hook,
        },
        [ALC882_TARGA] = {
@@ -6934,7 +7197,7 @@ static struct alc_config_preset alc882_presets[] = {
                .need_dac_fix = 1,
                .input_mux = &alc882_capture_source,
                .unsol_event = alc882_targa_unsol_event,
-               .init_hook = alc882_targa_automute,
+               .init_hook = alc882_targa_init_hook,
        },
        [ALC882_ASUS_A7J] = {
                .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer },
@@ -7014,7 +7277,6 @@ static void alc882_auto_init_multi_out(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        int i;
 
-       alc_subsystem_id(codec, 0x15, 0x1b, 0x14);
        for (i = 0; i <= HDA_SIDE; i++) {
                hda_nid_t nid = spec->autocfg.line_out_pins[i];
                int pin_type = get_pin_type(spec->autocfg.line_out_type);
@@ -7197,10 +7459,17 @@ static int patch_alc882(struct hda_codec *codec)
                case 0x106b00a1: /* Macbook (might be wrong - PCI SSID?) */
                case 0x106b00a4: /* MacbookPro4,1 */
                case 0x106b2c00: /* Macbook Pro rev3 */
-               case 0x106b3600: /* Macbook 3.1 */
+               /* Macbook 3.1 (0x106b3600) is handled by patch_alc883() */
                case 0x106b3800: /* MacbookPro4,1 - latter revision */
                        board_config = ALC885_MBP3;
                        break;
+               case 0x106b3f00: /* Macbook 5,1 */
+               case 0x106b4000: /* Macbook Pro 5,1 - FIXME: HP jack sense
+                                 *   seems not working, so apparently
+                                 *   no perfect solution yet
+                                 */
+                       board_config = ALC885_MB5;
+                       break;
                default:
                        /* ALC889A is handled better as ALC888-compatible */
                        if (codec->revision_id == 0x100101 ||
@@ -7208,8 +7477,9 @@ static int patch_alc882(struct hda_codec *codec)
                                alc_free(codec);
                                return patch_alc883(codec);
                        }
-                       printk(KERN_INFO "hda_codec: Unknown model for ALC882, "
-                                        "trying auto-probe from BIOS...\n");
+                       printk(KERN_INFO "hda_codec: Unknown model for %s, "
+                              "trying auto-probe from BIOS...\n",
+                              codec->chip_name);
                        board_config = ALC882_AUTO;
                }
        }
@@ -7239,14 +7509,6 @@ static int patch_alc882(struct hda_codec *codec)
        if (board_config != ALC882_AUTO)
                setup_preset(spec, &alc882_presets[board_config]);
 
-       if (codec->vendor_id == 0x10ec0885) {
-               spec->stream_name_analog = "ALC885 Analog";
-               spec->stream_name_digital = "ALC885 Digital";
-       } else {
-               spec->stream_name_analog = "ALC882 Analog";
-               spec->stream_name_digital = "ALC882 Digital";
-       }
-
        spec->stream_analog_playback = &alc882_pcm_analog_playback;
        spec->stream_analog_capture = &alc882_pcm_analog_capture;
        /* FIXME: setup DAC5 */
@@ -7399,6 +7661,17 @@ static struct hda_input_mux alc883_asus_eee1601_capture_source = {
        },
 };
 
+static struct hda_input_mux alc889A_mb31_capture_source = {
+       .num_items = 2,
+       .items = {
+               { "Mic", 0x0 },
+               /* Front Mic (0x01) unused */
+               { "Line", 0x2 },
+               /* Line 2 (0x03) unused */
+               /* CD (0x04) unsused? */
+       },
+};
+
 /*
  * 2ch mode
  */
@@ -7448,6 +7721,73 @@ static struct hda_channel_mode alc883_3ST_6ch_modes[3] = {
        { 6, alc883_3ST_ch6_init },
 };
 
+
+/*
+ * 2ch mode
+ */
+static struct hda_verb alc883_4ST_ch2_init[] = {
+       { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+       { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+       { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+       { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+       { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+       { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+       { } /* end */
+};
+
+/*
+ * 4ch mode
+ */
+static struct hda_verb alc883_4ST_ch4_init[] = {
+       { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+       { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+       { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+       { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+       { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+       { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+       { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+       { } /* end */
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc883_4ST_ch6_init[] = {
+       { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+       { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+       { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+       { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+       { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
+       { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+       { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+       { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+       { } /* end */
+};
+
+/*
+ * 8ch mode
+ */
+static struct hda_verb alc883_4ST_ch8_init[] = {
+       { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+       { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+       { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 },
+       { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+       { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+       { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
+       { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+       { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+       { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+       { } /* end */
+};
+
+static struct hda_channel_mode alc883_4ST_8ch_modes[4] = {
+       { 2, alc883_4ST_ch2_init },
+       { 4, alc883_4ST_ch4_init },
+       { 6, alc883_4ST_ch6_init },
+       { 8, alc883_4ST_ch8_init },
+};
+
+
 /*
  * 2ch mode
  */
@@ -7517,6 +7857,49 @@ static struct hda_channel_mode alc883_sixstack_modes[2] = {
        { 8, alc883_sixstack_ch8_init },
 };
 
+/* 2ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:front) */
+static struct hda_verb alc889A_mb31_ch2_init[] = {
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},             /* HP as front */
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},    /* Line as input */
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},   /* Line off */
+       { } /* end */
+};
+
+/* 4ch mode (Speaker:front, Subwoofer:CLFE, Line:CLFE, Headphones:front) */
+static struct hda_verb alc889A_mb31_ch4_init[] = {
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},             /* HP as front */
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},   /* Line as output */
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Line on */
+       { } /* end */
+};
+
+/* 5ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:rear) */
+static struct hda_verb alc889A_mb31_ch5_init[] = {
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},             /* HP as rear */
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},    /* Line as input */
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},   /* Line off */
+       { } /* end */
+};
+
+/* 6ch mode (Speaker:front, Subwoofer:off, Line:CLFE, Headphones:Rear) */
+static struct hda_verb alc889A_mb31_ch6_init[] = {
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},             /* HP as front */
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},   /* Subwoofer off */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},   /* Line as output */
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Line on */
+       { } /* end */
+};
+
+static struct hda_channel_mode alc889A_mb31_6ch_modes[4] = {
+       { 2, alc889A_mb31_ch2_init },
+       { 4, alc889A_mb31_ch4_init },
+       { 5, alc889A_mb31_ch5_init },
+       { 6, alc889A_mb31_ch6_init },
+};
+
 static struct hda_verb alc883_medion_eapd_verbs[] = {
         /* eanable EAPD on medion laptop */
        {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
@@ -7782,8 +8165,6 @@ static struct snd_kcontrol_new alc888_lenovo_sky_mixer[] = {
        HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT),
        HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
        HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
-       HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("iSpeaker 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),
@@ -7797,6 +8178,42 @@ static struct snd_kcontrol_new alc888_lenovo_sky_mixer[] = {
        { } /* end */
 };
 
+static struct snd_kcontrol_new alc889A_mb31_mixer[] = {
+       /* Output mixers */
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
+       HDA_BIND_MUTE("Front Playback Switch", 0x0c, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
+       HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x00,
+               HDA_OUTPUT),
+       HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x00, HDA_OUTPUT),
+       HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x02, HDA_INPUT),
+       /* Output switches */
+       HDA_CODEC_MUTE("Enable Speaker", 0x14, 0x00, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Enable Headphones", 0x15, 0x00, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("Enable LFE", 0x16, 2, 0x00, HDA_OUTPUT),
+       /* Boost mixers */
+       HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x00, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line Boost", 0x1a, 0x00, HDA_INPUT),
+       /* Input mixers */
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+       { } /* end */
+};
+
+static struct snd_kcontrol_new alc883_vaiott_mixer[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+       { } /* end */
+};
+
 static struct hda_bind_ctls alc883_bind_cap_vol = {
        .ops = &snd_hda_bind_vol,
        .values = {
@@ -7932,16 +8349,14 @@ static struct hda_verb alc883_init_verbs[] = {
 };
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc883_mitac_hp_automute(struct hda_codec *codec)
+static void alc883_mitac_init_hook(struct hda_codec *codec)
 {
-       unsigned int present;
+       struct alc_spec *spec = codec->spec;
 
-       present = snd_hda_codec_read(codec, 0x15, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-       snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[1] = 0x17;
+       alc_automute_amp(codec);
 }
 
 /* auto-toggle front mic */
@@ -7958,25 +8373,6 @@ static void alc883_mitac_mic_automute(struct hda_codec *codec)
 }
 */
 
-static void alc883_mitac_automute(struct hda_codec *codec)
-{
-       alc883_mitac_hp_automute(codec);
-       /* alc883_mitac_mic_automute(codec); */
-}
-
-static void alc883_mitac_unsol_event(struct hda_codec *codec,
-                                          unsigned int res)
-{
-       switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               alc883_mitac_hp_automute(codec);
-               break;
-       case ALC880_MIC_EVENT:
-               /* alc883_mitac_mic_automute(codec); */
-               break;
-       }
-}
-
 static struct hda_verb alc883_mitac_verbs[] = {
        /* HP */
        {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
@@ -8028,14 +8424,24 @@ static struct hda_verb alc883_tagra_verbs[] = {
        {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
        {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
 
-       {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
-       {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */
-       {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
+/* Connect Line-Out side jack (SPDIF) to Side */
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
+/* Connect Mic jack to CLFE */
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x18, AC_VERB_SET_CONNECT_SEL, 0x02},
+/* Connect Line-in jack to Surround */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01},
+/* Connect HP out jack to Front */
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
 
        {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-       {0x01, AC_VERB_SET_GPIO_MASK, 0x03},
-       {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03},
-       {0x01, AC_VERB_SET_GPIO_DATA, 0x03},
 
        { } /* end */
 };
@@ -8094,29 +8500,26 @@ static struct hda_verb alc888_6st_dell_verbs[] = {
        { }
 };
 
-static void alc888_3st_hp_front_automute(struct hda_codec *codec)
-{
-       unsigned int present, bits;
+static struct hda_verb alc883_vaiott_verbs[] = {
+       /* HP */
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 
-       present = snd_hda_codec_read(codec, 0x1b, 0,
-                       AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       bits = present ? HDA_AMP_MUTE : 0;
-       snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-       snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-       snd_hda_codec_amp_stereo(codec, 0x18, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-}
+       /* enable unsolicited event */
+       {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+
+       { } /* end */
+};
 
-static void alc888_3st_hp_unsol_event(struct hda_codec *codec,
-                                     unsigned int res)
+static void alc888_3st_hp_init_hook(struct hda_codec *codec)
 {
-       switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               alc888_3st_hp_front_automute(codec);
-               break;
-       }
+       struct alc_spec *spec = codec->spec;
+
+       spec->autocfg.hp_pins[0] = 0x1b;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[1] = 0x16;
+       spec->autocfg.speaker_pins[2] = 0x18;
+       alc_automute_amp(codec);
 }
 
 static struct hda_verb alc888_3st_hp_verbs[] = {
@@ -8213,56 +8616,18 @@ static struct hda_verb alc883_medion_md2_verbs[] = {
 };
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc883_medion_md2_automute(struct hda_codec *codec)
+static void alc883_medion_md2_init_hook(struct hda_codec *codec)
 {
-       unsigned int present;
-
-       present = snd_hda_codec_read(codec, 0x14, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-static void alc883_medion_md2_unsol_event(struct hda_codec *codec,
-                                         unsigned int res)
-{
-       if ((res >> 26) == ALC880_HP_EVENT)
-               alc883_medion_md2_automute(codec);
-}
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc883_tagra_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_codec_read(codec, 0x14, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       bits = present ? HDA_AMP_MUTE : 0;
-       snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-       snd_hda_codec_write_cache(codec, 1, 0, AC_VERB_SET_GPIO_DATA,
-                                 present ? 1 : 3);
-}
+       struct alc_spec *spec = codec->spec;
 
-static void alc883_tagra_unsol_event(struct hda_codec *codec, unsigned int res)
-{
-       if ((res >> 26) == ALC880_HP_EVENT)
-               alc883_tagra_automute(codec);
+       spec->autocfg.hp_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[0] = 0x15;
+       alc_automute_amp(codec);
 }
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc883_clevo_m720_hp_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0)
-               & AC_PINSENSE_PRESENCE;
-       bits = present ? HDA_AMP_MUTE : 0;
-       snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-}
+#define alc883_tagra_init_hook         alc882_targa_init_hook
+#define alc883_tagra_unsol_event       alc882_targa_unsol_event
 
 static void alc883_clevo_m720_mic_automute(struct hda_codec *codec)
 {
@@ -8274,9 +8639,13 @@ static void alc883_clevo_m720_mic_automute(struct hda_codec *codec)
                                 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
 }
 
-static void alc883_clevo_m720_automute(struct hda_codec *codec)
+static void alc883_clevo_m720_init_hook(struct hda_codec *codec)
 {
-       alc883_clevo_m720_hp_automute(codec);
+       struct alc_spec *spec = codec->spec;
+
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       alc_automute_amp(codec);
        alc883_clevo_m720_mic_automute(codec);
 }
 
@@ -8284,52 +8653,32 @@ static void alc883_clevo_m720_unsol_event(struct hda_codec *codec,
                                           unsigned int res)
 {
        switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               alc883_clevo_m720_hp_automute(codec);
-               break;
        case ALC880_MIC_EVENT:
                alc883_clevo_m720_mic_automute(codec);
                break;
+       default:
+               alc_automute_amp_unsol_event(codec, res);
+               break;
        }
 }
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc883_2ch_fujitsu_pi2515_automute(struct hda_codec *codec)
+static void alc883_2ch_fujitsu_pi2515_init_hook(struct hda_codec *codec)
 {
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0)
-               & AC_PINSENSE_PRESENCE;
-       bits = present ? HDA_AMP_MUTE : 0;
-       snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-}
+       struct alc_spec *spec = codec->spec;
 
-static void alc883_2ch_fujitsu_pi2515_unsol_event(struct hda_codec *codec,
-                                                 unsigned int res)
-{
-       if ((res >> 26) == ALC880_HP_EVENT)
-               alc883_2ch_fujitsu_pi2515_automute(codec);
+       spec->autocfg.hp_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[0] = 0x15;
+       alc_automute_amp(codec);
 }
 
-static void alc883_haier_w66_automute(struct hda_codec *codec)
+static void alc883_haier_w66_init_hook(struct hda_codec *codec)
 {
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_codec_read(codec, 0x1b, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       bits = present ? 0x80 : 0;
-       snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                0x80, bits);
-}
+       struct alc_spec *spec = codec->spec;
 
-static void alc883_haier_w66_unsol_event(struct hda_codec *codec,
-                                        unsigned int res)
-{
-       if ((res >> 26) == ALC880_HP_EVENT)
-               alc883_haier_w66_automute(codec);
+       spec->autocfg.hp_pins[0] = 0x1b;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       alc_automute_amp(codec);
 }
 
 static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
@@ -8337,8 +8686,8 @@ static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
        unsigned int present;
        unsigned char bits;
 
-       present = snd_hda_codec_read(codec, 0x14, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0)
+               & AC_PINSENSE_PRESENCE;
        bits = present ? HDA_AMP_MUTE : 0;
        snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
                                 HDA_AMP_MUTE, bits);
@@ -8368,23 +8717,14 @@ static void alc883_lenovo_101e_unsol_event(struct hda_codec *codec,
 }
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc883_acer_aspire_automute(struct hda_codec *codec)
+static void alc883_acer_aspire_init_hook(struct hda_codec *codec)
 {
-       unsigned int present;
-
-       present = snd_hda_codec_read(codec, 0x14, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-       snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
+       struct alc_spec *spec = codec->spec;
 
-static void alc883_acer_aspire_unsol_event(struct hda_codec *codec,
-                                          unsigned int res)
-{
-       if ((res >> 26) == ALC880_HP_EVENT)
-               alc883_acer_aspire_automute(codec);
+       spec->autocfg.hp_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[1] = 0x16;
+       alc_automute_amp(codec);
 }
 
 static struct hda_verb alc883_acer_eapd_verbs[] = {
@@ -8405,75 +8745,39 @@ static struct hda_verb alc883_acer_eapd_verbs[] = {
        { }
 };
 
-static void alc888_6st_dell_front_automute(struct hda_codec *codec)
+static void alc888_6st_dell_init_hook(struct hda_codec *codec)
 {
-       unsigned int present;
-
-       present = snd_hda_codec_read(codec, 0x1b, 0,
-                               AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                               HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-       snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-                               HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-       snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
-                               HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-       snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
-                               HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
+       struct alc_spec *spec = codec->spec;
 
-static void alc888_6st_dell_unsol_event(struct hda_codec *codec,
-                                            unsigned int res)
-{
-       switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               /* printk(KERN_DEBUG "hp_event\n"); */
-               alc888_6st_dell_front_automute(codec);
-               break;
-       }
+       spec->autocfg.hp_pins[0] = 0x1b;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[1] = 0x15;
+       spec->autocfg.speaker_pins[2] = 0x16;
+       spec->autocfg.speaker_pins[3] = 0x17;
+       alc_automute_amp(codec);
 }
 
-static void alc888_lenovo_sky_front_automute(struct hda_codec *codec)
+static void alc888_lenovo_sky_init_hook(struct hda_codec *codec)
 {
-       unsigned int mute;
-       unsigned int present;
+       struct alc_spec *spec = codec->spec;
 
-       snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0);
-       present = snd_hda_codec_read(codec, 0x1b, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0);
-       present = (present & 0x80000000) != 0;
-       if (present) {
-               /* mute internal speaker */
-               snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, HDA_AMP_MUTE);
-               snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, HDA_AMP_MUTE);
-               snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, HDA_AMP_MUTE);
-               snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, HDA_AMP_MUTE);
-               snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, HDA_AMP_MUTE);
-       } else {
-               /* unmute internal speaker if necessary */
-               mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0);
-               snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, mute);
-               snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, mute);
-               snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, mute);
-               snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, mute);
-               snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, mute);
-       }
+       spec->autocfg.hp_pins[0] = 0x1b;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[1] = 0x15;
+       spec->autocfg.speaker_pins[2] = 0x16;
+       spec->autocfg.speaker_pins[3] = 0x17;
+       spec->autocfg.speaker_pins[4] = 0x1a;
+       alc_automute_amp(codec);
 }
 
-static void alc883_lenovo_sky_unsol_event(struct hda_codec *codec,
-                                            unsigned int res)
+static void alc883_vaiott_init_hook(struct hda_codec *codec)
 {
-       if ((res >> 26) == ALC880_HP_EVENT)
-               alc888_lenovo_sky_front_automute(codec);
+       struct alc_spec *spec = codec->spec;
+
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[1] = 0x17;
+       alc_automute_amp(codec);
 }
 
 /*
@@ -8561,39 +8865,33 @@ static void alc883_nb_mic_automute(struct hda_codec *codec)
                            0x7000 | (0x01 << 8) | (present ? 0x80 : 0));
 }
 
-static void alc883_M90V_speaker_automute(struct hda_codec *codec)
+static void alc883_M90V_init_hook(struct hda_codec *codec)
 {
-       unsigned int present;
-       unsigned char bits;
+       struct alc_spec *spec = codec->spec;
 
-       present = snd_hda_codec_read(codec, 0x1b, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0)
-               & AC_PINSENSE_PRESENCE;
-       bits = present ? 0 : PIN_OUT;
-       snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           bits);
-       snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           bits);
-       snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           bits);
+       spec->autocfg.hp_pins[0] = 0x1b;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[1] = 0x15;
+       spec->autocfg.speaker_pins[2] = 0x16;
+       alc_automute_pin(codec);
 }
 
 static void alc883_mode2_unsol_event(struct hda_codec *codec,
                                           unsigned int res)
 {
        switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               alc883_M90V_speaker_automute(codec);
-               break;
        case ALC880_MIC_EVENT:
                alc883_nb_mic_automute(codec);
                break;
+       default:
+               alc_sku_unsol_event(codec, res);
+               break;
        }
 }
 
 static void alc883_mode2_inithook(struct hda_codec *codec)
 {
-       alc883_M90V_speaker_automute(codec);
+       alc883_M90V_init_hook(codec);
        alc883_nb_mic_automute(codec);
 }
 
@@ -8610,32 +8908,49 @@ static struct hda_verb alc888_asus_eee1601_verbs[] = {
        { } /* end */
 };
 
-static void alc883_eee1601_speaker_automute(struct hda_codec *codec)
+static void alc883_eee1601_inithook(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       spec->autocfg.hp_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[0] = 0x1b;
+       alc_automute_pin(codec);
+}
+
+static struct hda_verb alc889A_mb31_verbs[] = {
+       /* Init rear pin (used as headphone output) */
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4},    /* Apple Headphones */
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},           /* Connect to front */
+       {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+       /* Init line pin (used as output in 4ch and 6ch mode) */
+       {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02},           /* Connect to CLFE */
+       /* Init line 2 pin (used as headphone out by default) */
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},  /* Use as input */
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Mute output */
+       { } /* end */
+};
+
+/* Mute speakers according to the headphone jack state */
+static void alc889A_mb31_automute(struct hda_codec *codec)
 {
        unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_codec_read(codec, 0x14, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0)
-               & AC_PINSENSE_PRESENCE;
-       bits = present ? 0 : PIN_OUT;
-       snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           bits);
-}
 
-static void alc883_eee1601_unsol_event(struct hda_codec *codec,
-                                          unsigned int res)
-{
-       switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               alc883_eee1601_speaker_automute(codec);
-               break;
+       /* Mute only in 2ch or 4ch mode */
+       if (snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_CONNECT_SEL, 0)
+           == 0x00) {
+               present = snd_hda_codec_read(codec, 0x15, 0,
+                       AC_VERB_GET_PIN_SENSE, 0) & AC_PINSENSE_PRESENCE;
+               snd_hda_codec_amp_stereo(codec, 0x14,  HDA_OUTPUT, 0,
+                       HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+               snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
+                       HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
        }
 }
 
-static void alc883_eee1601_inithook(struct hda_codec *codec)
+static void alc889A_mb31_unsol_event(struct hda_codec *codec, unsigned int res)
 {
-       alc883_eee1601_speaker_automute(codec);
+       if ((res >> 26) == ALC880_HP_EVENT)
+               alc889A_mb31_automute(codec);
 }
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -8659,9 +8974,11 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {
        [ALC883_6ST_DIG]        = "6stack-dig",
        [ALC883_TARGA_DIG]      = "targa-dig",
        [ALC883_TARGA_2ch_DIG]  = "targa-2ch-dig",
+       [ALC883_TARGA_8ch_DIG]  = "targa-8ch-dig",
        [ALC883_ACER]           = "acer",
        [ALC883_ACER_ASPIRE]    = "acer-aspire",
        [ALC888_ACER_ASPIRE_4930G]      = "acer-aspire-4930g",
+       [ALC888_ACER_ASPIRE_8930G]      = "acer-aspire-8930g",
        [ALC883_MEDION]         = "medion",
        [ALC883_MEDION_MD2]     = "medion-md2",
        [ALC883_LAPTOP_EAPD]    = "laptop-eapd",
@@ -8678,6 +8995,8 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {
        [ALC888_FUJITSU_XA3530] = "fujitsu-xa3530",
        [ALC883_3ST_6ch_INTEL]  = "3stack-6ch-intel",
        [ALC1200_ASUS_P5Q]      = "asus-p5q",
+       [ALC889A_MB31]          = "mb31",
+       [ALC883_SONY_VAIO_TT]   = "sony-vaio-tt",
        [ALC883_AUTO]           = "auto",
 };
 
@@ -8693,14 +9012,18 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
                ALC888_ACER_ASPIRE_4930G),
        SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G",
                ALC888_ACER_ASPIRE_4930G),
+       SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G",
+               ALC888_ACER_ASPIRE_8930G),
        SND_PCI_QUIRK(0x1025, 0x0157, "Acer X3200", ALC883_AUTO),
        SND_PCI_QUIRK(0x1025, 0x0158, "Acer AX1700-U3700A", ALC883_AUTO),
        SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G",
                ALC888_ACER_ASPIRE_4930G),
        SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G",
                ALC888_ACER_ASPIRE_4930G),
-       /* default Acer */
-       SND_PCI_QUIRK_VENDOR(0x1025, "Acer laptop", ALC883_ACER),
+       /* default Acer -- disabled as it causes more problems.
+        *    model=auto should work fine now
+        */
+       /* SND_PCI_QUIRK_VENDOR(0x1025, "Acer laptop", ALC883_ACER), */
        SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL),
        SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG),
        SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
@@ -8736,6 +9059,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
        SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG),
        SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG),
        SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG),
+       SND_PCI_QUIRK(0x1462, 0x6510, "MSI GX620", ALC883_TARGA_8ch_DIG),
        SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG),
        SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
        SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG),
@@ -8768,6 +9092,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
        SND_PCI_QUIRK(0x8086, 0x2503, "82801H", ALC883_MITAC),
        SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC883_3ST_6ch_INTEL),
        SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch),
+       SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC883_SONY_VAIO_TT),
        {}
 };
 
@@ -8848,7 +9173,7 @@ static struct alc_config_preset alc883_presets[] = {
                .need_dac_fix = 1,
                .input_mux = &alc883_capture_source,
                .unsol_event = alc883_tagra_unsol_event,
-               .init_hook = alc883_tagra_automute,
+               .init_hook = alc883_tagra_init_hook,
        },
        [ALC883_TARGA_2ch_DIG] = {
                .mixers = { alc883_tagra_2ch_mixer},
@@ -8862,7 +9187,25 @@ static struct alc_config_preset alc883_presets[] = {
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_capture_source,
                .unsol_event = alc883_tagra_unsol_event,
-               .init_hook = alc883_tagra_automute,
+               .init_hook = alc883_tagra_init_hook,
+       },
+       [ALC883_TARGA_8ch_DIG] = {
+               .mixers = { alc883_base_mixer, alc883_chmode_mixer },
+               .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs,
+                               alc883_tagra_verbs },
+               .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+               .dac_nids = alc883_dac_nids,
+               .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
+               .adc_nids = alc883_adc_nids_rev,
+               .capsrc_nids = alc883_capsrc_nids_rev,
+               .dig_out_nid = ALC883_DIGOUT_NID,
+               .dig_in_nid = ALC883_DIGIN_NID,
+               .num_channel_mode = ARRAY_SIZE(alc883_4ST_8ch_modes),
+               .channel_mode = alc883_4ST_8ch_modes,
+               .need_dac_fix = 1,
+               .input_mux = &alc883_capture_source,
+               .unsol_event = alc883_tagra_unsol_event,
+               .init_hook = alc883_tagra_init_hook,
        },
        [ALC883_ACER] = {
                .mixers = { alc883_base_mixer },
@@ -8887,8 +9230,8 @@ static struct alc_config_preset alc883_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_capture_source,
-               .unsol_event = alc883_acer_aspire_unsol_event,
-               .init_hook = alc883_acer_aspire_automute,
+               .unsol_event = alc_automute_amp_unsol_event,
+               .init_hook = alc883_acer_aspire_init_hook,
        },
        [ALC888_ACER_ASPIRE_4930G] = {
                .mixers = { alc888_base_mixer,
@@ -8907,8 +9250,29 @@ static struct alc_config_preset alc883_presets[] = {
                .num_mux_defs =
                        ARRAY_SIZE(alc888_2_capture_sources),
                .input_mux = alc888_2_capture_sources,
-               .unsol_event = alc888_acer_aspire_4930g_unsol_event,
-               .init_hook = alc888_acer_aspire_4930g_automute,
+               .unsol_event = alc_automute_amp_unsol_event,
+               .init_hook = alc888_acer_aspire_4930g_init_hook,
+       },
+       [ALC888_ACER_ASPIRE_8930G] = {
+               .mixers = { alc888_base_mixer,
+                               alc883_chmode_mixer },
+               .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
+                               alc889_acer_aspire_8930g_verbs },
+               .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+               .dac_nids = alc883_dac_nids,
+               .num_adc_nids = ARRAY_SIZE(alc889_adc_nids),
+               .adc_nids = alc889_adc_nids,
+               .capsrc_nids = alc889_capsrc_nids,
+               .dig_out_nid = ALC883_DIGOUT_NID,
+               .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
+               .channel_mode = alc883_3ST_6ch_modes,
+               .need_dac_fix = 1,
+               .const_channel_count = 6,
+               .num_mux_defs =
+                       ARRAY_SIZE(alc889_capture_sources),
+               .input_mux = alc889_capture_sources,
+               .unsol_event = alc_automute_amp_unsol_event,
+               .init_hook = alc889_acer_aspire_8930g_init_hook,
        },
        [ALC883_MEDION] = {
                .mixers = { alc883_fivestack_mixer,
@@ -8932,8 +9296,8 @@ static struct alc_config_preset alc883_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_capture_source,
-               .unsol_event = alc883_medion_md2_unsol_event,
-               .init_hook = alc883_medion_md2_automute,
+               .unsol_event = alc_automute_amp_unsol_event,
+               .init_hook = alc883_medion_md2_init_hook,
        },
        [ALC883_LAPTOP_EAPD] = {
                .mixers = { alc883_base_mixer },
@@ -8954,7 +9318,7 @@ static struct alc_config_preset alc883_presets[] = {
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_capture_source,
                .unsol_event = alc883_clevo_m720_unsol_event,
-               .init_hook = alc883_clevo_m720_automute,
+               .init_hook = alc883_clevo_m720_init_hook,
        },
        [ALC883_LENOVO_101E_2ch] = {
                .mixers = { alc883_lenovo_101e_2ch_mixer},
@@ -8978,8 +9342,8 @@ static struct alc_config_preset alc883_presets[] = {
                .channel_mode = alc883_3ST_2ch_modes,
                .need_dac_fix = 1,
                .input_mux = &alc883_lenovo_nb0763_capture_source,
-               .unsol_event = alc883_medion_md2_unsol_event,
-               .init_hook = alc883_medion_md2_automute,
+               .unsol_event = alc_automute_amp_unsol_event,
+               .init_hook = alc883_medion_md2_init_hook,
        },
        [ALC888_LENOVO_MS7195_DIG] = {
                .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
@@ -9003,8 +9367,8 @@ static struct alc_config_preset alc883_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_capture_source,
-               .unsol_event = alc883_haier_w66_unsol_event,
-               .init_hook = alc883_haier_w66_automute,
+               .unsol_event = alc_automute_amp_unsol_event,
+               .init_hook = alc883_haier_w66_init_hook,
        },
        [ALC888_3ST_HP] = {
                .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
@@ -9015,8 +9379,8 @@ static struct alc_config_preset alc883_presets[] = {
                .channel_mode = alc888_3st_hp_modes,
                .need_dac_fix = 1,
                .input_mux = &alc883_capture_source,
-               .unsol_event = alc888_3st_hp_unsol_event,
-               .init_hook = alc888_3st_hp_front_automute,
+               .unsol_event = alc_automute_amp_unsol_event,
+               .init_hook = alc888_3st_hp_init_hook,
        },
        [ALC888_6ST_DELL] = {
                .mixers = { alc883_base_mixer, alc883_chmode_mixer },
@@ -9028,8 +9392,8 @@ static struct alc_config_preset alc883_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
                .channel_mode = alc883_sixstack_modes,
                .input_mux = &alc883_capture_source,
-               .unsol_event = alc888_6st_dell_unsol_event,
-               .init_hook = alc888_6st_dell_front_automute,
+               .unsol_event = alc_automute_amp_unsol_event,
+               .init_hook = alc888_6st_dell_init_hook,
        },
        [ALC883_MITAC] = {
                .mixers = { alc883_mitac_mixer },
@@ -9039,8 +9403,8 @@ static struct alc_config_preset alc883_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_capture_source,
-               .unsol_event = alc883_mitac_unsol_event,
-               .init_hook = alc883_mitac_automute,
+               .unsol_event = alc_automute_amp_unsol_event,
+               .init_hook = alc883_mitac_init_hook,
        },
        [ALC883_FUJITSU_PI2515] = {
                .mixers = { alc883_2ch_fujitsu_pi2515_mixer },
@@ -9052,8 +9416,8 @@ static struct alc_config_preset alc883_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_fujitsu_pi2515_capture_source,
-               .unsol_event = alc883_2ch_fujitsu_pi2515_unsol_event,
-               .init_hook = alc883_2ch_fujitsu_pi2515_automute,
+               .unsol_event = alc_automute_amp_unsol_event,
+               .init_hook = alc883_2ch_fujitsu_pi2515_init_hook,
        },
        [ALC888_FUJITSU_XA3530] = {
                .mixers = { alc888_base_mixer, alc883_chmode_mixer },
@@ -9070,8 +9434,8 @@ static struct alc_config_preset alc883_presets[] = {
                .num_mux_defs =
                        ARRAY_SIZE(alc888_2_capture_sources),
                .input_mux = alc888_2_capture_sources,
-               .unsol_event = alc888_fujitsu_xa3530_unsol_event,
-               .init_hook = alc888_fujitsu_xa3530_automute,
+               .unsol_event = alc_automute_amp_unsol_event,
+               .init_hook = alc888_fujitsu_xa3530_init_hook,
        },
        [ALC888_LENOVO_SKY] = {
                .mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer },
@@ -9083,8 +9447,8 @@ static struct alc_config_preset alc883_presets[] = {
                .channel_mode = alc883_sixstack_modes,
                .need_dac_fix = 1,
                .input_mux = &alc883_lenovo_sky_capture_source,
-               .unsol_event = alc883_lenovo_sky_unsol_event,
-               .init_hook = alc888_lenovo_sky_front_automute,
+               .unsol_event = alc_automute_amp_unsol_event,
+               .init_hook = alc888_lenovo_sky_init_hook,
        },
        [ALC888_ASUS_M90V] = {
                .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
@@ -9112,7 +9476,7 @@ static struct alc_config_preset alc883_presets[] = {
                .channel_mode = alc883_3ST_2ch_modes,
                .need_dac_fix = 1,
                .input_mux = &alc883_asus_eee1601_capture_source,
-               .unsol_event = alc883_eee1601_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .init_hook = alc883_eee1601_inithook,
        },
        [ALC1200_ASUS_P5Q] = {
@@ -9127,6 +9491,32 @@ static struct alc_config_preset alc883_presets[] = {
                .channel_mode = alc883_sixstack_modes,
                .input_mux = &alc883_capture_source,
        },
+       [ALC889A_MB31] = {
+               .mixers = { alc889A_mb31_mixer, alc883_chmode_mixer},
+               .init_verbs = { alc883_init_verbs, alc889A_mb31_verbs,
+                       alc880_gpio1_init_verbs },
+               .adc_nids = alc883_adc_nids,
+               .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+               .dac_nids = alc883_dac_nids,
+               .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+               .channel_mode = alc889A_mb31_6ch_modes,
+               .num_channel_mode = ARRAY_SIZE(alc889A_mb31_6ch_modes),
+               .input_mux = &alc889A_mb31_capture_source,
+               .dig_out_nid = ALC883_DIGOUT_NID,
+               .unsol_event = alc889A_mb31_unsol_event,
+               .init_hook = alc889A_mb31_automute,
+       },
+       [ALC883_SONY_VAIO_TT] = {
+               .mixers = { alc883_vaiott_mixer },
+               .init_verbs = { alc883_init_verbs, alc883_vaiott_verbs },
+               .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+               .dac_nids = alc883_dac_nids,
+               .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+               .channel_mode = alc883_3ST_2ch_modes,
+               .input_mux = &alc883_capture_source,
+               .unsol_event = alc_automute_amp_unsol_event,
+               .init_hook = alc883_vaiott_init_hook,
+       },
 };
 
 
@@ -9155,7 +9545,6 @@ static void alc883_auto_init_multi_out(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        int i;
 
-       alc_subsystem_id(codec, 0x15, 0x1b, 0x14);
        for (i = 0; i <= HDA_SIDE; i++) {
                hda_nid_t nid = spec->autocfg.line_out_pins[i];
                int pin_type = get_pin_type(spec->autocfg.line_out_type);
@@ -9273,10 +9662,18 @@ static int patch_alc883(struct hda_codec *codec)
        board_config = snd_hda_check_board_config(codec, ALC883_MODEL_LAST,
                                                  alc883_models,
                                                  alc883_cfg_tbl);
-       if (board_config < 0) {
-               printk(KERN_INFO "hda_codec: Unknown model for ALC883, "
-                      "trying auto-probe from BIOS...\n");
-               board_config = ALC883_AUTO;
+       if (board_config < 0 || board_config >= ALC883_MODEL_LAST) {
+               /* Pick up systems that don't supply PCI SSID */
+               switch (codec->subsystem_id) {
+               case 0x106b3600: /* Macbook 3.1 */
+                       board_config = ALC889A_MB31;
+                       break;
+               default:
+                       printk(KERN_INFO
+                               "hda_codec: Unknown model for %s, trying "
+                               "auto-probe from BIOS...\n", codec->chip_name);
+                       board_config = ALC883_AUTO;
+               }
        }
 
        if (board_config == ALC883_AUTO) {
@@ -9304,13 +9701,6 @@ static int patch_alc883(struct hda_codec *codec)
 
        switch (codec->vendor_id) {
        case 0x10ec0888:
-               if (codec->revision_id == 0x100101) {
-                       spec->stream_name_analog = "ALC1200 Analog";
-                       spec->stream_name_digital = "ALC1200 Digital";
-               } else {
-                       spec->stream_name_analog = "ALC888 Analog";
-                       spec->stream_name_digital = "ALC888 Digital";
-               }
                if (!spec->num_adc_nids) {
                        spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
                        spec->adc_nids = alc883_adc_nids;
@@ -9318,10 +9708,9 @@ static int patch_alc883(struct hda_codec *codec)
                if (!spec->capsrc_nids)
                        spec->capsrc_nids = alc883_capsrc_nids;
                spec->capture_style = CAPT_MIX; /* matrix-style capture */
+               spec->init_amp = ALC_INIT_DEFAULT; /* always initialize */
                break;
        case 0x10ec0889:
-               spec->stream_name_analog = "ALC889 Analog";
-               spec->stream_name_digital = "ALC889 Digital";
                if (!spec->num_adc_nids) {
                        spec->num_adc_nids = ARRAY_SIZE(alc889_adc_nids);
                        spec->adc_nids = alc889_adc_nids;
@@ -9332,8 +9721,6 @@ static int patch_alc883(struct hda_codec *codec)
                                                        capture */
                break;
        default:
-               spec->stream_name_analog = "ALC883 Analog";
-               spec->stream_name_digital = "ALC883 Digital";
                if (!spec->num_adc_nids) {
                        spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
                        spec->adc_nids = alc883_adc_nids;
@@ -9413,24 +9800,6 @@ static struct snd_kcontrol_new alc262_base_mixer[] = {
        { } /* end */
 };
 
-static struct snd_kcontrol_new alc262_hippo1_mixer[] = {
-       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Front Playback Switch", 0x14, 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("Mic Boost", 0x18, 0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
-       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
-       HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
-       /*HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT),*/
-       HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-       { } /* end */
-};
-
 /* update HP, line and mono-out pins according to the master switch */
 static void alc262_hp_master_update(struct hda_codec *codec)
 {
@@ -9486,14 +9855,7 @@ static void alc262_hp_wildwest_unsol_event(struct hda_codec *codec,
        alc262_hp_wildwest_automute(codec);
 }
 
-static int alc262_hp_master_sw_get(struct snd_kcontrol *kcontrol,
-                                  struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct alc_spec *spec = codec->spec;
-       *ucontrol->value.integer.value = spec->master_sw;
-       return 0;
-}
+#define alc262_hp_master_sw_get                alc260_hp_master_sw_get
 
 static int alc262_hp_master_sw_put(struct snd_kcontrol *kcontrol,
                                   struct snd_ctl_elem_value *ucontrol)
@@ -9509,14 +9871,17 @@ static int alc262_hp_master_sw_put(struct snd_kcontrol *kcontrol,
        return 1;
 }
 
+#define ALC262_HP_MASTER_SWITCH                                        \
+       {                                                       \
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,            \
+               .name = "Master Playback Switch",               \
+               .info = snd_ctl_boolean_mono_info,              \
+               .get = alc262_hp_master_sw_get,                 \
+               .put = alc262_hp_master_sw_put,                 \
+       }
+
 static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Master Playback Switch",
-               .info = snd_ctl_boolean_mono_info,
-               .get = alc262_hp_master_sw_get,
-               .put = alc262_hp_master_sw_put,
-       },
+       ALC262_HP_MASTER_SWITCH,
        HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
@@ -9540,13 +9905,7 @@ static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
 };
 
 static struct snd_kcontrol_new alc262_HP_BPC_WildWest_mixer[] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Master Playback Switch",
-               .info = snd_ctl_boolean_mono_info,
-               .get = alc262_hp_master_sw_get,
-               .put = alc262_hp_master_sw_put,
-       },
+       ALC262_HP_MASTER_SWITCH,
        HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
@@ -9573,32 +9932,13 @@ static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = {
 };
 
 /* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_hp_t5735_automute(struct hda_codec *codec, int force)
+static void alc262_hp_t5735_init_hook(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
-       if (force || !spec->sense_updated) {
-               unsigned int present;
-               present = snd_hda_codec_read(codec, 0x15, 0,
-                                            AC_VERB_GET_PIN_SENSE, 0);
-               spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
-               spec->sense_updated = 1;
-       }
-       snd_hda_codec_amp_stereo(codec, 0x0c, HDA_OUTPUT, 0, HDA_AMP_MUTE,
-                                spec->jack_present ? HDA_AMP_MUTE : 0);
-}
-
-static void alc262_hp_t5735_unsol_event(struct hda_codec *codec,
-                                       unsigned int res)
-{
-       if ((res >> 26) != ALC880_HP_EVENT)
-               return;
-       alc262_hp_t5735_automute(codec, 1);
-}
-
-static void alc262_hp_t5735_init_hook(struct hda_codec *codec)
-{
-       alc262_hp_t5735_automute(codec, 1);
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x0c; /* HACK: not actually a pin */
+       alc_automute_amp(codec);
 }
 
 static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = {
@@ -9651,46 +9991,132 @@ static struct hda_input_mux alc262_hp_rp5700_capture_source = {
        },
 };
 
-/* bind hp and internal speaker mute (with plug check) */
-static int alc262_sony_master_sw_put(struct snd_kcontrol *kcontrol,
-                                    struct snd_ctl_elem_value *ucontrol)
+/* bind hp and internal speaker mute (with plug check) as master switch */
+static void alc262_hippo_master_update(struct hda_codec *codec)
 {
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       long *valp = ucontrol->value.integer.value;
-       int change;
+       struct alc_spec *spec = codec->spec;
+       hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
+       hda_nid_t line_nid = spec->autocfg.line_out_pins[0];
+       hda_nid_t speaker_nid = spec->autocfg.speaker_pins[0];
+       unsigned int mute;
 
-       /* change hp mute */
-       change = snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0,
-                                         HDA_AMP_MUTE,
-                                         valp[0] ? 0 : HDA_AMP_MUTE);
-       change |= snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0,
-                                          HDA_AMP_MUTE,
-                                          valp[1] ? 0 : HDA_AMP_MUTE);
-       if (change) {
-               /* change speaker according to HP jack state */
-               struct alc_spec *spec = codec->spec;
-               unsigned int mute;
-               if (spec->jack_present)
-                       mute = HDA_AMP_MUTE;
-               else
-                       mute = snd_hda_codec_amp_read(codec, 0x15, 0,
-                                                     HDA_OUTPUT, 0);
-               snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+       /* HP */
+       mute = spec->master_sw ? 0 : HDA_AMP_MUTE;
+       snd_hda_codec_amp_stereo(codec, hp_nid, HDA_OUTPUT, 0,
+                                HDA_AMP_MUTE, mute);
+       /* mute internal speaker per jack sense */
+       if (spec->jack_present)
+               mute = HDA_AMP_MUTE;
+       if (line_nid)
+               snd_hda_codec_amp_stereo(codec, line_nid, HDA_OUTPUT, 0,
+                                        HDA_AMP_MUTE, mute);
+       if (speaker_nid && speaker_nid != line_nid)
+               snd_hda_codec_amp_stereo(codec, speaker_nid, HDA_OUTPUT, 0,
                                         HDA_AMP_MUTE, mute);
+}
+
+#define alc262_hippo_master_sw_get     alc262_hp_master_sw_get
+
+static int alc262_hippo_master_sw_put(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       int val = !!*ucontrol->value.integer.value;
+
+       if (val == spec->master_sw)
+               return 0;
+       spec->master_sw = val;
+       alc262_hippo_master_update(codec);
+       return 1;
+}
+
+#define ALC262_HIPPO_MASTER_SWITCH                             \
+       {                                                       \
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,            \
+               .name = "Master Playback Switch",               \
+               .info = snd_ctl_boolean_mono_info,              \
+               .get = alc262_hippo_master_sw_get,              \
+               .put = alc262_hippo_master_sw_put,              \
        }
-       return change;
+
+static struct snd_kcontrol_new alc262_hippo_mixer[] = {
+       ALC262_HIPPO_MASTER_SWITCH,
+       HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 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("Mic Boost", 0x18, 0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+       { } /* end */
+};
+
+static struct snd_kcontrol_new alc262_hippo1_mixer[] = {
+       HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       ALC262_HIPPO_MASTER_SWITCH,
+       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("Mic Boost", 0x18, 0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+       { } /* end */
+};
+
+/* mute/unmute internal speaker according to the hp jack and mute state */
+static void alc262_hippo_automute(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
+       unsigned int present;
+
+       /* need to execute and sync at first */
+       snd_hda_codec_read(codec, hp_nid, 0, AC_VERB_SET_PIN_SENSE, 0);
+       present = snd_hda_codec_read(codec, hp_nid, 0,
+                                    AC_VERB_GET_PIN_SENSE, 0);
+       spec->jack_present = (present & 0x80000000) != 0;
+       alc262_hippo_master_update(codec);
+}
+
+static void alc262_hippo_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+       if ((res >> 26) != ALC880_HP_EVENT)
+               return;
+       alc262_hippo_automute(codec);
+}
+
+static void alc262_hippo_init_hook(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       alc262_hippo_automute(codec);
+}
+
+static void alc262_hippo1_init_hook(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       spec->autocfg.hp_pins[0] = 0x1b;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       alc262_hippo_automute(codec);
 }
 
+
 static struct snd_kcontrol_new alc262_sony_mixer[] = {
        HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Master Playback Switch",
-               .info = snd_hda_mixer_amp_switch_info,
-               .get = snd_hda_mixer_amp_switch_get,
-               .put = alc262_sony_master_sw_put,
-               .private_value = HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
-       },
+       ALC262_HIPPO_MASTER_SWITCH,
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
        HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
@@ -9699,8 +10125,8 @@ static struct snd_kcontrol_new alc262_sony_mixer[] = {
 };
 
 static struct snd_kcontrol_new alc262_benq_t31_mixer[] = {
-       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       ALC262_HIPPO_MASTER_SWITCH,
        HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
        HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
@@ -9741,34 +10167,15 @@ static struct hda_verb alc262_tyan_verbs[] = {
 };
 
 /* unsolicited event for HP jack sensing */
-static void alc262_tyan_automute(struct hda_codec *codec)
+static void alc262_tyan_init_hook(struct hda_codec *codec)
 {
-       unsigned int mute;
-       unsigned int present;
+       struct alc_spec *spec = codec->spec;
 
-       snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0);
-       present = snd_hda_codec_read(codec, 0x1b, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0);
-       present = (present & 0x80000000) != 0;
-       if (present) {
-               /* mute line output on ATX panel */
-               snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, HDA_AMP_MUTE);
-       } else {
-               /* unmute line output if necessary */
-               mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0);
-               snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, mute);
-       }
+       spec->autocfg.hp_pins[0] = 0x1b;
+       spec->autocfg.speaker_pins[0] = 0x15;
+       alc_automute_amp(codec);
 }
 
-static void alc262_tyan_unsol_event(struct hda_codec *codec,
-                                      unsigned int res)
-{
-       if ((res >> 26) != ALC880_HP_EVENT)
-               return;
-       alc262_tyan_automute(codec);
-}
 
 #define alc262_capture_mixer           alc882_capture_mixer
 #define alc262_capture_alt_mixer       alc882_capture_alt_mixer
@@ -9920,102 +10327,28 @@ static void alc262_dmic_automute(struct hda_codec *codec)
        present = snd_hda_codec_read(codec, 0x18, 0,
                                        AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
        snd_hda_codec_write(codec, 0x22, 0,
-                               AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x09);
-}
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc262_toshiba_s06_speaker_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_codec_read(codec, 0x15, 0,
-                                       AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       bits = present ? 0 : PIN_OUT;
-       snd_hda_codec_write(codec, 0x14, 0,
-                                       AC_VERB_SET_PIN_WIDGET_CONTROL, bits);
+                               AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x09);
 }
 
 
-
 /* unsolicited event for HP jack sensing */
 static void alc262_toshiba_s06_unsol_event(struct hda_codec *codec,
                                       unsigned int res)
 {
-       if ((res >> 26) == ALC880_HP_EVENT)
-               alc262_toshiba_s06_speaker_automute(codec);
        if ((res >> 26) == ALC880_MIC_EVENT)
                alc262_dmic_automute(codec);
-
+       else
+               alc_sku_unsol_event(codec, res);
 }
 
 static void alc262_toshiba_s06_init_hook(struct hda_codec *codec)
-{
-       alc262_toshiba_s06_speaker_automute(codec);
-       alc262_dmic_automute(codec);
-}
-
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_hippo_automute(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       unsigned int mute;
-       unsigned int present;
-
-       /* need to execute and sync at first */
-       snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0);
-       present = snd_hda_codec_read(codec, 0x15, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0);
-       spec->jack_present = (present & 0x80000000) != 0;
-       if (spec->jack_present) {
-               /* mute internal speaker */
-               snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, HDA_AMP_MUTE);
-       } else {
-               /* unmute internal speaker if necessary */
-               mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0);
-               snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, mute);
-       }
-}
-
-/* unsolicited event for HP jack sensing */
-static void alc262_hippo_unsol_event(struct hda_codec *codec,
-                                      unsigned int res)
-{
-       if ((res >> 26) != ALC880_HP_EVENT)
-               return;
-       alc262_hippo_automute(codec);
-}
-
-static void alc262_hippo1_automute(struct hda_codec *codec)
-{
-       unsigned int mute;
-       unsigned int present;
-
-       snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0);
-       present = snd_hda_codec_read(codec, 0x1b, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0);
-       present = (present & 0x80000000) != 0;
-       if (present) {
-               /* mute internal speaker */
-               snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, HDA_AMP_MUTE);
-       } else {
-               /* unmute internal speaker if necessary */
-               mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0);
-               snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, mute);
-       }
-}
 
-/* unsolicited event for HP jack sensing */
-static void alc262_hippo1_unsol_event(struct hda_codec *codec,
-                                      unsigned int res)
-{
-       if ((res >> 26) != ALC880_HP_EVENT)
-               return;
-       alc262_hippo1_automute(codec);
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       alc_automute_pin(codec);
+       alc262_dmic_automute(codec);
 }
 
 /*
@@ -10285,14 +10618,7 @@ static struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = {
 
 static struct snd_kcontrol_new alc262_toshiba_rx1_mixer[] = {
        HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Master Playback Switch",
-               .info = snd_hda_mixer_amp_switch_info,
-               .get = snd_hda_mixer_amp_switch_get,
-               .put = alc262_sony_master_sw_put,
-               .private_value = HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
-       },
+       ALC262_HIPPO_MASTER_SWITCH,
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
        HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
@@ -10639,31 +10965,46 @@ static struct hda_verb alc262_HP_BPC_init_verbs[] = {
        {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
        {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
 
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 },
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
        {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
         {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
-       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 },
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
        {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
        {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
 
 
        /* 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 */
+       /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 0b, 12 */
+       /* Input mixer1: only unmute Mic */
        {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))},
        /* Input mixer2 */
        {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))},
        /* Input mixer3 */
        {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))},
 
        {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
 
@@ -10843,6 +11184,8 @@ static int alc262_parse_auto_config(struct hda_codec *codec)
        if (err < 0)
                return err;
 
+       alc_ssid_check(codec, 0x15, 0x14, 0x1b);
+
        return 1;
 }
 
@@ -10945,7 +11288,7 @@ static struct alc_config_preset alc262_presets[] = {
                .input_mux = &alc262_capture_source,
        },
        [ALC262_HIPPO] = {
-               .mixers = { alc262_base_mixer },
+               .mixers = { alc262_hippo_mixer },
                .init_verbs = { alc262_init_verbs, alc262_hippo_unsol_verbs},
                .num_dacs = ARRAY_SIZE(alc262_dac_nids),
                .dac_nids = alc262_dac_nids,
@@ -10955,7 +11298,7 @@ static struct alc_config_preset alc262_presets[] = {
                .channel_mode = alc262_modes,
                .input_mux = &alc262_capture_source,
                .unsol_event = alc262_hippo_unsol_event,
-               .init_hook = alc262_hippo_automute,
+               .init_hook = alc262_hippo_init_hook,
        },
        [ALC262_HIPPO_1] = {
                .mixers = { alc262_hippo1_mixer },
@@ -10967,8 +11310,8 @@ static struct alc_config_preset alc262_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc262_modes),
                .channel_mode = alc262_modes,
                .input_mux = &alc262_capture_source,
-               .unsol_event = alc262_hippo1_unsol_event,
-               .init_hook = alc262_hippo1_automute,
+               .unsol_event = alc262_hippo_unsol_event,
+               .init_hook = alc262_hippo1_init_hook,
        },
        [ALC262_FUJITSU] = {
                .mixers = { alc262_fujitsu_mixer },
@@ -11030,7 +11373,7 @@ static struct alc_config_preset alc262_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc262_modes),
                .channel_mode = alc262_modes,
                .input_mux = &alc262_capture_source,
-               .unsol_event = alc262_hp_t5735_unsol_event,
+               .unsol_event = alc_automute_amp_unsol_event,
                .init_hook = alc262_hp_t5735_init_hook,
        },
        [ALC262_HP_RP5700] = {
@@ -11062,7 +11405,7 @@ static struct alc_config_preset alc262_presets[] = {
                .channel_mode = alc262_modes,
                .input_mux = &alc262_capture_source,
                .unsol_event = alc262_hippo_unsol_event,
-               .init_hook = alc262_hippo_automute,
+               .init_hook = alc262_hippo_init_hook,
        },
        [ALC262_BENQ_T31] = {
                .mixers = { alc262_benq_t31_mixer },
@@ -11074,7 +11417,7 @@ static struct alc_config_preset alc262_presets[] = {
                .channel_mode = alc262_modes,
                .input_mux = &alc262_capture_source,
                .unsol_event = alc262_hippo_unsol_event,
-               .init_hook = alc262_hippo_automute,
+               .init_hook = alc262_hippo_init_hook,
        },
        [ALC262_ULTRA] = {
                .mixers = { alc262_ultra_mixer },
@@ -11139,7 +11482,7 @@ static struct alc_config_preset alc262_presets[] = {
                .channel_mode = alc262_modes,
                .input_mux = &alc262_capture_source,
                .unsol_event = alc262_hippo_unsol_event,
-               .init_hook = alc262_hippo_automute,
+               .init_hook = alc262_hippo_init_hook,
        },
        [ALC262_TYAN] = {
                .mixers = { alc262_tyan_mixer },
@@ -11151,8 +11494,8 @@ static struct alc_config_preset alc262_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc262_modes),
                .channel_mode = alc262_modes,
                .input_mux = &alc262_capture_source,
-               .unsol_event = alc262_tyan_unsol_event,
-               .init_hook = alc262_tyan_automute,
+               .unsol_event = alc_automute_amp_unsol_event,
+               .init_hook = alc262_tyan_init_hook,
        },
 };
 
@@ -11187,8 +11530,8 @@ static int patch_alc262(struct hda_codec *codec)
                                                  alc262_cfg_tbl);
 
        if (board_config < 0) {
-               printk(KERN_INFO "hda_codec: Unknown model for ALC262, "
-                      "trying auto-probe from BIOS...\n");
+               printk(KERN_INFO "hda_codec: Unknown model for %s, "
+                      "trying auto-probe from BIOS...\n", codec->chip_name);
                board_config = ALC262_AUTO;
        }
 
@@ -11217,11 +11560,9 @@ static int patch_alc262(struct hda_codec *codec)
        if (board_config != ALC262_AUTO)
                setup_preset(spec, &alc262_presets[board_config]);
 
-       spec->stream_name_analog = "ALC262 Analog";
        spec->stream_analog_playback = &alc262_pcm_analog_playback;
        spec->stream_analog_capture = &alc262_pcm_analog_capture;
 
-       spec->stream_name_digital = "ALC262 Digital";
        spec->stream_digital_playback = &alc262_pcm_digital_playback;
        spec->stream_digital_capture = &alc262_pcm_digital_capture;
 
@@ -11296,6 +11637,17 @@ static struct snd_kcontrol_new alc268_base_mixer[] = {
        { }
 };
 
+static struct snd_kcontrol_new alc268_toshiba_mixer[] = {
+       /* output mixer control */
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT),
+       ALC262_HIPPO_MASTER_SWITCH,
+       HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0, HDA_INPUT),
+       { }
+};
+
 /* bind Beep switches of both NID 0x0f and 0x10 */
 static struct hda_bind_ctls alc268_bind_beep_sw = {
        .ops = &snd_hda_bind_sw,
@@ -11319,8 +11671,6 @@ static struct hda_verb alc268_eapd_verbs[] = {
 };
 
 /* Toshiba specific */
-#define alc268_toshiba_automute        alc262_hippo_automute
-
 static struct hda_verb alc268_toshiba_verbs[] = {
        {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
        { } /* end */
@@ -11456,13 +11806,8 @@ static struct hda_verb alc268_acer_verbs[] = {
 };
 
 /* unsolicited event for HP jack sensing */
-static void alc268_toshiba_unsol_event(struct hda_codec *codec,
-                                      unsigned int res)
-{
-       if ((res >> 26) != ALC880_HP_EVENT)
-               return;
-       alc268_toshiba_automute(codec);
-}
+#define alc268_toshiba_unsol_event     alc262_hippo_unsol_event
+#define alc268_toshiba_init_hook       alc262_hippo_init_hook
 
 static void alc268_acer_unsol_event(struct hda_codec *codec,
                                       unsigned int res)
@@ -11537,30 +11882,15 @@ static struct hda_verb alc268_dell_verbs[] = {
 };
 
 /* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc268_dell_automute(struct hda_codec *codec)
+static void alc268_dell_init_hook(struct hda_codec *codec)
 {
-       unsigned int present;
-       unsigned int mute;
-
-       present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0);
-       if (present & 0x80000000)
-               mute = HDA_AMP_MUTE;
-       else
-               mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0);
-       snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, mute);
-}
+       struct alc_spec *spec = codec->spec;
 
-static void alc268_dell_unsol_event(struct hda_codec *codec,
-                                   unsigned int res)
-{
-       if ((res >> 26) != ALC880_HP_EVENT)
-               return;
-       alc268_dell_automute(codec);
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       alc_automute_pin(codec);
 }
 
-#define alc268_dell_init_hook  alc268_dell_automute
-
 static struct snd_kcontrol_new alc267_quanta_il1_mixer[] = {
        HDA_CODEC_VOLUME("Speaker Playback Volume", 0x2, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
@@ -11579,16 +11909,6 @@ static struct hda_verb alc267_quanta_il1_verbs[] = {
        { }
 };
 
-static void alc267_quanta_il1_hp_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-
-       present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0)
-               & AC_PINSENSE_PRESENCE;
-       snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           present ? 0 : PIN_OUT);
-}
-
 static void alc267_quanta_il1_mic_automute(struct hda_codec *codec)
 {
        unsigned int present;
@@ -11600,9 +11920,13 @@ static void alc267_quanta_il1_mic_automute(struct hda_codec *codec)
                            present ? 0x00 : 0x01);
 }
 
-static void alc267_quanta_il1_automute(struct hda_codec *codec)
+static void alc267_quanta_il1_init_hook(struct hda_codec *codec)
 {
-       alc267_quanta_il1_hp_automute(codec);
+       struct alc_spec *spec = codec->spec;
+
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       alc_automute_pin(codec);
        alc267_quanta_il1_mic_automute(codec);
 }
 
@@ -11610,12 +11934,12 @@ static void alc267_quanta_il1_unsol_event(struct hda_codec *codec,
                                           unsigned int res)
 {
        switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               alc267_quanta_il1_hp_automute(codec);
-               break;
        case ALC880_MIC_EVENT:
                alc267_quanta_il1_mic_automute(codec);
                break;
+       default:
+               alc_sku_unsol_event(codec, res);
+               break;
        }
 }
 
@@ -12063,16 +12387,16 @@ static struct snd_pci_quirk alc268_cfg_tbl[] = {
                                                ALC268_ACER_ASPIRE_ONE),
        SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL),
        SND_PCI_QUIRK(0x1028, 0x02b0, "Dell Inspiron Mini9", ALC268_DELL),
-       SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA),
-       SND_PCI_QUIRK(0x103c, 0x30f1, "HP TX25xx series", ALC268_TOSHIBA),
+       SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP TX25xx series",
+                          ALC268_TOSHIBA),
        SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST),
-       SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA),
-       SND_PCI_QUIRK(0x1179, 0xff50, "TOSHIBA A305", ALC268_TOSHIBA),
-       SND_PCI_QUIRK(0x1179, 0xff64, "TOSHIBA L305", ALC268_TOSHIBA),
+       SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO),
+       SND_PCI_QUIRK_MASK(0x1179, 0xff00, 0xff00, "TOSHIBA A/Lx05",
+                          ALC268_TOSHIBA),
        SND_PCI_QUIRK(0x14c0, 0x0025, "COMPAL IFL90/JFL-92", ALC268_TOSHIBA),
        SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER),
        SND_PCI_QUIRK(0x152d, 0x0771, "Quanta IL1", ALC267_QUANTA_IL1),
-       SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO),
+       SND_PCI_QUIRK(0x1854, 0x1775, "LG R510", ALC268_DELL),
        {}
 };
 
@@ -12090,7 +12414,7 @@ static struct alc_config_preset alc268_presets[] = {
                .channel_mode = alc268_modes,
                .input_mux = &alc268_capture_source,
                .unsol_event = alc267_quanta_il1_unsol_event,
-               .init_hook = alc267_quanta_il1_automute,
+               .init_hook = alc267_quanta_il1_init_hook,
        },
        [ALC268_3ST] = {
                .mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
@@ -12108,7 +12432,7 @@ static struct alc_config_preset alc268_presets[] = {
                .input_mux = &alc268_capture_source,
        },
        [ALC268_TOSHIBA] = {
-               .mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
+               .mixers = { alc268_toshiba_mixer, alc268_capture_alt_mixer,
                            alc268_beep_mixer },
                .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
                                alc268_toshiba_verbs },
@@ -12122,7 +12446,7 @@ static struct alc_config_preset alc268_presets[] = {
                .channel_mode = alc268_modes,
                .input_mux = &alc268_capture_source,
                .unsol_event = alc268_toshiba_unsol_event,
-               .init_hook = alc268_toshiba_automute,
+               .init_hook = alc268_toshiba_init_hook,
        },
        [ALC268_ACER] = {
                .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer,
@@ -12185,7 +12509,7 @@ static struct alc_config_preset alc268_presets[] = {
                .hp_nid = 0x02,
                .num_channel_mode = ARRAY_SIZE(alc268_modes),
                .channel_mode = alc268_modes,
-               .unsol_event = alc268_dell_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .init_hook = alc268_dell_init_hook,
                .input_mux = &alc268_capture_source,
        },
@@ -12205,7 +12529,7 @@ static struct alc_config_preset alc268_presets[] = {
                .channel_mode = alc268_modes,
                .input_mux = &alc268_capture_source,
                .unsol_event = alc268_toshiba_unsol_event,
-               .init_hook = alc268_toshiba_automute
+               .init_hook = alc268_toshiba_init_hook
        },
 #ifdef CONFIG_SND_DEBUG
        [ALC268_TEST] = {
@@ -12243,8 +12567,8 @@ static int patch_alc268(struct hda_codec *codec)
                                                  alc268_cfg_tbl);
 
        if (board_config < 0 || board_config >= ALC268_MODEL_LAST) {
-               printk(KERN_INFO "hda_codec: Unknown model for ALC268, "
-                      "trying auto-probe from BIOS...\n");
+               printk(KERN_INFO "hda_codec: Unknown model for %s, "
+                      "trying auto-probe from BIOS...\n", codec->chip_name);
                board_config = ALC268_AUTO;
        }
 
@@ -12265,14 +12589,6 @@ static int patch_alc268(struct hda_codec *codec)
        if (board_config != ALC268_AUTO)
                setup_preset(spec, &alc268_presets[board_config]);
 
-       if (codec->vendor_id == 0x10ec0267) {
-               spec->stream_name_analog = "ALC267 Analog";
-               spec->stream_name_digital = "ALC267 Digital";
-       } else {
-               spec->stream_name_analog = "ALC268 Analog";
-               spec->stream_name_digital = "ALC268 Digital";
-       }
-
        spec->stream_analog_playback = &alc268_pcm_analog_playback;
        spec->stream_analog_capture = &alc268_pcm_analog_capture;
        spec->stream_analog_alt_capture = &alc268_pcm_analog_alt_capture;
@@ -13099,8 +13415,8 @@ static int patch_alc269(struct hda_codec *codec)
                                                  alc269_cfg_tbl);
 
        if (board_config < 0) {
-               printk(KERN_INFO "hda_codec: Unknown model for ALC269, "
-                      "trying auto-probe from BIOS...\n");
+               printk(KERN_INFO "hda_codec: Unknown model for %s, "
+                      "trying auto-probe from BIOS...\n", codec->chip_name);
                board_config = ALC269_AUTO;
        }
 
@@ -13127,7 +13443,6 @@ static int patch_alc269(struct hda_codec *codec)
        if (board_config != ALC269_AUTO)
                setup_preset(spec, &alc269_presets[board_config]);
 
-       spec->stream_name_analog = "ALC269 Analog";
        if (codec->subsystem_id == 0x17aa3bf8) {
                /* Due to a hardware problem on Lenovo Ideadpad, we need to
                 * fix the sample rate of analog I/O to 44.1kHz
@@ -13138,7 +13453,6 @@ static int patch_alc269(struct hda_codec *codec)
                spec->stream_analog_playback = &alc269_pcm_analog_playback;
                spec->stream_analog_capture = &alc269_pcm_analog_capture;
        }
-       spec->stream_name_digital = "ALC269 Digital";
        spec->stream_digital_playback = &alc269_pcm_digital_playback;
        spec->stream_digital_capture = &alc269_pcm_digital_capture;
 
@@ -13927,7 +14241,6 @@ static void alc861_auto_init_multi_out(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        int i;
 
-       alc_subsystem_id(codec, 0x0e, 0x0f, 0x0b);
        for (i = 0; i < spec->autocfg.line_outs; i++) {
                hda_nid_t nid = spec->autocfg.line_out_pins[i];
                int pin_type = get_pin_type(spec->autocfg.line_out_type);
@@ -14010,6 +14323,8 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
        spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids);
        set_capture_mixer(spec);
 
+       alc_ssid_check(codec, 0x0e, 0x0f, 0x0b);
+
        return 1;
 }
 
@@ -14199,8 +14514,8 @@ static int patch_alc861(struct hda_codec *codec)
                                                  alc861_cfg_tbl);
 
        if (board_config < 0) {
-               printk(KERN_INFO "hda_codec: Unknown model for ALC861, "
-                      "trying auto-probe from BIOS...\n");
+               printk(KERN_INFO "hda_codec: Unknown model for %s, "
+                      "trying auto-probe from BIOS...\n", codec->chip_name);
                board_config = ALC861_AUTO;
        }
 
@@ -14227,11 +14542,9 @@ static int patch_alc861(struct hda_codec *codec)
        if (board_config != ALC861_AUTO)
                setup_preset(spec, &alc861_presets[board_config]);
 
-       spec->stream_name_analog = "ALC861 Analog";
        spec->stream_analog_playback = &alc861_pcm_analog_playback;
        spec->stream_analog_capture = &alc861_pcm_analog_capture;
 
-       spec->stream_name_digital = "ALC861 Digital";
        spec->stream_digital_playback = &alc861_pcm_digital_playback;
        spec->stream_digital_capture = &alc861_pcm_digital_capture;
 
@@ -14618,19 +14931,6 @@ static struct hda_verb alc861vd_lenovo_unsol_verbs[] = {
        {}
 };
 
-/* toggle speaker-output according to the hp-jack state */
-static void alc861vd_lenovo_hp_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_codec_read(codec, 0x1b, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       bits = present ? HDA_AMP_MUTE : 0;
-       snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-}
-
 static void alc861vd_lenovo_mic_automute(struct hda_codec *codec)
 {
        unsigned int present;
@@ -14643,9 +14943,13 @@ static void alc861vd_lenovo_mic_automute(struct hda_codec *codec)
                                 HDA_AMP_MUTE, bits);
 }
 
-static void alc861vd_lenovo_automute(struct hda_codec *codec)
+static void alc861vd_lenovo_init_hook(struct hda_codec *codec)
 {
-       alc861vd_lenovo_hp_automute(codec);
+       struct alc_spec *spec = codec->spec;
+
+       spec->autocfg.hp_pins[0] = 0x1b;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       alc_automute_amp(codec);
        alc861vd_lenovo_mic_automute(codec);
 }
 
@@ -14653,12 +14957,12 @@ static void alc861vd_lenovo_unsol_event(struct hda_codec *codec,
                                        unsigned int res)
 {
        switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               alc861vd_lenovo_hp_automute(codec);
-               break;
        case ALC880_MIC_EVENT:
                alc861vd_lenovo_mic_automute(codec);
                break;
+       default:
+               alc_automute_amp_unsol_event(codec, res);
+               break;
        }
 }
 
@@ -14708,20 +15012,13 @@ static struct hda_verb alc861vd_dallas_verbs[] = {
 };
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc861vd_dallas_automute(struct hda_codec *codec)
+static void alc861vd_dallas_init_hook(struct hda_codec *codec)
 {
-       unsigned int present;
-
-       present = snd_hda_codec_read(codec, 0x15, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
+       struct alc_spec *spec = codec->spec;
 
-static void alc861vd_dallas_unsol_event(struct hda_codec *codec, unsigned int res)
-{
-       if ((res >> 26) == ALC880_HP_EVENT)
-               alc861vd_dallas_automute(codec);
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       alc_automute_amp(codec);
 }
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -14835,7 +15132,7 @@ static struct alc_config_preset alc861vd_presets[] = {
                .channel_mode = alc861vd_3stack_2ch_modes,
                .input_mux = &alc861vd_capture_source,
                .unsol_event = alc861vd_lenovo_unsol_event,
-               .init_hook = alc861vd_lenovo_automute,
+               .init_hook = alc861vd_lenovo_init_hook,
        },
        [ALC861VD_DALLAS] = {
                .mixers = { alc861vd_dallas_mixer },
@@ -14845,8 +15142,8 @@ static struct alc_config_preset alc861vd_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
                .channel_mode = alc861vd_3stack_2ch_modes,
                .input_mux = &alc861vd_dallas_capture_source,
-               .unsol_event = alc861vd_dallas_unsol_event,
-               .init_hook = alc861vd_dallas_automute,
+               .unsol_event = alc_automute_amp_unsol_event,
+               .init_hook = alc861vd_dallas_init_hook,
        },
        [ALC861VD_HP] = {
                .mixers = { alc861vd_hp_mixer },
@@ -14857,8 +15154,8 @@ static struct alc_config_preset alc861vd_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
                .channel_mode = alc861vd_3stack_2ch_modes,
                .input_mux = &alc861vd_hp_capture_source,
-               .unsol_event = alc861vd_dallas_unsol_event,
-               .init_hook = alc861vd_dallas_automute,
+               .unsol_event = alc_automute_amp_unsol_event,
+               .init_hook = alc861vd_dallas_init_hook,
        },
        [ALC660VD_ASUS_V1S] = {
                .mixers = { alc861vd_lenovo_mixer },
@@ -14873,7 +15170,7 @@ static struct alc_config_preset alc861vd_presets[] = {
                .channel_mode = alc861vd_3stack_2ch_modes,
                .input_mux = &alc861vd_capture_source,
                .unsol_event = alc861vd_lenovo_unsol_event,
-               .init_hook = alc861vd_lenovo_automute,
+               .init_hook = alc861vd_lenovo_init_hook,
        },
 };
 
@@ -14891,7 +15188,6 @@ static void alc861vd_auto_init_multi_out(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        int i;
 
-       alc_subsystem_id(codec, 0x15, 0x1b, 0x14);
        for (i = 0; i <= HDA_SIDE; i++) {
                hda_nid_t nid = spec->autocfg.line_out_pins[i];
                int pin_type = get_pin_type(spec->autocfg.line_out_type);
@@ -15109,6 +15405,8 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec)
        if (err < 0)
                return err;
 
+       alc_ssid_check(codec, 0x15, 0x1b, 0x14);
+
        return 1;
 }
 
@@ -15140,8 +15438,8 @@ static int patch_alc861vd(struct hda_codec *codec)
                                                  alc861vd_cfg_tbl);
 
        if (board_config < 0 || board_config >= ALC861VD_MODEL_LAST) {
-               printk(KERN_INFO "hda_codec: Unknown model for ALC660VD/"
-                       "ALC861VD, trying auto-probe from BIOS...\n");
+               printk(KERN_INFO "hda_codec: Unknown model for %s, "
+                      "trying auto-probe from BIOS...\n", codec->chip_name);
                board_config = ALC861VD_AUTO;
        }
 
@@ -15169,13 +15467,8 @@ static int patch_alc861vd(struct hda_codec *codec)
                setup_preset(spec, &alc861vd_presets[board_config]);
 
        if (codec->vendor_id == 0x10ec0660) {
-               spec->stream_name_analog = "ALC660-VD Analog";
-               spec->stream_name_digital = "ALC660-VD Digital";
                /* always turn on EAPD */
                add_verb(spec, alc660vd_eapd_verbs);
-       } else {
-               spec->stream_name_analog = "ALC861VD Analog";
-               spec->stream_name_digital = "ALC861VD Digital";
        }
 
        spec->stream_analog_playback = &alc861vd_pcm_analog_playback;
@@ -15289,6 +15582,38 @@ static struct hda_input_mux alc663_m51va_capture_source = {
        },
 };
 
+#if 1 /* set to 0 for testing other input sources below */
+static struct hda_input_mux alc272_nc10_capture_source = {
+       .num_items = 2,
+       .items = {
+               { "Autoselect Mic", 0x0 },
+               { "Internal Mic", 0x1 },
+       },
+};
+#else
+static struct hda_input_mux alc272_nc10_capture_source = {
+       .num_items = 16,
+       .items = {
+               { "Autoselect Mic", 0x0 },
+               { "Internal Mic", 0x1 },
+               { "In-0x02", 0x2 },
+               { "In-0x03", 0x3 },
+               { "In-0x04", 0x4 },
+               { "In-0x05", 0x5 },
+               { "In-0x06", 0x6 },
+               { "In-0x07", 0x7 },
+               { "In-0x08", 0x8 },
+               { "In-0x09", 0x9 },
+               { "In-0x0a", 0x0a },
+               { "In-0x0b", 0x0b },
+               { "In-0x0c", 0x0c },
+               { "In-0x0d", 0x0d },
+               { "In-0x0e", 0x0e },
+               { "In-0x0f", 0x0f },
+       },
+};
+#endif
+
 /*
  * 2ch mode
  */
@@ -15428,10 +15753,8 @@ static struct snd_kcontrol_new alc662_lenovo_101e_mixer[] = {
 };
 
 static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = {
-       HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
-
-       HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x02, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Line-Out Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+       ALC262_HIPPO_MASTER_SWITCH,
 
        HDA_CODEC_VOLUME("e-Mic Boost", 0x18, 0, HDA_INPUT),
        HDA_CODEC_VOLUME("e-Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
@@ -15444,15 +15767,11 @@ static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = {
 };
 
 static struct snd_kcontrol_new alc662_eeepc_ep20_mixer[] = {
-       HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x02, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Line-Out Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       ALC262_HIPPO_MASTER_SWITCH,
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
-       HDA_BIND_MUTE("Surround Playback Switch", 0x03, 2, HDA_INPUT),
        HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT),
-       HDA_BIND_MUTE_MONO("Center Playback Switch", 0x04, 1, 2, HDA_INPUT),
-       HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x04, 2, 2, HDA_INPUT),
-       HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
        HDA_BIND_MUTE("MuteCtrl Playback Switch", 0x0c, 2, HDA_INPUT),
        HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
        HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
@@ -15960,51 +16279,25 @@ static void alc662_eeepc_mic_automute(struct hda_codec *codec)
 static void alc662_eeepc_unsol_event(struct hda_codec *codec,
                                     unsigned int res)
 {
-       if ((res >> 26) == ALC880_HP_EVENT)
-               alc262_hippo1_automute( codec );
-
        if ((res >> 26) == ALC880_MIC_EVENT)
                alc662_eeepc_mic_automute(codec);
+       else
+               alc262_hippo_unsol_event(codec, res);
 }
 
 static void alc662_eeepc_inithook(struct hda_codec *codec)
 {
-       alc262_hippo1_automute( codec );
+       alc262_hippo1_init_hook(codec);
        alc662_eeepc_mic_automute(codec);
 }
 
-static void alc662_eeepc_ep20_automute(struct hda_codec *codec)
-{
-       unsigned int mute;
-       unsigned int present;
-
-       snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0);
-       present = snd_hda_codec_read(codec, 0x14, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0);
-       present = (present & 0x80000000) != 0;
-       if (present) {
-               /* mute internal speaker */
-               snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
-                                       HDA_AMP_MUTE, HDA_AMP_MUTE);
-       } else {
-               /* unmute internal speaker if necessary */
-               mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0);
-               snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
-                                       HDA_AMP_MUTE, mute);
-       }
-}
-
-/* unsolicited event for HP jack sensing */
-static void alc662_eeepc_ep20_unsol_event(struct hda_codec *codec,
-                                         unsigned int res)
-{
-       if ((res >> 26) == ALC880_HP_EVENT)
-               alc662_eeepc_ep20_automute(codec);
-}
-
 static void alc662_eeepc_ep20_inithook(struct hda_codec *codec)
 {
-       alc662_eeepc_ep20_automute(codec);
+       struct alc_spec *spec = codec->spec;
+
+       spec->autocfg.hp_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[0] = 0x1b;
+       alc262_hippo_master_update(codec);
 }
 
 static void alc663_m51va_speaker_automute(struct hda_codec *codec)
@@ -16338,35 +16631,9 @@ static void alc663_g50v_inithook(struct hda_codec *codec)
        alc662_eeepc_mic_automute(codec);
 }
 
-/* bind hp and internal speaker mute (with plug check) */
-static int alc662_ecs_master_sw_put(struct snd_kcontrol *kcontrol,
-                                    struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       long *valp = ucontrol->value.integer.value;
-       int change;
-
-       change = snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0,
-                                         HDA_AMP_MUTE,
-                                         valp[0] ? 0 : HDA_AMP_MUTE);
-       change |= snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0,
-                                          HDA_AMP_MUTE,
-                                          valp[1] ? 0 : HDA_AMP_MUTE);
-       if (change)
-               alc262_hippo1_automute(codec);
-       return change;
-}
-
 static struct snd_kcontrol_new alc662_ecs_mixer[] = {
        HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Master Playback Switch",
-               .info = snd_hda_mixer_amp_switch_info,
-               .get = snd_hda_mixer_amp_switch_get,
-               .put = alc662_ecs_master_sw_put,
-               .private_value = HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
-       },
+       ALC262_HIPPO_MASTER_SWITCH,
 
        HDA_CODEC_VOLUME("e-Mic/LineIn Boost", 0x18, 0, HDA_INPUT),
        HDA_CODEC_VOLUME("e-Mic/LineIn Playback Volume", 0x0b, 0x0, HDA_INPUT),
@@ -16378,6 +16645,23 @@ static struct snd_kcontrol_new alc662_ecs_mixer[] = {
        { } /* end */
 };
 
+static struct snd_kcontrol_new alc272_nc10_mixer[] = {
+       /* Master Playback automatically created from Speaker and Headphone */
+       HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+
+       HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Ext Mic Boost", 0x18, 0, HDA_INPUT),
+
+       HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+       HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+       HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
+       { } /* end */
+};
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #define alc662_loopbacks       alc880_loopbacks
 #endif
@@ -16411,6 +16695,9 @@ static const char *alc662_models[ALC662_MODEL_LAST] = {
        [ALC663_ASUS_MODE4] = "asus-mode4",
        [ALC663_ASUS_MODE5] = "asus-mode5",
        [ALC663_ASUS_MODE6] = "asus-mode6",
+       [ALC272_DELL]           = "dell",
+       [ALC272_DELL_ZM1]       = "dell-zm1",
+       [ALC272_SAMSUNG_NC10]   = "samsung-nc10",
        [ALC662_AUTO]           = "auto",
 };
 
@@ -16468,6 +16755,7 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = {
        SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_ECS),
        SND_PCI_QUIRK(0x105b, 0x0d47, "Foxconn 45CMX/45GMX/45CMX-K",
                      ALC662_3ST_6ch_DIG),
+       SND_PCI_QUIRK(0x144d, 0xca00, "Samsung NC10", ALC272_SAMSUNG_NC10),
        SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L",
                      ALC662_3ST_6ch_DIG),
        SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG),
@@ -16558,7 +16846,7 @@ static struct alc_config_preset alc662_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
                .channel_mode = alc662_3ST_6ch_modes,
                .input_mux = &alc662_lenovo_101e_capture_source,
-               .unsol_event = alc662_eeepc_ep20_unsol_event,
+               .unsol_event = alc662_eeepc_unsol_event,
                .init_hook = alc662_eeepc_ep20_inithook,
        },
        [ALC662_ECS] = {
@@ -16739,6 +17027,18 @@ static struct alc_config_preset alc662_presets[] = {
                .unsol_event = alc663_m51va_unsol_event,
                .init_hook = alc663_m51va_inithook,
        },
+       [ALC272_SAMSUNG_NC10] = {
+               .mixers = { alc272_nc10_mixer },
+               .init_verbs = { alc662_init_verbs,
+                               alc663_21jd_amic_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc272_dac_nids),
+               .dac_nids = alc272_dac_nids,
+               .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+               .channel_mode = alc662_3ST_2ch_modes,
+               .input_mux = &alc272_nc10_capture_source,
+               .unsol_event = alc663_mode4_unsol_event,
+               .init_hook = alc663_mode4_inithook,
+       },
 };
 
 
@@ -16933,7 +17233,6 @@ static void alc662_auto_init_multi_out(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        int i;
 
-       alc_subsystem_id(codec, 0x15, 0x1b, 0x14);
        for (i = 0; i <= HDA_SIDE; i++) {
                hda_nid_t nid = spec->autocfg.line_out_pins[i];
                int pin_type = get_pin_type(spec->autocfg.line_out_type);
@@ -17030,6 +17329,8 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
        if (err < 0)
                return err;
 
+       alc_ssid_check(codec, 0x15, 0x1b, 0x14);
+
        return 1;
 }
 
@@ -17062,8 +17363,8 @@ static int patch_alc662(struct hda_codec *codec)
                                                  alc662_models,
                                                  alc662_cfg_tbl);
        if (board_config < 0) {
-               printk(KERN_INFO "hda_codec: Unknown model for ALC662, "
-                      "trying auto-probe from BIOS...\n");
+               printk(KERN_INFO "hda_codec: Unknown model for %s, "
+                      "trying auto-probe from BIOS...\n", codec->chip_name);
                board_config = ALC662_AUTO;
        }
 
@@ -17090,17 +17391,6 @@ static int patch_alc662(struct hda_codec *codec)
        if (board_config != ALC662_AUTO)
                setup_preset(spec, &alc662_presets[board_config]);
 
-       if (codec->vendor_id == 0x10ec0663) {
-               spec->stream_name_analog = "ALC663 Analog";
-               spec->stream_name_digital = "ALC663 Digital";
-       } else if (codec->vendor_id == 0x10ec0272) {
-               spec->stream_name_analog = "ALC272 Analog";
-               spec->stream_name_digital = "ALC272 Digital";
-       } else {
-               spec->stream_name_analog = "ALC662 Analog";
-               spec->stream_name_digital = "ALC662 Digital";
-       }
-
        spec->stream_analog_playback = &alc662_pcm_analog_playback;
        spec->stream_analog_capture = &alc662_pcm_analog_capture;
 
index d2fd8ef6aef846b9c3bf13093348c7d11733b1e3..93e47c96a38bce1800a9df2dbfff5aaa85fd7b47 100644 (file)
@@ -100,6 +100,7 @@ enum {
        STAC_HP_M4,
        STAC_HP_DV5,
        STAC_HP_HDX,
+       STAC_HP_DV4_1222NR,
        STAC_92HD71BXX_MODELS
 };
 
@@ -193,6 +194,7 @@ struct sigmatel_spec {
        unsigned int gpio_dir;
        unsigned int gpio_data;
        unsigned int gpio_mute;
+       unsigned int gpio_led;
 
        /* stream */
        unsigned int stream_delay;
@@ -634,6 +636,40 @@ static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
+static unsigned int stac92xx_vref_set(struct hda_codec *codec,
+                                       hda_nid_t nid, unsigned int new_vref)
+{
+       unsigned int error;
+       unsigned int pincfg;
+       pincfg = snd_hda_codec_read(codec, nid, 0,
+                               AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+
+       pincfg &= 0xff;
+       pincfg &= ~(AC_PINCTL_VREFEN | AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
+       pincfg |= new_vref;
+
+       if (new_vref == AC_PINCTL_VREF_HIZ)
+               pincfg |= AC_PINCTL_OUT_EN;
+       else
+               pincfg |= AC_PINCTL_IN_EN;
+
+       error = snd_hda_codec_write_cache(codec, nid, 0,
+                                       AC_VERB_SET_PIN_WIDGET_CONTROL, pincfg);
+       if (error < 0)
+               return error;
+       else
+               return 1;
+}
+
+static unsigned int stac92xx_vref_get(struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int vref;
+       vref = snd_hda_codec_read(codec, nid, 0,
+                               AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+       vref &= AC_PINCTL_VREFEN;
+       return vref;
+}
+
 static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
@@ -995,6 +1031,17 @@ static struct hda_verb stac9205_core_init[] = {
                .private_value = verb_read | (verb_write << 16), \
        }
 
+#define DC_BIAS(xname, idx, nid) \
+       { \
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+               .name = xname, \
+               .index = idx, \
+               .info = stac92xx_dc_bias_info, \
+               .get = stac92xx_dc_bias_get, \
+               .put = stac92xx_dc_bias_put, \
+               .private_value = nid, \
+       }
+
 static struct snd_kcontrol_new stac9200_mixer[] = {
        HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
@@ -1543,6 +1590,8 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = {
        /* SigmaTel reference board */
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
                      "DFI LanParty", STAC_REF),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xfb30,
+                     "SigmaTel",STAC_9205_REF),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
                      "DFI LanParty", STAC_REF),
        /* Dell laptops have BIOS problem */
@@ -1837,6 +1886,7 @@ static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = {
        [STAC_HP_M4]            = NULL,
        [STAC_HP_DV5]           = NULL,
        [STAC_HP_HDX]           = NULL,
+       [STAC_HP_DV4_1222NR]    = NULL,
 };
 
 static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
@@ -1848,6 +1898,7 @@ static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
        [STAC_HP_M4] = "hp-m4",
        [STAC_HP_DV5] = "hp-dv5",
        [STAC_HP_HDX] = "hp-hdx",
+       [STAC_HP_DV4_1222NR] = "hp-dv4-1222nr",
 };
 
 static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
@@ -1856,6 +1907,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
                      "DFI LanParty", STAC_92HD71BXX_REF),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
                      "DFI LanParty", STAC_92HD71BXX_REF),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fb,
+                     "HP dv4-1222nr", STAC_HP_DV4_1222NR),
        SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080,
                      "HP", STAC_HP_DV5),
        SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0,
@@ -2545,7 +2598,8 @@ static int stac92xx_build_pcms(struct hda_codec *codec)
        return 0;
 }
 
-static unsigned int stac92xx_get_vref(struct hda_codec *codec, hda_nid_t nid)
+static unsigned int stac92xx_get_default_vref(struct hda_codec *codec,
+                                       hda_nid_t nid)
 {
        unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
        pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
@@ -2599,15 +2653,108 @@ static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
        return 1;
 }
 
-#define stac92xx_io_switch_info                snd_ctl_boolean_mono_info
+static int stac92xx_dc_bias_info(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_info *uinfo)
+{
+       int i;
+       static char *texts[] = {
+               "Mic In", "Line In", "Line Out"
+       };
+
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct sigmatel_spec *spec = codec->spec;
+       hda_nid_t nid = kcontrol->private_value;
+
+       if (nid == spec->mic_switch || nid == spec->line_switch)
+               i = 3;
+       else
+               i = 2;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->value.enumerated.items = i;
+       uinfo->count = 1;
+       if (uinfo->value.enumerated.item >= i)
+               uinfo->value.enumerated.item = i-1;
+       strcpy(uinfo->value.enumerated.name,
+               texts[uinfo->value.enumerated.item]);
+
+       return 0;
+}
+
+static int stac92xx_dc_bias_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = kcontrol->private_value;
+       unsigned int vref = stac92xx_vref_get(codec, nid);
+
+       if (vref == stac92xx_get_default_vref(codec, nid))
+               ucontrol->value.enumerated.item[0] = 0;
+       else if (vref == AC_PINCTL_VREF_GRD)
+               ucontrol->value.enumerated.item[0] = 1;
+       else if (vref == AC_PINCTL_VREF_HIZ)
+               ucontrol->value.enumerated.item[0] = 2;
+
+       return 0;
+}
+
+static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       unsigned int new_vref = 0;
+       unsigned int error;
+       hda_nid_t nid = kcontrol->private_value;
+
+       if (ucontrol->value.enumerated.item[0] == 0)
+               new_vref = stac92xx_get_default_vref(codec, nid);
+       else if (ucontrol->value.enumerated.item[0] == 1)
+               new_vref = AC_PINCTL_VREF_GRD;
+       else if (ucontrol->value.enumerated.item[0] == 2)
+               new_vref = AC_PINCTL_VREF_HIZ;
+       else
+               return 0;
+
+       if (new_vref != stac92xx_vref_get(codec, nid)) {
+               error = stac92xx_vref_set(codec, nid, new_vref);
+               return error;
+       }
+
+       return 0;
+}
+
+static int stac92xx_io_switch_info(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_info *uinfo)
+{
+       static char *texts[2];
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct sigmatel_spec *spec = codec->spec;
+
+       if (kcontrol->private_value == spec->line_switch)
+               texts[0] = "Line In";
+       else
+               texts[0] = "Mic In";
+       texts[1] = "Line Out";
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->value.enumerated.items = 2;
+       uinfo->count = 1;
+
+       if (uinfo->value.enumerated.item >= 2)
+               uinfo->value.enumerated.item = 1;
+       strcpy(uinfo->value.enumerated.name,
+               texts[uinfo->value.enumerated.item]);
+
+       return 0;
+}
 
 static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct sigmatel_spec *spec = codec->spec;
-       int io_idx = kcontrol-> private_value & 0xff;
+       hda_nid_t nid = kcontrol->private_value;
+       int io_idx = (nid == spec->mic_switch) ? 1 : 0;
 
-       ucontrol->value.integer.value[0] = spec->io_switch[io_idx];
+       ucontrol->value.enumerated.item[0] = spec->io_switch[io_idx];
        return 0;
 }
 
@@ -2615,9 +2762,9 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
 {
         struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct sigmatel_spec *spec = codec->spec;
-        hda_nid_t nid = kcontrol->private_value >> 8;
-       int io_idx = kcontrol-> private_value & 0xff;
-       unsigned short val = !!ucontrol->value.integer.value[0];
+       hda_nid_t nid = kcontrol->private_value;
+       int io_idx = (nid == spec->mic_switch) ? 1 : 0;
+       unsigned short val = !!ucontrol->value.enumerated.item[0];
 
        spec->io_switch[io_idx] = val;
 
@@ -2626,7 +2773,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
        else {
                unsigned int pinctl = AC_PINCTL_IN_EN;
                if (io_idx) /* set VREF for mic */
-                       pinctl |= stac92xx_get_vref(codec, nid);
+                       pinctl |= stac92xx_get_default_vref(codec, nid);
                stac92xx_auto_set_pinctl(codec, nid, pinctl);
        }
 
@@ -2707,7 +2854,8 @@ enum {
        STAC_CTL_WIDGET_AMP_VOL,
        STAC_CTL_WIDGET_HP_SWITCH,
        STAC_CTL_WIDGET_IO_SWITCH,
-       STAC_CTL_WIDGET_CLFE_SWITCH
+       STAC_CTL_WIDGET_CLFE_SWITCH,
+       STAC_CTL_WIDGET_DC_BIAS
 };
 
 static struct snd_kcontrol_new stac92xx_control_templates[] = {
@@ -2719,6 +2867,7 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = {
        STAC_CODEC_HP_SWITCH(NULL),
        STAC_CODEC_IO_SWITCH(NULL, 0),
        STAC_CODEC_CLFE_SWITCH(NULL, 0),
+       DC_BIAS(NULL, 0, 0),
 };
 
 /* add dynamic controls */
@@ -2782,6 +2931,34 @@ static struct snd_kcontrol_new stac_input_src_temp = {
        .put = stac92xx_mux_enum_put,
 };
 
+static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
+                                               hda_nid_t nid, int idx)
+{
+       int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+       int control = 0;
+       struct sigmatel_spec *spec = codec->spec;
+       char name[22];
+
+       if (!((get_defcfg_connect(def_conf)) & AC_JACK_PORT_FIXED)) {
+               if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
+                       && nid == spec->line_switch)
+                       control = STAC_CTL_WIDGET_IO_SWITCH;
+               else if (snd_hda_query_pin_caps(codec, nid)
+                       & (AC_PINCAP_VREF_GRD << AC_PINCAP_VREF_SHIFT))
+                       control = STAC_CTL_WIDGET_DC_BIAS;
+               else if (nid == spec->mic_switch)
+                       control = STAC_CTL_WIDGET_IO_SWITCH;
+       }
+
+       if (control) {
+               strcpy(name, auto_pin_cfg_labels[idx]);
+               return stac92xx_add_control(codec->spec, control,
+                                       strcat(name, " Jack Mode"), nid);
+       }
+
+       return 0;
+}
+
 static int stac92xx_add_input_source(struct sigmatel_spec *spec)
 {
        struct snd_kcontrol_new *knew;
@@ -3144,7 +3321,9 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
                                               const struct auto_pin_cfg *cfg)
 {
        struct sigmatel_spec *spec = codec->spec;
+       hda_nid_t nid;
        int err;
+       int idx;
 
        err = create_multi_out_ctls(codec, cfg->line_outs, cfg->line_out_pins,
                                    spec->multiout.dac_nids,
@@ -3161,20 +3340,13 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
                        return err;
        }
 
-       if (spec->line_switch) {
-               err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH,
-                                          "Line In as Output Switch",
-                                          spec->line_switch << 8);
-               if (err < 0)
-                       return err;
-       }
-
-       if (spec->mic_switch) {
-               err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH,
-                                          "Mic as Output Switch",
-                                          (spec->mic_switch << 8) | 1);
-               if (err < 0)
-                       return err;
+       for (idx = AUTO_PIN_MIC; idx <= AUTO_PIN_FRONT_LINE; idx++) {
+               nid = cfg->input_pins[idx];
+               if (nid) {
+                       err = stac92xx_add_jack_mode_control(codec, nid, idx);
+                       if (err < 0)
+                               return err;
+               }
        }
 
        return 0;
@@ -3639,6 +3811,8 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
                err = snd_hda_attach_beep_device(codec, nid);
                if (err < 0)
                        return err;
+               /* IDT/STAC codecs have linear beep tone parameter */
+               codec->beep->linear_tone = 1;
                /* if no beep switch is available, make its own one */
                caps = query_amp_caps(codec, nid, HDA_OUTPUT);
                if (codec->beep &&
@@ -4082,7 +4256,7 @@ static int stac92xx_init(struct hda_codec *codec)
                        unsigned int pinctl, conf;
                        if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) {
                                /* for mic pins, force to initialize */
-                               pinctl = stac92xx_get_vref(codec, nid);
+                               pinctl = stac92xx_get_default_vref(codec, nid);
                                pinctl |= AC_PINCTL_IN_EN;
                                stac92xx_auto_set_pinctl(codec, nid, pinctl);
                        } else {
@@ -4535,17 +4709,19 @@ static int stac92xx_resume(struct hda_codec *codec)
        return 0;
 }
 
-
 /*
- * using power check for controlling mute led of HP HDX notebooks
+ * using power check for controlling mute led of HP notebooks
  * check for mute state only on Speakers (nid = 0x10)
  *
  * For this feature CONFIG_SND_HDA_POWER_SAVE is needed, otherwise
  * the LED is NOT working properly !
+ *
+ * Changed name to reflect that it now works for any designated
+ * model, not just HP HDX.
  */
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-static int stac92xx_hp_hdx_check_power_status(struct hda_codec *codec,
+static int stac92xx_hp_check_power_status(struct hda_codec *codec,
                                              hda_nid_t nid)
 {
        struct sigmatel_spec *spec = codec->spec;
@@ -4553,9 +4729,9 @@ static int stac92xx_hp_hdx_check_power_status(struct hda_codec *codec,
        if (nid == 0x10) {
                if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
                    HDA_AMP_MUTE)
-                       spec->gpio_data &= ~0x08;  /* orange */
+                       spec->gpio_data &= ~spec->gpio_led; /* orange */
                else
-                       spec->gpio_data |= 0x08;   /* white */
+                       spec->gpio_data |= spec->gpio_led; /* white */
 
                stac_gpio_set(codec, spec->gpio_mask,
                              spec->gpio_dir,
@@ -5201,6 +5377,15 @@ again:
        if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
                snd_hda_sequence_write_cache(codec, unmute_init);
 
+       /* Some HP machines seem to have unstable codec communications
+        * especially with ATI fglrx driver.  For recovering from the
+        * CORB/RIRB stall, allow the BUS reset and keep always sync
+        */
+       if (spec->board_config == STAC_HP_DV5) {
+               codec->bus->sync_write = 1;
+               codec->bus->allow_bus_reset = 1;
+       }
+
        spec->aloopback_ctl = stac92hd71bxx_loopback;
        spec->aloopback_mask = 0x50;
        spec->aloopback_shift = 0;
@@ -5234,6 +5419,15 @@ again:
                spec->num_smuxes = 0;
                spec->num_dmuxes = 1;
                break;
+       case STAC_HP_DV4_1222NR:
+               spec->num_dmics = 1;
+               /* I don't know if it needs 1 or 2 smuxes - will wait for
+                * bug reports to fix if needed
+                */
+               spec->num_smuxes = 1;
+               spec->num_dmuxes = 1;
+               spec->gpio_led = 0x01;
+               /* fallthrough */
        case STAC_HP_DV5:
                snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010);
                stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN);
@@ -5242,22 +5436,21 @@ again:
                spec->num_dmics = 1;
                spec->num_dmuxes = 1;
                spec->num_smuxes = 1;
-               /*
-                * For controlling MUTE LED on HP HDX16/HDX18 notebooks,
-                * the CONFIG_SND_HDA_POWER_SAVE is needed to be set.
-                */
-#ifdef CONFIG_SND_HDA_POWER_SAVE
                /* orange/white mute led on GPIO3, orange=0, white=1 */
-               spec->gpio_mask |= 0x08;
-               spec->gpio_dir  |= 0x08;
-               spec->gpio_data |= 0x08;  /* set to white */
+               spec->gpio_led = 0x08;
+               break;
+       }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       if (spec->gpio_led) {
+               spec->gpio_mask |= spec->gpio_led;
+               spec->gpio_dir |= spec->gpio_led;
+               spec->gpio_data |= spec->gpio_led;
                /* register check_power_status callback. */
                codec->patch_ops.check_power_status =
-                   stac92xx_hp_hdx_check_power_status;
+                       stac92xx_hp_check_power_status;
+       }
 #endif 
-               break;
-       };
 
        spec->multiout.dac_nids = spec->dac_nids;
        if (spec->dinput_mux)
@@ -5282,7 +5475,7 @@ again:
        codec->proc_widget_hook = stac92hd7x_proc_hook;
 
        return 0;
-};
+}
 
 static int patch_stac922x(struct hda_codec *codec)
 {
@@ -5437,7 +5630,7 @@ static int patch_stac927x(struct hda_codec *codec)
                        /* correct the device field to SPDIF out */
                        snd_hda_codec_set_pincfg(codec, 0x21, 0x01442070);
                        break;
-               };
+               }
                /* configure the analog microphone on some laptops */
                snd_hda_codec_set_pincfg(codec, 0x0c, 0x90a79130);
                /* correct the front output jack as a hp out */
@@ -5747,6 +5940,7 @@ static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
        { .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 },
        { .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 },
        { .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 },
+       { .id = 0x83847698, .name = "STAC9205", .patch = patch_stac9205 },
        { .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 },
        { .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 },
        { .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 },
index b25a5cc637d6072cbbad235753d64ff6c12c3ff1..8e004fb6961add86847d11b2a128577488a7821b 100644 (file)
@@ -205,7 +205,7 @@ struct via_spec {
 
        /* playback */
        struct hda_multi_out multiout;
-       hda_nid_t extra_dig_out_nid;
+       hda_nid_t slave_dig_outs[2];
 
        /* capture */
        unsigned int num_adc_nids;
@@ -731,21 +731,6 @@ static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
        return snd_hda_multi_out_dig_close(codec, &spec->multiout);
 }
 
-/* setup SPDIF output stream */
-static void setup_dig_playback_stream(struct hda_codec *codec, hda_nid_t nid,
-                                unsigned int stream_tag, unsigned int format)
-{
-       /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
-       if (codec->spdif_ctls & AC_DIG1_ENABLE)
-               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
-                                   codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
-       snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
-       /* turn on again (if needed) */
-       if (codec->spdif_ctls & AC_DIG1_ENABLE)
-               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
-                                   codec->spdif_ctls & 0xff);
-}
-
 static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                                        struct hda_codec *codec,
                                        unsigned int stream_tag,
@@ -753,19 +738,16 @@ static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                                        struct snd_pcm_substream *substream)
 {
        struct via_spec *spec = codec->spec;
-       hda_nid_t nid;
-
-       /* 1st or 2nd S/PDIF */
-       if (substream->number == 0)
-               nid = spec->multiout.dig_out_nid;
-       else if (substream->number == 1)
-               nid = spec->extra_dig_out_nid;
-       else
-               return -1;
+       return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+                                            stream_tag, format, substream);
+}
 
-       mutex_lock(&codec->spdif_mutex);
-       setup_dig_playback_stream(codec, nid, stream_tag, format);
-       mutex_unlock(&codec->spdif_mutex);
+static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                       struct hda_codec *codec,
+                                       struct snd_pcm_substream *substream)
+{
+       struct via_spec *spec = codec->spec;
+       snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
        return 0;
 }
 
@@ -842,7 +824,8 @@ static struct hda_pcm_stream vt1708_pcm_digital_playback = {
        .ops = {
                .open = via_dig_playback_pcm_open,
                .close = via_dig_playback_pcm_close,
-               .prepare = via_dig_playback_pcm_prepare
+               .prepare = via_dig_playback_pcm_prepare,
+               .cleanup = via_dig_playback_pcm_cleanup
        },
 };
 
@@ -874,13 +857,6 @@ static int via_build_controls(struct hda_codec *codec)
                if (err < 0)
                        return err;
                spec->multiout.share_spdif = 1;
-
-               if (spec->extra_dig_out_nid) {
-                       err = snd_hda_create_spdif_out_ctls(codec,
-                                                   spec->extra_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);
@@ -1013,10 +989,6 @@ static void via_unsol_event(struct hda_codec *codec,
                via_gpio_control(codec);
 }
 
-static hda_nid_t slave_dig_outs[] = {
-       0,
-};
-
 static int via_init(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
@@ -1051,8 +1023,9 @@ static int via_init(struct hda_codec *codec)
                snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
                                    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
 
-       /* no slave outs */
-       codec->slave_dig_outs = slave_dig_outs;
+       /* assign slave outs */
+       if (spec->slave_dig_outs[0])
+               codec->slave_dig_outs = spec->slave_dig_outs;
 
        return 0;
 }
@@ -2134,7 +2107,8 @@ static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
        .ops = {
                .open = via_dig_playback_pcm_open,
                .close = via_dig_playback_pcm_close,
-               .prepare = via_dig_playback_pcm_prepare
+               .prepare = via_dig_playback_pcm_prepare,
+               .cleanup = via_dig_playback_pcm_cleanup
        },
 };
 
@@ -2589,14 +2563,15 @@ static struct hda_pcm_stream vt1708S_pcm_analog_capture = {
 };
 
 static struct hda_pcm_stream vt1708S_pcm_digital_playback = {
-       .substreams = 2,
+       .substreams = 1,
        .channels_min = 2,
        .channels_max = 2,
        /* NID is set in via_build_pcms */
        .ops = {
                .open = via_dig_playback_pcm_open,
                .close = via_dig_playback_pcm_close,
-               .prepare = via_dig_playback_pcm_prepare
+               .prepare = via_dig_playback_pcm_prepare,
+               .cleanup = via_dig_playback_pcm_cleanup
        },
 };
 
@@ -2805,14 +2780,37 @@ static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec,
        return 0;
 }
 
+/* fill out digital output widgets; one for master and one for slave outputs */
+static void fill_dig_outs(struct hda_codec *codec)
+{
+       struct via_spec *spec = codec->spec;
+       int i;
+
+       for (i = 0; i < spec->autocfg.dig_outs; i++) {
+               hda_nid_t nid;
+               int conn;
+
+               nid = spec->autocfg.dig_out_pins[i];
+               if (!nid)
+                       continue;
+               conn = snd_hda_get_connections(codec, nid, &nid, 1);
+               if (conn < 1)
+                       continue;
+               if (!spec->multiout.dig_out_nid)
+                       spec->multiout.dig_out_nid = nid;
+               else {
+                       spec->slave_dig_outs[0] = nid;
+                       break; /* at most two dig outs */
+               }
+       }
+}
+
 static int vt1708S_parse_auto_config(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
        int err;
-       static hda_nid_t vt1708s_ignore[] = {0x21, 0};
 
-       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
-                                          vt1708s_ignore);
+       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
        if (err < 0)
                return err;
        err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg);
@@ -2833,10 +2831,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
 
        spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
-       if (spec->autocfg.dig_outs)
-               spec->multiout.dig_out_nid = VT1708S_DIGOUT_NID;
-
-       spec->extra_dig_out_nid = 0x15;
+       fill_dig_outs(codec);
 
        if (spec->kctls.list)
                spec->mixers[spec->num_mixers++] = spec->kctls.list;
@@ -3000,7 +2995,8 @@ static struct hda_pcm_stream vt1702_pcm_digital_playback = {
        .ops = {
                .open = via_dig_playback_pcm_open,
                .close = via_dig_playback_pcm_close,
-               .prepare = via_dig_playback_pcm_prepare
+               .prepare = via_dig_playback_pcm_prepare,
+               .cleanup = via_dig_playback_pcm_cleanup
        },
 };
 
@@ -3128,10 +3124,8 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
        int err;
-       static hda_nid_t vt1702_ignore[] = {0x1C, 0};
 
-       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
-                                          vt1702_ignore);
+       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
        if (err < 0)
                return err;
        err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
@@ -3152,10 +3146,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
 
        spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
-       if (spec->autocfg.dig_outs)
-               spec->multiout.dig_out_nid = VT1702_DIGOUT_NID;
-
-       spec->extra_dig_out_nid = 0x1B;
+       fill_dig_outs(codec);
 
        if (spec->kctls.list)
                spec->mixers[spec->num_mixers++] = spec->kctls.list;
index f99fe089495d716ae3c2d05a73903790d4b57fbc..536eae2ccf94d006bbd0e3ea577f17e9ddcea9de 100644 (file)
@@ -5,7 +5,7 @@
 
 snd-ice17xx-ak4xxx-objs := ak4xxx.o
 snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o
-snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o
+snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o
index fdae6deba16bc8df8e07690db8ee14d16b869168..adc909ec125c24e51bfa6bac490cfcd05680c3b5 100644 (file)
@@ -335,6 +335,7 @@ struct snd_ice1712 {
        unsigned int force_rdma1:1;     /* VT1720/4 - RDMA1 as non-spdif */
        unsigned int midi_output:1;     /* VT1720/4: MIDI output triggered */
        unsigned int midi_input:1;      /* VT1720/4: MIDI input triggered */
+       unsigned int own_routing:1;     /* VT1720/4: use own routing ctls */
        unsigned int num_total_dacs;    /* total DACs */
        unsigned int num_total_adcs;    /* total ADCs */
        unsigned int cur_rate;          /* current rate */
@@ -458,10 +459,17 @@ static inline int snd_ice1712_gpio_read_bits(struct snd_ice1712 *ice,
        return  snd_ice1712_gpio_read(ice) & mask;
 }
 
+/* route access functions */
+int snd_ice1724_get_route_val(struct snd_ice1712 *ice, int shift);
+int snd_ice1724_put_route_val(struct snd_ice1712 *ice, unsigned int val,
+                                                               int shift);
+
 int snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice);
 
-int snd_ice1712_akm4xxx_init(struct snd_akm4xxx *ak, const struct snd_akm4xxx *template,
-                            const struct snd_ak4xxx_private *priv, struct snd_ice1712 *ice);
+int snd_ice1712_akm4xxx_init(struct snd_akm4xxx *ak,
+                            const struct snd_akm4xxx *template,
+                            const struct snd_ak4xxx_private *priv,
+                            struct snd_ice1712 *ice);
 void snd_ice1712_akm4xxx_free(struct snd_ice1712 *ice);
 int snd_ice1712_akm4xxx_build_controls(struct snd_ice1712 *ice);
 
index 128510e77a785344886cf6d3207b244850ed6245..36ade77cf37116216d6424dc3871e23377ee79cd 100644 (file)
@@ -49,6 +49,7 @@
 #include "prodigy192.h"
 #include "prodigy_hifi.h"
 #include "juli.h"
+#include "maya44.h"
 #include "phase.h"
 #include "wtm.h"
 #include "se.h"
@@ -65,6 +66,7 @@ MODULE_SUPPORTED_DEVICE("{"
               PRODIGY192_DEVICE_DESC
               PRODIGY_HIFI_DEVICE_DESC
               JULI_DEVICE_DESC
+              MAYA44_DEVICE_DESC
               PHASE_DEVICE_DESC
               WTM_DEVICE_DESC
               SE_DEVICE_DESC
@@ -626,7 +628,7 @@ static unsigned char stdclock_set_mclk(struct snd_ice1712 *ice,
        return 0;
 }
 
-static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
+static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
                                    int force)
 {
        unsigned long flags;
@@ -634,17 +636,18 @@ static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
        unsigned int i, old_rate;
 
        if (rate > ice->hw_rates->list[ice->hw_rates->count - 1])
-               return;
+               return -EINVAL;
+
        spin_lock_irqsave(&ice->reg_lock, flags);
        if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) ||
            (inb(ICEMT1724(ice, DMA_PAUSE)) & DMA_PAUSES)) {
                /* running? we cannot change the rate now... */
                spin_unlock_irqrestore(&ice->reg_lock, flags);
-               return;
+               return -EBUSY;
        }
        if (!force && is_pro_rate_locked(ice)) {
                spin_unlock_irqrestore(&ice->reg_lock, flags);
-               return;
+               return (rate == ice->cur_rate) ? 0 : -EBUSY;
        }
 
        old_rate = ice->get_rate(ice);
@@ -652,7 +655,7 @@ static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
                ice->set_rate(ice, rate);
        else if (rate == ice->cur_rate) {
                spin_unlock_irqrestore(&ice->reg_lock, flags);
-               return;
+               return 0;
        }
 
        ice->cur_rate = rate;
@@ -674,13 +677,15 @@ static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
        }
        if (ice->spdif.ops.setup_rate)
                ice->spdif.ops.setup_rate(ice, rate);
+
+       return 0;
 }
 
 static int snd_vt1724_pcm_hw_params(struct snd_pcm_substream *substream,
                                    struct snd_pcm_hw_params *hw_params)
 {
        struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
-       int i, chs;
+       int i, chs, err;
 
        chs = params_channels(hw_params);
        mutex_lock(&ice->open_mutex);
@@ -715,7 +720,11 @@ static int snd_vt1724_pcm_hw_params(struct snd_pcm_substream *substream,
                }
        }
        mutex_unlock(&ice->open_mutex);
-       snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0);
+
+       err = snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0);
+       if (err < 0)
+               return err;
+
        return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
 }
 
@@ -848,20 +857,39 @@ static snd_pcm_uframes_t snd_vt1724_pcm_pointer(struct snd_pcm_substream *substr
 #endif
 }
 
-static const struct vt1724_pcm_reg vt1724_playback_pro_reg = {
+static const struct vt1724_pcm_reg vt1724_pdma0_reg = {
        .addr = VT1724_MT_PLAYBACK_ADDR,
        .size = VT1724_MT_PLAYBACK_SIZE,
        .count = VT1724_MT_PLAYBACK_COUNT,
        .start = VT1724_PDMA0_START,
 };
 
-static const struct vt1724_pcm_reg vt1724_capture_pro_reg = {
+static const struct vt1724_pcm_reg vt1724_pdma4_reg = {
+       .addr = VT1724_MT_PDMA4_ADDR,
+       .size = VT1724_MT_PDMA4_SIZE,
+       .count = VT1724_MT_PDMA4_COUNT,
+       .start = VT1724_PDMA4_START,
+};
+
+static const struct vt1724_pcm_reg vt1724_rdma0_reg = {
        .addr = VT1724_MT_CAPTURE_ADDR,
        .size = VT1724_MT_CAPTURE_SIZE,
        .count = VT1724_MT_CAPTURE_COUNT,
        .start = VT1724_RDMA0_START,
 };
 
+static const struct vt1724_pcm_reg vt1724_rdma1_reg = {
+       .addr = VT1724_MT_RDMA1_ADDR,
+       .size = VT1724_MT_RDMA1_SIZE,
+       .count = VT1724_MT_RDMA1_COUNT,
+       .start = VT1724_RDMA1_START,
+};
+
+#define vt1724_playback_pro_reg vt1724_pdma0_reg
+#define vt1724_playback_spdif_reg vt1724_pdma4_reg
+#define vt1724_capture_pro_reg vt1724_rdma0_reg
+#define vt1724_capture_spdif_reg vt1724_rdma1_reg
+
 static const struct snd_pcm_hardware snd_vt1724_playback_pro = {
        .info =                 (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1077,20 +1105,6 @@ static int __devinit snd_vt1724_pcm_profi(struct snd_ice1712 *ice, int device)
  * SPDIF PCM
  */
 
-static const struct vt1724_pcm_reg vt1724_playback_spdif_reg = {
-       .addr = VT1724_MT_PDMA4_ADDR,
-       .size = VT1724_MT_PDMA4_SIZE,
-       .count = VT1724_MT_PDMA4_COUNT,
-       .start = VT1724_PDMA4_START,
-};
-
-static const struct vt1724_pcm_reg vt1724_capture_spdif_reg = {
-       .addr = VT1724_MT_RDMA1_ADDR,
-       .size = VT1724_MT_RDMA1_SIZE,
-       .count = VT1724_MT_RDMA1_COUNT,
-       .start = VT1724_RDMA1_START,
-};
-
 /* update spdif control bits; call with reg_lock */
 static void update_spdif_bits(struct snd_ice1712 *ice, unsigned int val)
 {
@@ -1963,7 +1977,7 @@ static inline int digital_route_shift(int idx)
        return idx * 3;
 }
 
-static int get_route_val(struct snd_ice1712 *ice, int shift)
+int snd_ice1724_get_route_val(struct snd_ice1712 *ice, int shift)
 {
        unsigned long val;
        unsigned char eitem;
@@ -1982,7 +1996,8 @@ static int get_route_val(struct snd_ice1712 *ice, int shift)
        return eitem;
 }
 
-static int put_route_val(struct snd_ice1712 *ice, unsigned int val, int shift)
+int snd_ice1724_put_route_val(struct snd_ice1712 *ice, unsigned int val,
+                                                               int shift)
 {
        unsigned int old_val, nval;
        int change;
@@ -2010,7 +2025,7 @@ static int snd_vt1724_pro_route_analog_get(struct snd_kcontrol *kcontrol,
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
        int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
        ucontrol->value.enumerated.item[0] =
-               get_route_val(ice, analog_route_shift(idx));
+               snd_ice1724_get_route_val(ice, analog_route_shift(idx));
        return 0;
 }
 
@@ -2019,8 +2034,9 @@ static int snd_vt1724_pro_route_analog_put(struct snd_kcontrol *kcontrol,
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
        int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-       return put_route_val(ice, ucontrol->value.enumerated.item[0],
-                            analog_route_shift(idx));
+       return snd_ice1724_put_route_val(ice,
+                                        ucontrol->value.enumerated.item[0],
+                                        analog_route_shift(idx));
 }
 
 static int snd_vt1724_pro_route_spdif_get(struct snd_kcontrol *kcontrol,
@@ -2029,7 +2045,7 @@ static int snd_vt1724_pro_route_spdif_get(struct snd_kcontrol *kcontrol,
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
        int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
        ucontrol->value.enumerated.item[0] =
-               get_route_val(ice, digital_route_shift(idx));
+               snd_ice1724_get_route_val(ice, digital_route_shift(idx));
        return 0;
 }
 
@@ -2038,11 +2054,13 @@ static int snd_vt1724_pro_route_spdif_put(struct snd_kcontrol *kcontrol,
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
        int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-       return put_route_val(ice, ucontrol->value.enumerated.item[0],
-                            digital_route_shift(idx));
+       return snd_ice1724_put_route_val(ice,
+                                        ucontrol->value.enumerated.item[0],
+                                        digital_route_shift(idx));
 }
 
-static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata = {
+static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata =
+{
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
        .name = "H/W Playback Route",
        .info = snd_vt1724_pro_route_info,
@@ -2109,6 +2127,7 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
        snd_vt1724_prodigy_hifi_cards,
        snd_vt1724_prodigy192_cards,
        snd_vt1724_juli_cards,
+       snd_vt1724_maya44_cards,
        snd_vt1724_phase_cards,
        snd_vt1724_wtm_cards,
        snd_vt1724_se_cards,
@@ -2246,8 +2265,10 @@ static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice,
 static void __devinit snd_vt1724_chip_reset(struct snd_ice1712 *ice)
 {
        outb(VT1724_RESET , ICEREG1724(ice, CONTROL));
+       inb(ICEREG1724(ice, CONTROL)); /* pci posting flush */
        msleep(10);
        outb(0, ICEREG1724(ice, CONTROL));
+       inb(ICEREG1724(ice, CONTROL)); /* pci posting flush */
        msleep(10);
 }
 
@@ -2277,9 +2298,12 @@ static int __devinit snd_vt1724_spdif_build_controls(struct snd_ice1712 *ice)
        if (snd_BUG_ON(!ice->pcm))
                return -EIO;
 
-       err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_mixer_pro_spdif_route, ice));
-       if (err < 0)
-               return err;
+       if (!ice->own_routing) {
+               err = snd_ctl_add(ice->card,
+                       snd_ctl_new1(&snd_vt1724_mixer_pro_spdif_route, ice));
+               if (err < 0)
+                       return err;
+       }
 
        err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_spdif_switch, ice));
        if (err < 0)
@@ -2326,7 +2350,7 @@ static int __devinit snd_vt1724_build_controls(struct snd_ice1712 *ice)
        if (err < 0)
                return err;
 
-       if (ice->num_total_dacs > 0) {
+       if (!ice->own_routing && ice->num_total_dacs > 0) {
                struct snd_kcontrol_new tmp = snd_vt1724_mixer_pro_analog_route;
                tmp.count = ice->num_total_dacs;
                if (ice->vt1720 && tmp.count > 2)
diff --git a/sound/pci/ice1712/maya44.c b/sound/pci/ice1712/maya44.c
new file mode 100644 (file)
index 0000000..3e1c20a
--- /dev/null
@@ -0,0 +1,779 @@
+/*
+ *   ALSA driver for ICEnsemble VT1724 (Envy24HT)
+ *
+ *   Lowlevel functions for ESI Maya44 cards
+ *
+ *     Copyright (c) 2009 Takashi Iwai <tiwai@suse.de>
+ *     Based on the patches by Rainer Zimmermann <mail@lightshed.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+
+#include "ice1712.h"
+#include "envy24ht.h"
+#include "maya44.h"
+
+/* WM8776 register indexes */
+#define WM8776_REG_HEADPHONE_L         0x00
+#define WM8776_REG_HEADPHONE_R         0x01
+#define WM8776_REG_HEADPHONE_MASTER    0x02
+#define WM8776_REG_DAC_ATTEN_L         0x03
+#define WM8776_REG_DAC_ATTEN_R         0x04
+#define WM8776_REG_DAC_ATTEN_MASTER    0x05
+#define WM8776_REG_DAC_PHASE           0x06
+#define WM8776_REG_DAC_CONTROL         0x07
+#define WM8776_REG_DAC_MUTE            0x08
+#define WM8776_REG_DAC_DEEMPH          0x09
+#define WM8776_REG_DAC_IF_CONTROL      0x0a
+#define WM8776_REG_ADC_IF_CONTROL      0x0b
+#define WM8776_REG_MASTER_MODE_CONTROL 0x0c
+#define WM8776_REG_POWERDOWN           0x0d
+#define WM8776_REG_ADC_ATTEN_L         0x0e
+#define WM8776_REG_ADC_ATTEN_R         0x0f
+#define WM8776_REG_ADC_ALC1            0x10
+#define WM8776_REG_ADC_ALC2            0x11
+#define WM8776_REG_ADC_ALC3            0x12
+#define WM8776_REG_ADC_NOISE_GATE      0x13
+#define WM8776_REG_ADC_LIMITER         0x14
+#define WM8776_REG_ADC_MUX             0x15
+#define WM8776_REG_OUTPUT_MUX          0x16
+#define WM8776_REG_RESET               0x17
+
+#define WM8776_NUM_REGS                        0x18
+
+/* clock ratio identifiers for snd_wm8776_set_rate() */
+#define WM8776_CLOCK_RATIO_128FS       0
+#define WM8776_CLOCK_RATIO_192FS       1
+#define WM8776_CLOCK_RATIO_256FS       2
+#define WM8776_CLOCK_RATIO_384FS       3
+#define WM8776_CLOCK_RATIO_512FS       4
+#define WM8776_CLOCK_RATIO_768FS       5
+
+enum { WM_VOL_HP, WM_VOL_DAC, WM_VOL_ADC, WM_NUM_VOLS };
+enum { WM_SW_DAC, WM_SW_BYPASS, WM_NUM_SWITCHES };
+
+struct snd_wm8776 {
+       unsigned char addr;
+       unsigned short regs[WM8776_NUM_REGS];
+       unsigned char volumes[WM_NUM_VOLS][2];
+       unsigned int switch_bits;
+};
+
+struct snd_maya44 {
+       struct snd_ice1712 *ice;
+       struct snd_wm8776 wm[2];
+       struct mutex mutex;
+};
+
+
+/* write the given register and save the data to the cache */
+static void wm8776_write(struct snd_ice1712 *ice, struct snd_wm8776 *wm,
+                        unsigned char reg, unsigned short val)
+{
+       /*
+        * WM8776 registers are up to 9 bits wide, bit 8 is placed in the LSB
+        * of the address field
+        */
+       snd_vt1724_write_i2c(ice, wm->addr,
+                            (reg << 1) | ((val >> 8) & 1),
+                            val & 0xff);
+       wm->regs[reg] = val;
+}
+
+/*
+ * update the given register with and/or mask and save the data to the cache
+ */
+static int wm8776_write_bits(struct snd_ice1712 *ice, struct snd_wm8776 *wm,
+                            unsigned char reg,
+                            unsigned short mask, unsigned short val)
+{
+       val |= wm->regs[reg] & ~mask;
+       if (val != wm->regs[reg]) {
+               wm8776_write(ice, wm, reg, val);
+               return 1;
+       }
+       return 0;
+}
+
+
+/*
+ * WM8776 volume controls
+ */
+
+struct maya_vol_info {
+       unsigned int maxval;            /* volume range: 0..maxval */
+       unsigned char regs[2];          /* left and right registers */
+       unsigned short mask;            /* value mask */
+       unsigned short offset;          /* zero-value offset */
+       unsigned short mute;            /* mute bit */
+       unsigned short update;          /* update bits */
+       unsigned char mux_bits[2];      /* extra bits for ADC mute */
+};
+
+static struct maya_vol_info vol_info[WM_NUM_VOLS] = {
+       [WM_VOL_HP] = {
+               .maxval = 80,
+               .regs = { WM8776_REG_HEADPHONE_L, WM8776_REG_HEADPHONE_R },
+               .mask = 0x7f,
+               .offset = 0x30,
+               .mute = 0x00,
+               .update = 0x180,        /* update and zero-cross enable */
+       },
+       [WM_VOL_DAC] = {
+               .maxval = 255,
+               .regs = { WM8776_REG_DAC_ATTEN_L, WM8776_REG_DAC_ATTEN_R },
+               .mask = 0xff,
+               .offset = 0x01,
+               .mute = 0x00,
+               .update = 0x100,        /* zero-cross enable */
+       },
+       [WM_VOL_ADC] = {
+               .maxval = 91,
+               .regs = { WM8776_REG_ADC_ATTEN_L, WM8776_REG_ADC_ATTEN_R },
+               .mask = 0xff,
+               .offset = 0xa5,
+               .mute = 0xa5,
+               .update = 0x100,        /* update */
+               .mux_bits = { 0x80, 0x40 }, /* ADCMUX bits */
+       },
+};
+
+/*
+ * dB tables
+ */
+/* headphone output: mute, -73..+6db (1db step) */
+static const DECLARE_TLV_DB_SCALE(db_scale_hp, -7400, 100, 1);
+/* DAC output: mute, -127..0db (0.5db step) */
+static const DECLARE_TLV_DB_SCALE(db_scale_dac, -12750, 50, 1);
+/* ADC gain: mute, -21..+24db (0.5db step) */
+static const DECLARE_TLV_DB_SCALE(db_scale_adc, -2100, 50, 1);
+
+static int maya_vol_info(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_info *uinfo)
+{
+       unsigned int idx = kcontrol->private_value;
+       struct maya_vol_info *vol = &vol_info[idx];
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 2;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = vol->maxval;
+       return 0;
+}
+
+static int maya_vol_get(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
+       struct snd_wm8776 *wm =
+               &chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
+       unsigned int idx = kcontrol->private_value;
+
+       mutex_lock(&chip->mutex);
+       ucontrol->value.integer.value[0] = wm->volumes[idx][0];
+       ucontrol->value.integer.value[1] = wm->volumes[idx][1];
+       mutex_unlock(&chip->mutex);
+       return 0;
+}
+
+static int maya_vol_put(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
+       struct snd_wm8776 *wm =
+               &chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
+       unsigned int idx = kcontrol->private_value;
+       struct maya_vol_info *vol = &vol_info[idx];
+       unsigned int val, data;
+       int ch, changed = 0;
+
+       mutex_lock(&chip->mutex);
+       for (ch = 0; ch < 2; ch++) {
+               val = ucontrol->value.integer.value[ch];
+               if (val > vol->maxval)
+                       val = vol->maxval;
+               if (val == wm->volumes[idx][ch])
+                       continue;
+               if (!val)
+                       data = vol->mute;
+               else
+                       data = (val - 1) + vol->offset;
+               data |= vol->update;
+               changed |= wm8776_write_bits(chip->ice, wm, vol->regs[ch],
+                                            vol->mask | vol->update, data);
+               if (vol->mux_bits[ch])
+                       wm8776_write_bits(chip->ice, wm, WM8776_REG_ADC_MUX,
+                                         vol->mux_bits[ch],
+                                         val ? 0 : vol->mux_bits[ch]);
+               wm->volumes[idx][ch] = val;
+       }
+       mutex_unlock(&chip->mutex);
+       return changed;
+}
+
+/*
+ * WM8776 switch controls
+ */
+
+#define COMPOSE_SW_VAL(idx, reg, mask) ((idx) | ((reg) << 8) | ((mask) << 16))
+#define GET_SW_VAL_IDX(val)    ((val) & 0xff)
+#define GET_SW_VAL_REG(val)    (((val) >> 8) & 0xff)
+#define GET_SW_VAL_MASK(val)   (((val) >> 16) & 0xff)
+
+#define maya_sw_info   snd_ctl_boolean_mono_info
+
+static int maya_sw_get(struct snd_kcontrol *kcontrol,
+                      struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
+       struct snd_wm8776 *wm =
+               &chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
+       unsigned int idx = GET_SW_VAL_IDX(kcontrol->private_value);
+
+       ucontrol->value.integer.value[0] = (wm->switch_bits >> idx) & 1;
+       return 0;
+}
+
+static int maya_sw_put(struct snd_kcontrol *kcontrol,
+                      struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
+       struct snd_wm8776 *wm =
+               &chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
+       unsigned int idx = GET_SW_VAL_IDX(kcontrol->private_value);
+       unsigned int mask, val;
+       int changed;
+
+       mutex_lock(&chip->mutex);
+       mask = 1 << idx;
+       wm->switch_bits &= ~mask;
+       val = ucontrol->value.integer.value[0];
+       if (val)
+               wm->switch_bits |= mask;
+       mask = GET_SW_VAL_MASK(kcontrol->private_value);
+       changed = wm8776_write_bits(chip->ice, wm,
+                                   GET_SW_VAL_REG(kcontrol->private_value),
+                                   mask, val ? mask : 0);
+       mutex_unlock(&chip->mutex);
+       return changed;
+}
+
+/*
+ * GPIO pins (known ones for maya44)
+ */
+#define GPIO_PHANTOM_OFF       2
+#define GPIO_MIC_RELAY         4
+#define GPIO_SPDIF_IN_INV      5
+#define GPIO_MUST_BE_0         7
+
+/*
+ * GPIO switch controls
+ */
+
+#define COMPOSE_GPIO_VAL(shift, inv)   ((shift) | ((inv) << 8))
+#define GET_GPIO_VAL_SHIFT(val)                ((val) & 0xff)
+#define GET_GPIO_VAL_INV(val)          (((val) >> 8) & 1)
+
+static int maya_set_gpio_bits(struct snd_ice1712 *ice, unsigned int mask,
+                             unsigned int bits)
+{
+       unsigned int data;
+       data = snd_ice1712_gpio_read(ice);
+       if ((data & mask) == bits)
+               return 0;
+       snd_ice1712_gpio_write(ice, (data & ~mask) | bits);
+       return 1;
+}
+
+#define maya_gpio_sw_info      snd_ctl_boolean_mono_info
+
+static int maya_gpio_sw_get(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
+       unsigned int shift = GET_GPIO_VAL_SHIFT(kcontrol->private_value);
+       unsigned int val;
+
+       val = (snd_ice1712_gpio_read(chip->ice) >> shift) & 1;
+       if (GET_GPIO_VAL_INV(kcontrol->private_value))
+               val = !val;
+       ucontrol->value.integer.value[0] = val;
+       return 0;
+}
+
+static int maya_gpio_sw_put(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
+       unsigned int shift = GET_GPIO_VAL_SHIFT(kcontrol->private_value);
+       unsigned int val, mask;
+       int changed;
+
+       mutex_lock(&chip->mutex);
+       mask = 1 << shift;
+       val = ucontrol->value.integer.value[0];
+       if (GET_GPIO_VAL_INV(kcontrol->private_value))
+               val = !val;
+       val = val ? mask : 0;
+       changed = maya_set_gpio_bits(chip->ice, mask, val);
+       mutex_unlock(&chip->mutex);
+       return changed;
+}
+
+/*
+ * capture source selection
+ */
+
+/* known working input slots (0-4) */
+#define MAYA_LINE_IN   1       /* in-2 */
+#define MAYA_MIC_IN    4       /* in-5 */
+
+static void wm8776_select_input(struct snd_maya44 *chip, int idx, int line)
+{
+       wm8776_write_bits(chip->ice, &chip->wm[idx], WM8776_REG_ADC_MUX,
+                         0x1f, 1 << line);
+}
+
+static int maya_rec_src_info(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_info *uinfo)
+{
+       static char *texts[] = { "Line", "Mic" };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = ARRAY_SIZE(texts);
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item =
+                       uinfo->value.enumerated.items - 1;
+       strcpy(uinfo->value.enumerated.name,
+              texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int maya_rec_src_get(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
+       int sel;
+
+       if (snd_ice1712_gpio_read(chip->ice) & (1 << GPIO_MIC_RELAY))
+               sel = 1;
+       else
+               sel = 0;
+       ucontrol->value.enumerated.item[0] = sel;
+       return 0;
+}
+
+static int maya_rec_src_put(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
+       int sel = ucontrol->value.enumerated.item[0];
+       int changed;
+
+       mutex_lock(&chip->mutex);
+       changed = maya_set_gpio_bits(chip->ice, GPIO_MIC_RELAY,
+                                    sel ? GPIO_MIC_RELAY : 0);
+       wm8776_select_input(chip, 0, sel ? MAYA_MIC_IN : MAYA_LINE_IN);
+       mutex_unlock(&chip->mutex);
+       return changed;
+}
+
+/*
+ * Maya44 routing switch settings have different meanings than the standard
+ * ice1724 switches as defined in snd_vt1724_pro_route_info (ice1724.c).
+ */
+static int maya_pb_route_info(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_info *uinfo)
+{
+       static char *texts[] = {
+               "PCM Out", /* 0 */
+               "Input 1", "Input 2", "Input 3", "Input 4"
+       };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = ARRAY_SIZE(texts);
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item =
+                       uinfo->value.enumerated.items - 1;
+       strcpy(uinfo->value.enumerated.name,
+              texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int maya_pb_route_shift(int idx)
+{
+       static const unsigned char shift[10] =
+               { 8, 20, 0, 3, 11, 23, 14, 26, 17, 29 };
+       return shift[idx % 10];
+}
+
+static int maya_pb_route_get(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
+       int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+       ucontrol->value.enumerated.item[0] =
+               snd_ice1724_get_route_val(chip->ice, maya_pb_route_shift(idx));
+       return 0;
+}
+
+static int maya_pb_route_put(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
+       int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+       return snd_ice1724_put_route_val(chip->ice,
+                                        ucontrol->value.enumerated.item[0],
+                                        maya_pb_route_shift(idx));
+}
+
+
+/*
+ * controls to be added
+ */
+
+static struct snd_kcontrol_new maya_controls[] __devinitdata = {
+       {
+               .name = "Crossmix Playback Volume",
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                       SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+               .info = maya_vol_info,
+               .get = maya_vol_get,
+               .put = maya_vol_put,
+               .tlv = { .p = db_scale_hp },
+               .private_value = WM_VOL_HP,
+               .count = 2,
+       },
+       {
+               .name = "PCM Playback Volume",
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                       SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+               .info = maya_vol_info,
+               .get = maya_vol_get,
+               .put = maya_vol_put,
+               .tlv = { .p = db_scale_dac },
+               .private_value = WM_VOL_DAC,
+               .count = 2,
+       },
+       {
+               .name = "Line Capture Volume",
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                       SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+               .info = maya_vol_info,
+               .get = maya_vol_get,
+               .put = maya_vol_put,
+               .tlv = { .p = db_scale_adc },
+               .private_value = WM_VOL_ADC,
+               .count = 2,
+       },
+       {
+               .name = "PCM Playback Switch",
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .info = maya_sw_info,
+               .get = maya_sw_get,
+               .put = maya_sw_put,
+               .private_value = COMPOSE_SW_VAL(WM_SW_DAC,
+                                               WM8776_REG_OUTPUT_MUX, 0x01),
+               .count = 2,
+       },
+       {
+               .name = "Bypass Playback Switch",
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .info = maya_sw_info,
+               .get = maya_sw_get,
+               .put = maya_sw_put,
+               .private_value = COMPOSE_SW_VAL(WM_SW_BYPASS,
+                                               WM8776_REG_OUTPUT_MUX, 0x04),
+               .count = 2,
+       },
+       {
+               .name = "Capture Source",
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .info = maya_rec_src_info,
+               .get = maya_rec_src_get,
+               .put = maya_rec_src_put,
+       },
+       {
+               .name = "Mic Phantom Power Switch",
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .info = maya_gpio_sw_info,
+               .get = maya_gpio_sw_get,
+               .put = maya_gpio_sw_put,
+               .private_value = COMPOSE_GPIO_VAL(GPIO_PHANTOM_OFF, 1),
+       },
+       {
+               .name = "SPDIF Capture Switch",
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .info = maya_gpio_sw_info,
+               .get = maya_gpio_sw_get,
+               .put = maya_gpio_sw_put,
+               .private_value = COMPOSE_GPIO_VAL(GPIO_SPDIF_IN_INV, 1),
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "H/W Playback Route",
+               .info = maya_pb_route_info,
+               .get = maya_pb_route_get,
+               .put = maya_pb_route_put,
+               .count = 4,  /* FIXME: do controls 5-9 have any meaning? */
+       },
+};
+
+static int __devinit maya44_add_controls(struct snd_ice1712 *ice)
+{
+       int err, i;
+
+       for (i = 0; i < ARRAY_SIZE(maya_controls); i++) {
+               err = snd_ctl_add(ice->card, snd_ctl_new1(&maya_controls[i],
+                                                         ice->spec));
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
+
+/*
+ * initialize a wm8776 chip
+ */
+static void __devinit wm8776_init(struct snd_ice1712 *ice,
+                                 struct snd_wm8776 *wm, unsigned int addr)
+{
+       static const unsigned short inits_wm8776[] = {
+               0x02, 0x100, /* R2: headphone L+R muted + update */
+               0x05, 0x100, /* R5: DAC output L+R muted + update */
+               0x06, 0x000, /* R6: DAC output phase normal */
+               0x07, 0x091, /* R7: DAC enable zero cross detection,
+                               normal output */
+               0x08, 0x000, /* R8: DAC soft mute off */
+               0x09, 0x000, /* R9: no deemph, DAC zero detect disabled */
+               0x0a, 0x022, /* R10: DAC I2C mode, std polarities, 24bit */
+               0x0b, 0x022, /* R11: ADC I2C mode, std polarities, 24bit,
+                               highpass filter enabled */
+               0x0c, 0x042, /* R12: ADC+DAC slave, ADC+DAC 44,1kHz */
+               0x0d, 0x000, /* R13: all power up */
+               0x0e, 0x100, /* R14: ADC left muted,
+                               enable zero cross detection */
+               0x0f, 0x100, /* R15: ADC right muted,
+                               enable zero cross detection */
+                            /* R16: ALC...*/
+               0x11, 0x000, /* R17: disable ALC */
+                            /* R18: ALC...*/
+                            /* R19: noise gate...*/
+               0x15, 0x000, /* R21: ADC input mux init, mute all inputs */
+               0x16, 0x001, /* R22: output mux, select DAC */
+               0xff, 0xff
+       };
+
+       const unsigned short *ptr;
+       unsigned char reg;
+       unsigned short data;
+
+       wm->addr = addr;
+       /* enable DAC output; mute bypass, aux & all inputs */
+       wm->switch_bits = (1 << WM_SW_DAC);
+
+       ptr = inits_wm8776;
+       while (*ptr != 0xff) {
+               reg = *ptr++;
+               data = *ptr++;
+               wm8776_write(ice, wm, reg, data);
+       }
+}
+
+
+/*
+ * change the rate on the WM8776 codecs.
+ * this assumes that the VT17xx's rate is changed by the calling function.
+ * NOTE: even though the WM8776's are running in slave mode and rate
+ * selection is automatic, we need to call snd_wm8776_set_rate() here
+ * to make sure some flags are set correctly.
+ */
+static void set_rate(struct snd_ice1712 *ice, unsigned int rate)
+{
+       struct snd_maya44 *chip = ice->spec;
+       unsigned int ratio, adc_ratio, val;
+       int i;
+
+       switch (rate) {
+       case 192000:
+               ratio = WM8776_CLOCK_RATIO_128FS;
+               break;
+       case 176400:
+               ratio = WM8776_CLOCK_RATIO_128FS;
+               break;
+       case 96000:
+               ratio = WM8776_CLOCK_RATIO_256FS;
+               break;
+       case 88200:
+               ratio = WM8776_CLOCK_RATIO_384FS;
+               break;
+       case 48000:
+               ratio = WM8776_CLOCK_RATIO_512FS;
+               break;
+       case 44100:
+               ratio = WM8776_CLOCK_RATIO_512FS;
+               break;
+       case 32000:
+               ratio = WM8776_CLOCK_RATIO_768FS;
+               break;
+       case 0:
+               /* no hint - S/PDIF input is master, simply return */
+               return;
+       default:
+               snd_BUG();
+               return;
+       }
+
+       /*
+        * this currently sets the same rate for ADC and DAC, but limits
+        * ADC rate to 256X (96kHz). For 256X mode (96kHz), this sets ADC
+        * oversampling to 64x, as recommended by WM8776 datasheet.
+        * Setting the rate is not really necessary in slave mode.
+        */
+       adc_ratio = ratio;
+       if (adc_ratio < WM8776_CLOCK_RATIO_256FS)
+               adc_ratio = WM8776_CLOCK_RATIO_256FS;
+
+       val = adc_ratio;
+       if (adc_ratio == WM8776_CLOCK_RATIO_256FS)
+               val |= 8;
+       val |= ratio << 4;
+
+       mutex_lock(&chip->mutex);
+       for (i = 0; i < 2; i++)
+               wm8776_write_bits(ice, &chip->wm[i],
+                                 WM8776_REG_MASTER_MODE_CONTROL,
+                                 0x180, val);
+       mutex_unlock(&chip->mutex);
+}
+
+/*
+ * supported sample rates (to override the default one)
+ */
+
+static unsigned int rates[] = {
+       32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000
+};
+
+/* playback rates: 32..192 kHz */
+static struct snd_pcm_hw_constraint_list dac_rates = {
+       .count = ARRAY_SIZE(rates),
+       .list = rates,
+       .mask = 0
+};
+
+
+/*
+ * chip addresses on I2C bus
+ */
+static unsigned char wm8776_addr[2] __devinitdata = {
+       0x34, 0x36, /* codec 0 & 1 */
+};
+
+/*
+ * initialize the chip
+ */
+static int __devinit maya44_init(struct snd_ice1712 *ice)
+{
+       int i;
+       struct snd_maya44 *chip;
+
+       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+       mutex_init(&chip->mutex);
+       chip->ice = ice;
+       ice->spec = chip;
+
+       /* initialise codecs */
+       ice->num_total_dacs = 4;
+       ice->num_total_adcs = 4;
+       ice->akm_codecs = 0;
+
+       for (i = 0; i < 2; i++) {
+               wm8776_init(ice, &chip->wm[i], wm8776_addr[i]);
+               wm8776_select_input(chip, i, MAYA_LINE_IN);
+       }
+
+       /* set card specific rates */
+       ice->hw_rates = &dac_rates;
+
+       /* register change rate notifier */
+       ice->gpio.set_pro_rate = set_rate;
+
+       /* RDMA1 (2nd input channel) is used for ADC by default */
+       ice->force_rdma1 = 1;
+
+       /* have an own routing control */
+       ice->own_routing = 1;
+
+       return 0;
+}
+
+
+/*
+ * Maya44 boards don't provide the EEPROM data except for the vendor IDs.
+ * hence the driver needs to sets up it properly.
+ */
+
+static unsigned char maya44_eeprom[] __devinitdata = {
+       [ICE_EEP2_SYSCONF]     = 0x45,
+               /* clock xin1=49.152MHz, mpu401, 2 stereo ADCs+DACs */
+       [ICE_EEP2_ACLINK]      = 0x80,
+               /* I2S */
+       [ICE_EEP2_I2S]         = 0xf8,
+               /* vol, 96k, 24bit, 192k */
+       [ICE_EEP2_SPDIF]       = 0xc3,
+               /* enable spdif out, spdif out supp, spdif-in, ext spdif out */
+       [ICE_EEP2_GPIO_DIR]    = 0xff,
+       [ICE_EEP2_GPIO_DIR1]   = 0xff,
+       [ICE_EEP2_GPIO_DIR2]   = 0xff,
+       [ICE_EEP2_GPIO_MASK]   = 0/*0x9f*/,
+       [ICE_EEP2_GPIO_MASK1]  = 0/*0xff*/,
+       [ICE_EEP2_GPIO_MASK2]  = 0/*0x7f*/,
+       [ICE_EEP2_GPIO_STATE]  = (1 << GPIO_PHANTOM_OFF) |
+                       (1 << GPIO_SPDIF_IN_INV),
+       [ICE_EEP2_GPIO_STATE1] = 0x00,
+       [ICE_EEP2_GPIO_STATE2] = 0x00,
+};
+
+/* entry point */
+struct snd_ice1712_card_info snd_vt1724_maya44_cards[] __devinitdata = {
+       {
+               .subvendor = VT1724_SUBDEVICE_MAYA44,
+               .name = "ESI Maya44",
+               .model = "maya44",
+               .chip_init = maya44_init,
+               .build_controls = maya44_add_controls,
+               .eeprom_size = sizeof(maya44_eeprom),
+               .eeprom_data = maya44_eeprom,
+       },
+       { } /* terminator */
+};
diff --git a/sound/pci/ice1712/maya44.h b/sound/pci/ice1712/maya44.h
new file mode 100644 (file)
index 0000000..eafd03a
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __SOUND_MAYA44_H
+#define __SOUND_MAYA44_H
+
+#define MAYA44_DEVICE_DESC             "{ESI,Maya44},"
+
+#define VT1724_SUBDEVICE_MAYA44                0x34315441      /* Maya44 */
+
+extern struct snd_ice1712_card_info  snd_vt1724_maya44_cards[];
+
+#endif /* __SOUND_MAYA44_H */
diff --git a/sound/pci/lx6464es/Makefile b/sound/pci/lx6464es/Makefile
new file mode 100644 (file)
index 0000000..eb04a6c
--- /dev/null
@@ -0,0 +1,2 @@
+snd-lx6464es-objs := lx6464es.o lx_core.o
+obj-$(CONFIG_SND_LX6464ES) += snd-lx6464es.o
diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c
new file mode 100644 (file)
index 0000000..ccf1b38
--- /dev/null
@@ -0,0 +1,1159 @@
+/* -*- linux-c -*- *
+ *
+ * ALSA driver for the digigram lx6464es interface
+ *
+ * Copyright (c) 2008, 2009 Tim Blechmann <tim@klingt.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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/info.h>
+
+#include "lx6464es.h"
+
+MODULE_AUTHOR("Tim Blechmann");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("digigram lx6464es");
+MODULE_SUPPORTED_DEVICE("{digigram lx6464es{}}");
+
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Digigram LX6464ES interface.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for  Digigram LX6464ES interface.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable/disable specific Digigram LX6464ES soundcards.");
+
+static const char card_name[] = "LX6464ES";
+
+
+#define PCI_DEVICE_ID_PLX_LX6464ES             PCI_DEVICE_ID_PLX_9056
+
+static struct pci_device_id snd_lx6464es_ids[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES),
+         .subvendor = PCI_VENDOR_ID_DIGIGRAM,
+         .subdevice = PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM
+       },                      /* LX6464ES */
+       { PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES),
+         .subvendor = PCI_VENDOR_ID_DIGIGRAM,
+         .subdevice = PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM
+       },                      /* LX6464ES-CAE */
+       { 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, snd_lx6464es_ids);
+
+
+
+/* PGO pour USERo dans le registre pci_0x06/loc_0xEC */
+#define CHIPSC_RESET_XILINX (1L<<16)
+
+
+/* alsa callbacks */
+static struct snd_pcm_hardware lx_caps = {
+       .info             = (SNDRV_PCM_INFO_MMAP |
+                            SNDRV_PCM_INFO_INTERLEAVED |
+                            SNDRV_PCM_INFO_MMAP_VALID |
+                            SNDRV_PCM_INFO_SYNC_START),
+       .formats          = (SNDRV_PCM_FMTBIT_S16_LE |
+                            SNDRV_PCM_FMTBIT_S16_BE |
+                            SNDRV_PCM_FMTBIT_S24_3LE |
+                            SNDRV_PCM_FMTBIT_S24_3BE),
+       .rates            = (SNDRV_PCM_RATE_CONTINUOUS |
+                            SNDRV_PCM_RATE_8000_192000),
+       .rate_min         = 8000,
+       .rate_max         = 192000,
+       .channels_min     = 2,
+       .channels_max     = 64,
+       .buffer_bytes_max = 64*2*3*MICROBLAZE_IBL_MAX*MAX_STREAM_BUFFER,
+       .period_bytes_min = (2*2*MICROBLAZE_IBL_MIN*2),
+       .period_bytes_max = (4*64*MICROBLAZE_IBL_MAX*MAX_STREAM_BUFFER),
+       .periods_min      = 2,
+       .periods_max      = MAX_STREAM_BUFFER,
+};
+
+static int lx_set_granularity(struct lx6464es *chip, u32 gran);
+
+
+static int lx_hardware_open(struct lx6464es *chip,
+                           struct snd_pcm_substream *substream)
+{
+       int err = 0;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int channels = runtime->channels;
+       int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+
+       snd_pcm_uframes_t period_size = runtime->period_size;
+
+       snd_printd(LXP "allocating pipe for %d channels\n", channels);
+       err = lx_pipe_allocate(chip, 0, is_capture, channels);
+       if (err < 0) {
+               snd_printk(KERN_ERR LXP "allocating pipe failed\n");
+               return err;
+       }
+
+       err = lx_set_granularity(chip, period_size);
+       if (err < 0) {
+               snd_printk(KERN_ERR LXP "setting granularity to %ld failed\n",
+                          period_size);
+               return err;
+       }
+
+       return 0;
+}
+
+static int lx_hardware_start(struct lx6464es *chip,
+                            struct snd_pcm_substream *substream)
+{
+       int err = 0;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+
+       snd_printd(LXP "setting stream format\n");
+       err = lx_stream_set_format(chip, runtime, 0, is_capture);
+       if (err < 0) {
+               snd_printk(KERN_ERR LXP "setting stream format failed\n");
+               return err;
+       }
+
+       snd_printd(LXP "starting pipe\n");
+       err = lx_pipe_start(chip, 0, is_capture);
+       if (err < 0) {
+               snd_printk(KERN_ERR LXP "starting pipe failed\n");
+               return err;
+       }
+
+       snd_printd(LXP "waiting for pipe to start\n");
+       err = lx_pipe_wait_for_start(chip, 0, is_capture);
+       if (err < 0) {
+               snd_printk(KERN_ERR LXP "waiting for pipe failed\n");
+               return err;
+       }
+
+       return err;
+}
+
+
+static int lx_hardware_stop(struct lx6464es *chip,
+                           struct snd_pcm_substream *substream)
+{
+       int err = 0;
+       int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+
+       snd_printd(LXP "pausing pipe\n");
+       err = lx_pipe_pause(chip, 0, is_capture);
+       if (err < 0) {
+               snd_printk(KERN_ERR LXP "pausing pipe failed\n");
+               return err;
+       }
+
+       snd_printd(LXP "waiting for pipe to become idle\n");
+       err = lx_pipe_wait_for_idle(chip, 0, is_capture);
+       if (err < 0) {
+               snd_printk(KERN_ERR LXP "waiting for pipe failed\n");
+               return err;
+       }
+
+       snd_printd(LXP "stopping pipe\n");
+       err = lx_pipe_stop(chip, 0, is_capture);
+       if (err < 0) {
+               snd_printk(LXP "stopping pipe failed\n");
+               return err;
+       }
+
+       return err;
+}
+
+
+static int lx_hardware_close(struct lx6464es *chip,
+                            struct snd_pcm_substream *substream)
+{
+       int err = 0;
+       int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+
+       snd_printd(LXP "releasing pipe\n");
+       err = lx_pipe_release(chip, 0, is_capture);
+       if (err < 0) {
+               snd_printk(LXP "releasing pipe failed\n");
+               return err;
+       }
+
+       return err;
+}
+
+
+static int lx_pcm_open(struct snd_pcm_substream *substream)
+{
+       struct lx6464es *chip = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int err = 0;
+       int board_rate;
+
+       snd_printdd("->lx_pcm_open\n");
+       mutex_lock(&chip->setup_mutex);
+
+       /* copy the struct snd_pcm_hardware struct */
+       runtime->hw = lx_caps;
+
+#if 0
+       /* buffer-size should better be multiple of period-size */
+       err = snd_pcm_hw_constraint_integer(runtime,
+                                           SNDRV_PCM_HW_PARAM_PERIODS);
+       if (err < 0) {
+               snd_printk(KERN_WARNING LXP "could not constrain periods\n");
+               goto exit;
+       }
+#endif
+
+       /* the clock rate cannot be changed */
+       board_rate = chip->board_sample_rate;
+       err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,
+                                          board_rate, board_rate);
+
+       if (err < 0) {
+               snd_printk(KERN_WARNING LXP "could not constrain periods\n");
+               goto exit;
+       }
+
+       /* constrain period size */
+       err = snd_pcm_hw_constraint_minmax(runtime,
+                                          SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                                          MICROBLAZE_IBL_MIN,
+                                          MICROBLAZE_IBL_MAX);
+       if (err < 0) {
+               snd_printk(KERN_WARNING LXP
+                          "could not constrain period size\n");
+               goto exit;
+       }
+
+       snd_pcm_hw_constraint_step(runtime, 0,
+                                  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
+
+       snd_pcm_set_sync(substream);
+       err = 0;
+
+exit:
+       runtime->private_data = chip;
+
+       mutex_unlock(&chip->setup_mutex);
+       snd_printdd("<-lx_pcm_open, %d\n", err);
+       return err;
+}
+
+static int lx_pcm_close(struct snd_pcm_substream *substream)
+{
+       int err = 0;
+       snd_printdd("->lx_pcm_close\n");
+       return err;
+}
+
+static snd_pcm_uframes_t lx_pcm_stream_pointer(struct snd_pcm_substream
+                                              *substream)
+{
+       struct lx6464es *chip = snd_pcm_substream_chip(substream);
+       snd_pcm_uframes_t pos;
+       unsigned long flags;
+       int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+
+       struct lx_stream *lx_stream = is_capture ? &chip->capture_stream :
+               &chip->playback_stream;
+
+       snd_printdd("->lx_pcm_stream_pointer\n");
+
+       spin_lock_irqsave(&chip->lock, flags);
+       pos = lx_stream->frame_pos * substream->runtime->period_size;
+       spin_unlock_irqrestore(&chip->lock, flags);
+
+       snd_printdd(LXP "stream_pointer at %ld\n", pos);
+       return pos;
+}
+
+static int lx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+       struct lx6464es *chip = snd_pcm_substream_chip(substream);
+       int err = 0;
+       const int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+
+       snd_printdd("->lx_pcm_prepare\n");
+
+       mutex_lock(&chip->setup_mutex);
+
+       if (chip->hardware_running[is_capture]) {
+               err = lx_hardware_stop(chip, substream);
+               if (err < 0) {
+                       snd_printk(KERN_ERR LXP "failed to stop hardware. "
+                                  "Error code %d\n", err);
+                       goto exit;
+               }
+
+               err = lx_hardware_close(chip, substream);
+               if (err < 0) {
+                       snd_printk(KERN_ERR LXP "failed to close hardware. "
+                                  "Error code %d\n", err);
+                       goto exit;
+               }
+       }
+
+       snd_printd(LXP "opening hardware\n");
+       err = lx_hardware_open(chip, substream);
+       if (err < 0) {
+               snd_printk(KERN_ERR LXP "failed to open hardware. "
+                          "Error code %d\n", err);
+               goto exit;
+       }
+
+       err = lx_hardware_start(chip, substream);
+       if (err < 0) {
+               snd_printk(KERN_ERR LXP "failed to start hardware. "
+                          "Error code %d\n", err);
+               goto exit;
+       }
+
+       chip->hardware_running[is_capture] = 1;
+
+       if (chip->board_sample_rate != substream->runtime->rate) {
+               if (!err)
+                       chip->board_sample_rate = substream->runtime->rate;
+       }
+
+exit:
+       mutex_unlock(&chip->setup_mutex);
+       return err;
+}
+
+static int lx_pcm_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *hw_params, int is_capture)
+{
+       struct lx6464es *chip = snd_pcm_substream_chip(substream);
+       int err = 0;
+
+       snd_printdd("->lx_pcm_hw_params\n");
+
+       mutex_lock(&chip->setup_mutex);
+
+       /* set dma buffer */
+       err = snd_pcm_lib_malloc_pages(substream,
+                                      params_buffer_bytes(hw_params));
+
+       if (is_capture)
+               chip->capture_stream.stream = substream;
+       else
+               chip->playback_stream.stream = substream;
+
+       mutex_unlock(&chip->setup_mutex);
+       return err;
+}
+
+static int lx_pcm_hw_params_playback(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *hw_params)
+{
+       return lx_pcm_hw_params(substream, hw_params, 0);
+}
+
+static int lx_pcm_hw_params_capture(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *hw_params)
+{
+       return lx_pcm_hw_params(substream, hw_params, 1);
+}
+
+static int lx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       struct lx6464es *chip = snd_pcm_substream_chip(substream);
+       int err = 0;
+       int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+
+       snd_printdd("->lx_pcm_hw_free\n");
+       mutex_lock(&chip->setup_mutex);
+
+       if (chip->hardware_running[is_capture]) {
+               err = lx_hardware_stop(chip, substream);
+               if (err < 0) {
+                       snd_printk(KERN_ERR LXP "failed to stop hardware. "
+                                  "Error code %d\n", err);
+                       goto exit;
+               }
+
+               err = lx_hardware_close(chip, substream);
+               if (err < 0) {
+                       snd_printk(KERN_ERR LXP "failed to close hardware. "
+                                  "Error code %d\n", err);
+                       goto exit;
+               }
+
+               chip->hardware_running[is_capture] = 0;
+       }
+
+       err = snd_pcm_lib_free_pages(substream);
+
+       if (is_capture)
+               chip->capture_stream.stream = 0;
+       else
+               chip->playback_stream.stream = 0;
+
+exit:
+       mutex_unlock(&chip->setup_mutex);
+       return err;
+}
+
+static void lx_trigger_start(struct lx6464es *chip, struct lx_stream *lx_stream)
+{
+       struct snd_pcm_substream *substream = lx_stream->stream;
+       const int is_capture = lx_stream->is_capture;
+
+       int err;
+
+       const u32 channels = substream->runtime->channels;
+       const u32 bytes_per_frame = channels * 3;
+       const u32 period_size = substream->runtime->period_size;
+       const u32 periods = substream->runtime->periods;
+       const u32 period_bytes = period_size * bytes_per_frame;
+
+       dma_addr_t buf = substream->dma_buffer.addr;
+       int i;
+
+       u32 needed, freed;
+       u32 size_array[5];
+
+       for (i = 0; i != periods; ++i) {
+               u32 buffer_index = 0;
+
+               err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed,
+                                   size_array);
+               snd_printdd(LXP "starting: needed %d, freed %d\n",
+                           needed, freed);
+
+               err = lx_buffer_give(chip, 0, is_capture, period_bytes,
+                                    lower_32_bits(buf), upper_32_bits(buf),
+                                    &buffer_index);
+
+               snd_printdd(LXP "starting: buffer index %x on %p (%d bytes)\n",
+                           buffer_index, (void *)buf, period_bytes);
+               buf += period_bytes;
+       }
+
+       err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed, size_array);
+       snd_printdd(LXP "starting: needed %d, freed %d\n", needed, freed);
+
+       snd_printd(LXP "starting: starting stream\n");
+       err = lx_stream_start(chip, 0, is_capture);
+       if (err < 0)
+               snd_printk(KERN_ERR LXP "couldn't start stream\n");
+       else
+               lx_stream->status = LX_STREAM_STATUS_RUNNING;
+
+       lx_stream->frame_pos = 0;
+}
+
+static void lx_trigger_stop(struct lx6464es *chip, struct lx_stream *lx_stream)
+{
+       const int is_capture = lx_stream->is_capture;
+       int err;
+
+       snd_printd(LXP "stopping: stopping stream\n");
+       err = lx_stream_stop(chip, 0, is_capture);
+       if (err < 0)
+               snd_printk(KERN_ERR LXP "couldn't stop stream\n");
+       else
+               lx_stream->status = LX_STREAM_STATUS_FREE;
+
+}
+
+static void lx_trigger_tasklet_dispatch_stream(struct lx6464es *chip,
+                                              struct lx_stream *lx_stream)
+{
+       switch (lx_stream->status) {
+       case LX_STREAM_STATUS_SCHEDULE_RUN:
+               lx_trigger_start(chip, lx_stream);
+               break;
+
+       case LX_STREAM_STATUS_SCHEDULE_STOP:
+               lx_trigger_stop(chip, lx_stream);
+               break;
+
+       default:
+               break;
+       }
+}
+
+static void lx_trigger_tasklet(unsigned long data)
+{
+       struct lx6464es *chip = (struct lx6464es *)data;
+       unsigned long flags;
+
+       snd_printdd("->lx_trigger_tasklet\n");
+
+       spin_lock_irqsave(&chip->lock, flags);
+       lx_trigger_tasklet_dispatch_stream(chip, &chip->capture_stream);
+       lx_trigger_tasklet_dispatch_stream(chip, &chip->playback_stream);
+       spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int lx_pcm_trigger_dispatch(struct lx6464es *chip,
+                                  struct lx_stream *lx_stream, int cmd)
+{
+       int err = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               lx_stream->status = LX_STREAM_STATUS_SCHEDULE_RUN;
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+               lx_stream->status = LX_STREAM_STATUS_SCHEDULE_STOP;
+               break;
+
+       default:
+               err = -EINVAL;
+               goto exit;
+       }
+       tasklet_schedule(&chip->trigger_tasklet);
+
+exit:
+       return err;
+}
+
+
+static int lx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct lx6464es *chip = snd_pcm_substream_chip(substream);
+       const int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+       struct lx_stream *stream = is_capture ? &chip->capture_stream :
+               &chip->playback_stream;
+
+       snd_printdd("->lx_pcm_trigger\n");
+
+       return lx_pcm_trigger_dispatch(chip, stream, cmd);
+}
+
+static int snd_lx6464es_free(struct lx6464es *chip)
+{
+       snd_printdd("->snd_lx6464es_free\n");
+
+       lx_irq_disable(chip);
+
+       if (chip->irq >= 0)
+               free_irq(chip->irq, chip);
+
+       iounmap(chip->port_dsp_bar);
+       ioport_unmap(chip->port_plx_remapped);
+
+       pci_release_regions(chip->pci);
+       pci_disable_device(chip->pci);
+
+       kfree(chip);
+
+       return 0;
+}
+
+static int snd_lx6464es_dev_free(struct snd_device *device)
+{
+       return snd_lx6464es_free(device->device_data);
+}
+
+/* reset the dsp during initialization */
+static int __devinit lx_init_xilinx_reset(struct lx6464es *chip)
+{
+       int i;
+       u32 plx_reg = lx_plx_reg_read(chip, ePLX_CHIPSC);
+
+       snd_printdd("->lx_init_xilinx_reset\n");
+
+       /* activate reset of xilinx */
+       plx_reg &= ~CHIPSC_RESET_XILINX;
+
+       lx_plx_reg_write(chip, ePLX_CHIPSC, plx_reg);
+       msleep(1);
+
+       lx_plx_reg_write(chip, ePLX_MBOX3, 0);
+       msleep(1);
+
+       plx_reg |= CHIPSC_RESET_XILINX;
+       lx_plx_reg_write(chip, ePLX_CHIPSC, plx_reg);
+
+       /* deactivate reset of xilinx */
+       for (i = 0; i != 100; ++i) {
+               u32 reg_mbox3;
+               msleep(10);
+               reg_mbox3 = lx_plx_reg_read(chip, ePLX_MBOX3);
+               if (reg_mbox3) {
+                       snd_printd(LXP "xilinx reset done\n");
+                       snd_printdd(LXP "xilinx took %d loops\n", i);
+                       break;
+               }
+       }
+
+       /* todo: add some error handling? */
+
+       /* clear mr */
+       lx_dsp_reg_write(chip, eReg_CSM, 0);
+
+       /* le xilinx ES peut ne pas etre encore pret, on attend. */
+       msleep(600);
+
+       return 0;
+}
+
+static int __devinit lx_init_xilinx_test(struct lx6464es *chip)
+{
+       u32 reg;
+
+       snd_printdd("->lx_init_xilinx_test\n");
+
+       /* TEST if we have access to Xilinx/MicroBlaze */
+       lx_dsp_reg_write(chip, eReg_CSM, 0);
+
+       reg = lx_dsp_reg_read(chip, eReg_CSM);
+
+       if (reg) {
+               snd_printk(KERN_ERR LXP "Problem: Reg_CSM %x.\n", reg);
+
+               /* PCI9056_SPACE0_REMAP */
+               lx_plx_reg_write(chip, ePLX_PCICR, 1);
+
+               reg = lx_dsp_reg_read(chip, eReg_CSM);
+               if (reg) {
+                       snd_printk(KERN_ERR LXP "Error: Reg_CSM %x.\n", reg);
+                       return -EAGAIN; /* seems to be appropriate */
+               }
+       }
+
+       snd_printd(LXP "Xilinx/MicroBlaze access test successful\n");
+
+       return 0;
+}
+
+/* initialize ethersound */
+static int __devinit lx_init_ethersound_config(struct lx6464es *chip)
+{
+       int i;
+       u32 orig_conf_es = lx_dsp_reg_read(chip, eReg_CONFES);
+
+       u32 default_conf_es = (64 << IOCR_OUTPUTS_OFFSET) |
+               (64 << IOCR_INPUTS_OFFSET) |
+               (FREQ_RATIO_SINGLE_MODE << FREQ_RATIO_OFFSET);
+
+       u32 conf_es = (orig_conf_es & CONFES_READ_PART_MASK)
+               | (default_conf_es & CONFES_WRITE_PART_MASK);
+
+       snd_printdd("->lx_init_ethersound\n");
+
+       chip->freq_ratio = FREQ_RATIO_SINGLE_MODE;
+
+       /*
+        * write it to the card !
+        * this actually kicks the ES xilinx, the first time since poweron.
+        * the MAC address in the Reg_ADMACESMSB Reg_ADMACESLSB registers
+        * is not ready before this is done, and the bit 2 in Reg_CSES is set.
+        * */
+       lx_dsp_reg_write(chip, eReg_CONFES, conf_es);
+
+       for (i = 0; i != 1000; ++i) {
+               if (lx_dsp_reg_read(chip, eReg_CSES) & 4) {
+                       snd_printd(LXP "ethersound initialized after %dms\n",
+                                  i);
+                       goto ethersound_initialized;
+               }
+               msleep(1);
+       }
+       snd_printk(KERN_WARNING LXP
+                  "ethersound could not be initialized after %dms\n", i);
+       return -ETIMEDOUT;
+
+ ethersound_initialized:
+       snd_printd(LXP "ethersound initialized\n");
+       return 0;
+}
+
+static int __devinit lx_init_get_version_features(struct lx6464es *chip)
+{
+       u32 dsp_version;
+
+       int err;
+
+       snd_printdd("->lx_init_get_version_features\n");
+
+       err = lx_dsp_get_version(chip, &dsp_version);
+
+       if (err == 0) {
+               u32 freq;
+
+               snd_printk(LXP "DSP version: V%02d.%02d #%d\n",
+                          (dsp_version>>16) & 0xff, (dsp_version>>8) & 0xff,
+                          dsp_version & 0xff);
+
+               /* later: what firmware version do we expect? */
+
+               /* retrieve Play/Rec features */
+               /* done here because we may have to handle alternate
+                * DSP files. */
+               /* later */
+
+               /* init the EtherSound sample rate */
+               err = lx_dsp_get_clock_frequency(chip, &freq);
+               if (err == 0)
+                       chip->board_sample_rate = freq;
+               snd_printd(LXP "actual clock frequency %d\n", freq);
+       } else {
+               snd_printk(KERN_ERR LXP "DSP corrupted \n");
+               err = -EAGAIN;
+       }
+
+       return err;
+}
+
+static int lx_set_granularity(struct lx6464es *chip, u32 gran)
+{
+       int err = 0;
+       u32 snapped_gran = MICROBLAZE_IBL_MIN;
+
+       snd_printdd("->lx_set_granularity\n");
+
+       /* blocksize is a power of 2 */
+       while ((snapped_gran < gran) &&
+              (snapped_gran < MICROBLAZE_IBL_MAX)) {
+               snapped_gran *= 2;
+       }
+
+       if (snapped_gran == chip->pcm_granularity)
+               return 0;
+
+       err = lx_dsp_set_granularity(chip, snapped_gran);
+       if (err < 0) {
+               snd_printk(KERN_WARNING LXP "could not set granularity\n");
+               err = -EAGAIN;
+       }
+
+       if (snapped_gran != gran)
+               snd_printk(LXP "snapped blocksize to %d\n", snapped_gran);
+
+       snd_printd(LXP "set blocksize on board %d\n", snapped_gran);
+       chip->pcm_granularity = snapped_gran;
+
+       return err;
+}
+
+/* initialize and test the xilinx dsp chip */
+static int __devinit lx_init_dsp(struct lx6464es *chip)
+{
+       int err;
+       u8 mac_address[6];
+       int i;
+
+       snd_printdd("->lx_init_dsp\n");
+
+       snd_printd(LXP "initialize board\n");
+       err = lx_init_xilinx_reset(chip);
+       if (err)
+               return err;
+
+       snd_printd(LXP "testing board\n");
+       err = lx_init_xilinx_test(chip);
+       if (err)
+               return err;
+
+       snd_printd(LXP "initialize ethersound configuration\n");
+       err = lx_init_ethersound_config(chip);
+       if (err)
+               return err;
+
+       lx_irq_enable(chip);
+
+       /** \todo the mac address should be ready by not, but it isn't,
+        *  so we wait for it */
+       for (i = 0; i != 1000; ++i) {
+               err = lx_dsp_get_mac(chip, mac_address);
+               if (err)
+                       return err;
+               if (mac_address[0] || mac_address[1] || mac_address[2] ||
+                   mac_address[3] || mac_address[4] || mac_address[5])
+                       goto mac_ready;
+               msleep(1);
+       }
+       return -ETIMEDOUT;
+
+mac_ready:
+       snd_printd(LXP "mac address ready read after: %dms\n", i);
+       snd_printk(LXP "mac address: %02X.%02X.%02X.%02X.%02X.%02X\n",
+                  mac_address[0], mac_address[1], mac_address[2],
+                  mac_address[3], mac_address[4], mac_address[5]);
+
+       err = lx_init_get_version_features(chip);
+       if (err)
+               return err;
+
+       lx_set_granularity(chip, MICROBLAZE_IBL_DEFAULT);
+
+       chip->playback_mute = 0;
+
+       return err;
+}
+
+static struct snd_pcm_ops lx_ops_playback = {
+       .open      = lx_pcm_open,
+       .close     = lx_pcm_close,
+       .ioctl     = snd_pcm_lib_ioctl,
+       .prepare   = lx_pcm_prepare,
+       .hw_params = lx_pcm_hw_params_playback,
+       .hw_free   = lx_pcm_hw_free,
+       .trigger   = lx_pcm_trigger,
+       .pointer   = lx_pcm_stream_pointer,
+};
+
+static struct snd_pcm_ops lx_ops_capture = {
+       .open      = lx_pcm_open,
+       .close     = lx_pcm_close,
+       .ioctl     = snd_pcm_lib_ioctl,
+       .prepare   = lx_pcm_prepare,
+       .hw_params = lx_pcm_hw_params_capture,
+       .hw_free   = lx_pcm_hw_free,
+       .trigger   = lx_pcm_trigger,
+       .pointer   = lx_pcm_stream_pointer,
+};
+
+static int __devinit lx_pcm_create(struct lx6464es *chip)
+{
+       int err;
+       struct snd_pcm *pcm;
+
+       u32 size = 64 *              /* channels */
+               3 *                  /* 24 bit samples */
+               MAX_STREAM_BUFFER *  /* periods */
+               MICROBLAZE_IBL_MAX * /* frames per period */
+               2;                   /* duplex */
+
+       size = PAGE_ALIGN(size);
+
+       /* hardcoded device name & channel count */
+       err = snd_pcm_new(chip->card, (char *)card_name, 0,
+                         1, 1, &pcm);
+
+       pcm->private_data = chip;
+
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &lx_ops_playback);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &lx_ops_capture);
+
+       pcm->info_flags = 0;
+       strcpy(pcm->name, card_name);
+
+       err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+                                                   snd_dma_pci_data(chip->pci),
+                                                   size, size);
+       if (err < 0)
+               return err;
+
+       chip->pcm = pcm;
+       chip->capture_stream.is_capture = 1;
+
+       return 0;
+}
+
+static int lx_control_playback_info(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_info *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 lx_control_playback_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct lx6464es *chip = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.integer.value[0] = chip->playback_mute;
+       return 0;
+}
+
+static int lx_control_playback_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct lx6464es *chip = snd_kcontrol_chip(kcontrol);
+       int changed = 0;
+       int current_value = chip->playback_mute;
+
+       if (current_value != ucontrol->value.integer.value[0]) {
+               lx_level_unmute(chip, 0, !current_value);
+               chip->playback_mute = !current_value;
+               changed = 1;
+       }
+       return changed;
+}
+
+static struct snd_kcontrol_new lx_control_playback_switch __devinitdata = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "PCM Playback Switch",
+       .index = 0,
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .private_value = 0,
+       .info = lx_control_playback_info,
+       .get = lx_control_playback_get,
+       .put = lx_control_playback_put
+};
+
+
+
+static void lx_proc_levels_read(struct snd_info_entry *entry,
+                               struct snd_info_buffer *buffer)
+{
+       u32 levels[64];
+       int err;
+       int i, j;
+       struct lx6464es *chip = entry->private_data;
+
+       snd_iprintf(buffer, "capture levels:\n");
+       err = lx_level_peaks(chip, 1, 64, levels);
+       if (err < 0)
+               return;
+
+       for (i = 0; i != 8; ++i) {
+               for (j = 0; j != 8; ++j)
+                       snd_iprintf(buffer, "%08x ", levels[i*8+j]);
+               snd_iprintf(buffer, "\n");
+       }
+
+       snd_iprintf(buffer, "\nplayback levels:\n");
+
+       err = lx_level_peaks(chip, 0, 64, levels);
+       if (err < 0)
+               return;
+
+       for (i = 0; i != 8; ++i) {
+               for (j = 0; j != 8; ++j)
+                       snd_iprintf(buffer, "%08x ", levels[i*8+j]);
+               snd_iprintf(buffer, "\n");
+       }
+
+       snd_iprintf(buffer, "\n");
+}
+
+static int __devinit lx_proc_create(struct snd_card *card, struct lx6464es *chip)
+{
+       struct snd_info_entry *entry;
+       int err = snd_card_proc_new(card, "levels", &entry);
+       if (err < 0)
+               return err;
+
+       snd_info_set_text_ops(entry, chip, lx_proc_levels_read);
+       return 0;
+}
+
+
+static int __devinit snd_lx6464es_create(struct snd_card *card,
+                                        struct pci_dev *pci,
+                                        struct lx6464es **rchip)
+{
+       struct lx6464es *chip;
+       int err;
+
+       static struct snd_device_ops ops = {
+               .dev_free = snd_lx6464es_dev_free,
+       };
+
+       snd_printdd("->snd_lx6464es_create\n");
+
+       *rchip = NULL;
+
+       /* enable PCI device */
+       err = pci_enable_device(pci);
+       if (err < 0)
+               return err;
+
+       pci_set_master(pci);
+
+       /* check if we can restrict PCI DMA transfers to 32 bits */
+       err = pci_set_dma_mask(pci, DMA_32BIT_MASK);
+       if (err < 0) {
+               snd_printk(KERN_ERR "architecture does not support "
+                          "32bit PCI busmaster DMA\n");
+               pci_disable_device(pci);
+               return -ENXIO;
+       }
+
+       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+       if (chip == NULL) {
+               err = -ENOMEM;
+               goto alloc_failed;
+       }
+
+       chip->card = card;
+       chip->pci = pci;
+       chip->irq = -1;
+
+       /* initialize synchronization structs */
+       spin_lock_init(&chip->lock);
+       spin_lock_init(&chip->msg_lock);
+       mutex_init(&chip->setup_mutex);
+       tasklet_init(&chip->trigger_tasklet, lx_trigger_tasklet,
+                    (unsigned long)chip);
+       tasklet_init(&chip->tasklet_capture, lx_tasklet_capture,
+                    (unsigned long)chip);
+       tasklet_init(&chip->tasklet_playback, lx_tasklet_playback,
+                    (unsigned long)chip);
+
+       /* request resources */
+       err = pci_request_regions(pci, card_name);
+       if (err < 0)
+               goto request_regions_failed;
+
+       /* plx port */
+       chip->port_plx = pci_resource_start(pci, 1);
+       chip->port_plx_remapped = ioport_map(chip->port_plx,
+                                            pci_resource_len(pci, 1));
+
+       /* dsp port */
+       chip->port_dsp_bar = pci_ioremap_bar(pci, 2);
+
+       err = request_irq(pci->irq, lx_interrupt, IRQF_SHARED,
+                         card_name, chip);
+       if (err) {
+               snd_printk(KERN_ERR LXP "unable to grab IRQ %d\n", pci->irq);
+               goto request_irq_failed;
+       }
+       chip->irq = pci->irq;
+
+       err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+       if (err < 0)
+               goto device_new_failed;
+
+       err = lx_init_dsp(chip);
+       if (err < 0) {
+               snd_printk(KERN_ERR LXP "error during DSP initialization\n");
+               return err;
+       }
+
+       err = lx_pcm_create(chip);
+       if (err < 0)
+               return err;
+
+       err = lx_proc_create(card, chip);
+       if (err < 0)
+               return err;
+
+       err = snd_ctl_add(card, snd_ctl_new1(&lx_control_playback_switch,
+                                            chip));
+       if (err < 0)
+               return err;
+
+       snd_card_set_dev(card, &pci->dev);
+
+       *rchip = chip;
+       return 0;
+
+device_new_failed:
+       free_irq(pci->irq, chip);
+
+request_irq_failed:
+       pci_release_regions(pci);
+
+request_regions_failed:
+       kfree(chip);
+
+alloc_failed:
+       pci_disable_device(pci);
+
+       return err;
+}
+
+static int __devinit snd_lx6464es_probe(struct pci_dev *pci,
+                                       const struct pci_device_id *pci_id)
+{
+       static int dev;
+       struct snd_card *card;
+       struct lx6464es *chip;
+       int err;
+
+       snd_printdd("->snd_lx6464es_probe\n");
+
+       if (dev >= SNDRV_CARDS)
+               return -ENODEV;
+       if (!enable[dev]) {
+               dev++;
+               return -ENOENT;
+       }
+
+       err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+       if (err < 0)
+               return err;
+
+       err = snd_lx6464es_create(card, pci, &chip);
+       if (err < 0) {
+               snd_printk(KERN_ERR LXP "error during snd_lx6464es_create\n");
+               goto out_free;
+       }
+
+       strcpy(card->driver, "lx6464es");
+       strcpy(card->shortname, "Digigram LX6464ES");
+       sprintf(card->longname, "%s at 0x%lx, 0x%p, irq %i",
+               card->shortname, chip->port_plx,
+               chip->port_dsp_bar, chip->irq);
+
+       err = snd_card_register(card);
+       if (err < 0)
+               goto out_free;
+
+       snd_printdd(LXP "initialization successful\n");
+       pci_set_drvdata(pci, card);
+       dev++;
+       return 0;
+
+out_free:
+       snd_card_free(card);
+       return err;
+
+}
+
+static void __devexit snd_lx6464es_remove(struct pci_dev *pci)
+{
+       snd_card_free(pci_get_drvdata(pci));
+       pci_set_drvdata(pci, NULL);
+}
+
+
+static struct pci_driver driver = {
+       .name =     "Digigram LX6464ES",
+       .id_table = snd_lx6464es_ids,
+       .probe =    snd_lx6464es_probe,
+       .remove = __devexit_p(snd_lx6464es_remove),
+};
+
+
+/* module initialization */
+static int __init mod_init(void)
+{
+       return pci_register_driver(&driver);
+}
+
+static void __exit mod_exit(void)
+{
+       pci_unregister_driver(&driver);
+}
+
+module_init(mod_init);
+module_exit(mod_exit);
diff --git a/sound/pci/lx6464es/lx6464es.h b/sound/pci/lx6464es/lx6464es.h
new file mode 100644 (file)
index 0000000..012c010
--- /dev/null
@@ -0,0 +1,114 @@
+/* -*- linux-c -*- *
+ *
+ * ALSA driver for the digigram lx6464es interface
+ *
+ * Copyright (c) 2009 Tim Blechmann <tim@klingt.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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef LX6464ES_H
+#define LX6464ES_H
+
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include "lx_core.h"
+
+#define LXP "LX6464ES: "
+
+enum {
+    ES_cmd_free         = 0,    /* no command executing */
+    ES_cmd_processing   = 1,   /* execution of a read/write command */
+    ES_read_pending     = 2,    /* a asynchron read command is pending */
+    ES_read_finishing   = 3,    /* a read command has finished waiting (set by
+                                * Interrupt or CancelIrp) */
+};
+
+enum lx_stream_status {
+       LX_STREAM_STATUS_FREE,
+/*     LX_STREAM_STATUS_OPEN, */
+       LX_STREAM_STATUS_SCHEDULE_RUN,
+/*     LX_STREAM_STATUS_STARTED, */
+       LX_STREAM_STATUS_RUNNING,
+       LX_STREAM_STATUS_SCHEDULE_STOP,
+/*     LX_STREAM_STATUS_STOPPED, */
+/*     LX_STREAM_STATUS_PAUSED */
+};
+
+
+struct lx_stream {
+       struct snd_pcm_substream  *stream;
+       snd_pcm_uframes_t          frame_pos;
+       enum lx_stream_status      status; /* free, open, running, draining
+                                           * pause */
+       int                        is_capture:1;
+};
+
+
+struct lx6464es {
+       struct snd_card        *card;
+       struct pci_dev         *pci;
+       int                     irq;
+
+       spinlock_t              lock;        /* interrupt spinlock */
+       struct mutex            setup_mutex; /* mutex used in hw_params, open
+                                             * and close */
+
+       struct tasklet_struct   trigger_tasklet; /* trigger tasklet */
+       struct tasklet_struct   tasklet_capture;
+       struct tasklet_struct   tasklet_playback;
+
+       /* ports */
+       unsigned long           port_plx;          /* io port (size=256) */
+       void __iomem           *port_plx_remapped; /* remapped plx port */
+       void __iomem           *port_dsp_bar;      /* memory port (32-bit,
+                                                   * non-prefetchable,
+                                                   * size=8K) */
+
+       /* messaging */
+       spinlock_t              msg_lock;          /* message spinlock */
+       atomic_t                send_message_locked;
+       struct lx_rmh           rmh;
+
+       /* configuration */
+       uint                    freq_ratio : 2;
+       uint                    playback_mute : 1;
+       uint                    hardware_running[2];
+       u32                     board_sample_rate; /* sample rate read from
+                                                   * board */
+       u32                     sample_rate;       /* our sample rate */
+       u16                     pcm_granularity;   /* board blocksize */
+
+       /* dma */
+       struct snd_dma_buffer   capture_dma_buf;
+       struct snd_dma_buffer   playback_dma_buf;
+
+       /* pcm */
+       struct snd_pcm         *pcm;
+
+       /* streams */
+       struct lx_stream        capture_stream;
+       struct lx_stream        playback_stream;
+};
+
+
+#endif /* LX6464ES_H */
diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c
new file mode 100644 (file)
index 0000000..5812780
--- /dev/null
@@ -0,0 +1,1444 @@
+/* -*- linux-c -*- *
+ *
+ * ALSA driver for the digigram lx6464es interface
+ * low-level interface
+ *
+ * Copyright (c) 2009 Tim Blechmann <tim@klingt.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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+/* #define RMH_DEBUG 1 */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include "lx6464es.h"
+#include "lx_core.h"
+
+/* low-level register access */
+
+static const unsigned long dsp_port_offsets[] = {
+       0,
+       0x400,
+       0x401,
+       0x402,
+       0x403,
+       0x404,
+       0x405,
+       0x406,
+       0x407,
+       0x408,
+       0x409,
+       0x40a,
+       0x40b,
+       0x40c,
+
+       0x410,
+       0x411,
+       0x412,
+       0x413,
+       0x414,
+       0x415,
+       0x416,
+
+       0x420,
+       0x430,
+       0x431,
+       0x432,
+       0x433,
+       0x434,
+       0x440
+};
+
+static void __iomem *lx_dsp_register(struct lx6464es *chip, int port)
+{
+       void __iomem *base_address = chip->port_dsp_bar;
+       return base_address + dsp_port_offsets[port]*4;
+}
+
+unsigned long lx_dsp_reg_read(struct lx6464es *chip, int port)
+{
+       void __iomem *address = lx_dsp_register(chip, port);
+       return ioread32(address);
+}
+
+void lx_dsp_reg_readbuf(struct lx6464es *chip, int port, u32 *data, u32 len)
+{
+       void __iomem *address = lx_dsp_register(chip, port);
+       memcpy_fromio(data, address, len*sizeof(u32));
+}
+
+
+void lx_dsp_reg_write(struct lx6464es *chip, int port, unsigned data)
+{
+       void __iomem *address = lx_dsp_register(chip, port);
+       iowrite32(data, address);
+}
+
+void lx_dsp_reg_writebuf(struct lx6464es *chip, int port, const u32 *data,
+                        u32 len)
+{
+       void __iomem *address = lx_dsp_register(chip, port);
+       memcpy_toio(address, data, len*sizeof(u32));
+}
+
+
+static const unsigned long plx_port_offsets[] = {
+       0x04,
+       0x40,
+       0x44,
+       0x48,
+       0x4c,
+       0x50,
+       0x54,
+       0x58,
+       0x5c,
+       0x64,
+       0x68,
+       0x6C
+};
+
+static void __iomem *lx_plx_register(struct lx6464es *chip, int port)
+{
+       void __iomem *base_address = chip->port_plx_remapped;
+       return base_address + plx_port_offsets[port];
+}
+
+unsigned long lx_plx_reg_read(struct lx6464es *chip, int port)
+{
+       void __iomem *address = lx_plx_register(chip, port);
+       return ioread32(address);
+}
+
+void lx_plx_reg_write(struct lx6464es *chip, int port, u32 data)
+{
+       void __iomem *address = lx_plx_register(chip, port);
+       iowrite32(data, address);
+}
+
+u32 lx_plx_mbox_read(struct lx6464es *chip, int mbox_nr)
+{
+       int index;
+
+       switch (mbox_nr) {
+       case 1:
+               index = ePLX_MBOX1;    break;
+       case 2:
+               index = ePLX_MBOX2;    break;
+       case 3:
+               index = ePLX_MBOX3;    break;
+       case 4:
+               index = ePLX_MBOX4;    break;
+       case 5:
+               index = ePLX_MBOX5;    break;
+       case 6:
+               index = ePLX_MBOX6;    break;
+       case 7:
+               index = ePLX_MBOX7;    break;
+       case 0:                 /* reserved for HF flags */
+               snd_BUG();
+       default:
+               return 0xdeadbeef;
+       }
+
+       return lx_plx_reg_read(chip, index);
+}
+
+int lx_plx_mbox_write(struct lx6464es *chip, int mbox_nr, u32 value)
+{
+       int index = -1;
+
+       switch (mbox_nr) {
+       case 1:
+               index = ePLX_MBOX1;    break;
+       case 3:
+               index = ePLX_MBOX3;    break;
+       case 4:
+               index = ePLX_MBOX4;    break;
+       case 5:
+               index = ePLX_MBOX5;    break;
+       case 6:
+               index = ePLX_MBOX6;    break;
+       case 7:
+               index = ePLX_MBOX7;    break;
+       case 0:                 /* reserved for HF flags */
+       case 2:                 /* reserved for Pipe States
+                                * the DSP keeps an image of it */
+               snd_BUG();
+               return -EBADRQC;
+       }
+
+       lx_plx_reg_write(chip, index, value);
+       return 0;
+}
+
+
+/* rmh */
+
+#ifdef CONFIG_SND_DEBUG
+#define CMD_NAME(a) a
+#else
+#define CMD_NAME(a) NULL
+#endif
+
+#define Reg_CSM_MR                     0x00000002
+#define Reg_CSM_MC                     0x00000001
+
+struct dsp_cmd_info {
+       u32    dcCodeOp;        /* Op Code of the command (usually 1st 24-bits
+                                * word).*/
+       u16    dcCmdLength;     /* Command length in words of 24 bits.*/
+       u16    dcStatusType;    /* Status type: 0 for fixed length, 1 for
+                                * random. */
+       u16    dcStatusLength;  /* Status length (if fixed).*/
+       char  *dcOpName;
+};
+
+/*
+  Initialization and control data for the Microblaze interface
+  - OpCode:
+    the opcode field of the command set at the proper offset
+  - CmdLength
+    the number of command words
+  - StatusType
+    offset in the status registers: 0 means that the return value may be
+    different from 0, and must be read
+  - StatusLength
+    the number of status words (in addition to the return value)
+*/
+
+static struct dsp_cmd_info dsp_commands[] =
+{
+       { (CMD_00_INFO_DEBUG << OPCODE_OFFSET)                  , 1 /*custom*/
+         , 1   , 0 /**/                    , CMD_NAME("INFO_DEBUG") },
+       { (CMD_01_GET_SYS_CFG << OPCODE_OFFSET)                 , 1 /**/
+         , 1      , 2 /**/                 , CMD_NAME("GET_SYS_CFG") },
+       { (CMD_02_SET_GRANULARITY << OPCODE_OFFSET)             , 1 /**/
+         , 1      , 0 /**/                 , CMD_NAME("SET_GRANULARITY") },
+       { (CMD_03_SET_TIMER_IRQ << OPCODE_OFFSET)               , 1 /**/
+         , 1      , 0 /**/                 , CMD_NAME("SET_TIMER_IRQ") },
+       { (CMD_04_GET_EVENT << OPCODE_OFFSET)                   , 1 /**/
+         , 1      , 0 /*up to 10*/     , CMD_NAME("GET_EVENT") },
+       { (CMD_05_GET_PIPES << OPCODE_OFFSET)                   , 1 /**/
+         , 1      , 2 /*up to 4*/      , CMD_NAME("GET_PIPES") },
+       { (CMD_06_ALLOCATE_PIPE << OPCODE_OFFSET)               , 1 /**/
+         , 0      , 0 /**/                 , CMD_NAME("ALLOCATE_PIPE") },
+       { (CMD_07_RELEASE_PIPE << OPCODE_OFFSET)                , 1 /**/
+         , 0      , 0 /**/                 , CMD_NAME("RELEASE_PIPE") },
+       { (CMD_08_ASK_BUFFERS << OPCODE_OFFSET)                 , 1 /**/
+         , 1      , MAX_STREAM_BUFFER  , CMD_NAME("ASK_BUFFERS") },
+       { (CMD_09_STOP_PIPE << OPCODE_OFFSET)                   , 1 /**/
+         , 0      , 0 /*up to 2*/      , CMD_NAME("STOP_PIPE") },
+       { (CMD_0A_GET_PIPE_SPL_COUNT << OPCODE_OFFSET)          , 1 /**/
+         , 1      , 1 /*up to 2*/      , CMD_NAME("GET_PIPE_SPL_COUNT") },
+       { (CMD_0B_TOGGLE_PIPE_STATE << OPCODE_OFFSET)           , 1 /*up to 5*/
+         , 1      , 0 /**/                 , CMD_NAME("TOGGLE_PIPE_STATE") },
+       { (CMD_0C_DEF_STREAM << OPCODE_OFFSET)                  , 1 /*up to 4*/
+         , 1      , 0 /**/                 , CMD_NAME("DEF_STREAM") },
+       { (CMD_0D_SET_MUTE  << OPCODE_OFFSET)                   , 3 /**/
+         , 1      , 0 /**/                 , CMD_NAME("SET_MUTE") },
+       { (CMD_0E_GET_STREAM_SPL_COUNT << OPCODE_OFFSET)        , 1/**/
+         , 1      , 2 /**/                 , CMD_NAME("GET_STREAM_SPL_COUNT") },
+       { (CMD_0F_UPDATE_BUFFER << OPCODE_OFFSET)               , 3 /*up to 4*/
+         , 0      , 1 /**/                 , CMD_NAME("UPDATE_BUFFER") },
+       { (CMD_10_GET_BUFFER << OPCODE_OFFSET)                  , 1 /**/
+         , 1      , 4 /**/                 , CMD_NAME("GET_BUFFER") },
+       { (CMD_11_CANCEL_BUFFER << OPCODE_OFFSET)               , 1 /**/
+         , 1      , 1 /*up to 4*/      , CMD_NAME("CANCEL_BUFFER") },
+       { (CMD_12_GET_PEAK << OPCODE_OFFSET)                    , 1 /**/
+         , 1      , 1 /**/                 , CMD_NAME("GET_PEAK") },
+       { (CMD_13_SET_STREAM_STATE << OPCODE_OFFSET)            , 1 /**/
+         , 1      , 0 /**/                 , CMD_NAME("SET_STREAM_STATE") },
+};
+
+static void lx_message_init(struct lx_rmh *rmh, enum cmd_mb_opcodes cmd)
+{
+       snd_BUG_ON(cmd >= CMD_14_INVALID);
+
+       rmh->cmd[0] = dsp_commands[cmd].dcCodeOp;
+       rmh->cmd_len = dsp_commands[cmd].dcCmdLength;
+       rmh->stat_len = dsp_commands[cmd].dcStatusLength;
+       rmh->dsp_stat = dsp_commands[cmd].dcStatusType;
+       rmh->cmd_idx = cmd;
+       memset(&rmh->cmd[1], 0, (REG_CRM_NUMBER - 1) * sizeof(u32));
+
+#ifdef CONFIG_SND_DEBUG
+       memset(rmh->stat, 0, REG_CRM_NUMBER * sizeof(u32));
+#endif
+#ifdef RMH_DEBUG
+       rmh->cmd_idx = cmd;
+#endif
+}
+
+#ifdef RMH_DEBUG
+#define LXRMH "lx6464es rmh: "
+static void lx_message_dump(struct lx_rmh *rmh)
+{
+       u8 idx = rmh->cmd_idx;
+       int i;
+
+       snd_printk(LXRMH "command %s\n", dsp_commands[idx].dcOpName);
+
+       for (i = 0; i != rmh->cmd_len; ++i)
+               snd_printk(LXRMH "\tcmd[%d] %08x\n", i, rmh->cmd[i]);
+
+       for (i = 0; i != rmh->stat_len; ++i)
+               snd_printk(LXRMH "\tstat[%d]: %08x\n", i, rmh->stat[i]);
+       snd_printk("\n");
+}
+#else
+static inline void lx_message_dump(struct lx_rmh *rmh)
+{}
+#endif
+
+
+
+/* sleep 500 - 100 = 400 times 100us -> the timeout is >= 40 ms */
+#define XILINX_TIMEOUT_MS       40
+#define XILINX_POLL_NO_SLEEP    100
+#define XILINX_POLL_ITERATIONS  150
+
+#if 0 /* not used now */
+static int lx_message_send(struct lx6464es *chip, struct lx_rmh *rmh)
+{
+       u32 reg = ED_DSP_TIMED_OUT;
+       int dwloop;
+       int answer_received;
+
+       if (lx_dsp_reg_read(chip, eReg_CSM) & (Reg_CSM_MC | Reg_CSM_MR)) {
+               snd_printk(KERN_ERR LXP "PIOSendMessage eReg_CSM %x\n", reg);
+               return -EBUSY;
+       }
+
+       /* write command */
+       lx_dsp_reg_writebuf(chip, eReg_CRM1, rmh->cmd, rmh->cmd_len);
+
+       snd_BUG_ON(atomic_read(&chip->send_message_locked) != 0);
+       atomic_set(&chip->send_message_locked, 1);
+
+       /* MicoBlaze gogogo */
+       lx_dsp_reg_write(chip, eReg_CSM, Reg_CSM_MC);
+
+       /* wait for interrupt to answer */
+       for (dwloop = 0; dwloop != XILINX_TIMEOUT_MS; ++dwloop) {
+               answer_received = atomic_read(&chip->send_message_locked);
+               if (answer_received == 0)
+                       break;
+               msleep(1);
+       }
+
+       if (answer_received == 0) {
+               /* in Debug mode verify Reg_CSM_MR */
+               snd_BUG_ON(!(lx_dsp_reg_read(chip, eReg_CSM) & Reg_CSM_MR));
+
+               /* command finished, read status */
+               if (rmh->dsp_stat == 0)
+                       reg = lx_dsp_reg_read(chip, eReg_CRM1);
+               else
+                       reg = 0;
+       } else {
+               int i;
+               snd_printk(KERN_WARNING LXP "TIMEOUT lx_message_send! "
+                          "Interrupts disabled?\n");
+
+               /* attente bit Reg_CSM_MR */
+               for (i = 0; i != XILINX_POLL_ITERATIONS; i++) {
+                       if ((lx_dsp_reg_read(chip, eReg_CSM) & Reg_CSM_MR)) {
+                               if (rmh->dsp_stat == 0)
+                                       reg = lx_dsp_reg_read(chip, eReg_CRM1);
+                               else
+                                       reg = 0;
+                               goto polling_successful;
+                       }
+
+                       if (i > XILINX_POLL_NO_SLEEP)
+                               msleep(1);
+               }
+               snd_printk(KERN_WARNING LXP "TIMEOUT lx_message_send! "
+                          "polling failed\n");
+
+polling_successful:
+               atomic_set(&chip->send_message_locked, 0);
+       }
+
+       if ((reg & ERROR_VALUE) == 0) {
+               /* read response */
+               if (rmh->stat_len) {
+                       snd_BUG_ON(rmh->stat_len >= (REG_CRM_NUMBER-1));
+
+                       lx_dsp_reg_readbuf(chip, eReg_CRM2, rmh->stat,
+                                          rmh->stat_len);
+               }
+       } else
+               snd_printk(KERN_WARNING LXP "lx_message_send: error_value %x\n",
+                          reg);
+
+       /* clear Reg_CSM_MR */
+       lx_dsp_reg_write(chip, eReg_CSM, 0);
+
+       switch (reg) {
+       case ED_DSP_TIMED_OUT:
+               snd_printk(KERN_WARNING LXP "lx_message_send: dsp timeout\n");
+               return -ETIMEDOUT;
+
+       case ED_DSP_CRASHED:
+               snd_printk(KERN_WARNING LXP "lx_message_send: dsp crashed\n");
+               return -EAGAIN;
+       }
+
+       lx_message_dump(rmh);
+       return 0;
+}
+#endif /* not used now */
+
+static int lx_message_send_atomic(struct lx6464es *chip, struct lx_rmh *rmh)
+{
+       u32 reg = ED_DSP_TIMED_OUT;
+       int dwloop;
+
+       if (lx_dsp_reg_read(chip, eReg_CSM) & (Reg_CSM_MC | Reg_CSM_MR)) {
+               snd_printk(KERN_ERR LXP "PIOSendMessage eReg_CSM %x\n", reg);
+               return -EBUSY;
+       }
+
+       /* write command */
+       lx_dsp_reg_writebuf(chip, eReg_CRM1, rmh->cmd, rmh->cmd_len);
+
+       /* MicoBlaze gogogo */
+       lx_dsp_reg_write(chip, eReg_CSM, Reg_CSM_MC);
+
+       /* wait for interrupt to answer */
+       for (dwloop = 0; dwloop != XILINX_TIMEOUT_MS * 1000; ++dwloop) {
+               if (lx_dsp_reg_read(chip, eReg_CSM) & Reg_CSM_MR) {
+                       if (rmh->dsp_stat == 0)
+                               reg = lx_dsp_reg_read(chip, eReg_CRM1);
+                       else
+                               reg = 0;
+                       goto polling_successful;
+               } else
+                       udelay(1);
+       }
+       snd_printk(KERN_WARNING LXP "TIMEOUT lx_message_send_atomic! "
+                  "polling failed\n");
+
+polling_successful:
+       if ((reg & ERROR_VALUE) == 0) {
+               /* read response */
+               if (rmh->stat_len) {
+                       snd_BUG_ON(rmh->stat_len >= (REG_CRM_NUMBER-1));
+                       lx_dsp_reg_readbuf(chip, eReg_CRM2, rmh->stat,
+                                          rmh->stat_len);
+               }
+       } else
+               snd_printk(LXP "rmh error: %08x\n", reg);
+
+       /* clear Reg_CSM_MR */
+       lx_dsp_reg_write(chip, eReg_CSM, 0);
+
+       switch (reg) {
+       case ED_DSP_TIMED_OUT:
+               snd_printk(KERN_WARNING LXP "lx_message_send: dsp timeout\n");
+               return -ETIMEDOUT;
+
+       case ED_DSP_CRASHED:
+               snd_printk(KERN_WARNING LXP "lx_message_send: dsp crashed\n");
+               return -EAGAIN;
+       }
+
+       lx_message_dump(rmh);
+
+       return reg;
+}
+
+
+/* low-level dsp access */
+int __devinit lx_dsp_get_version(struct lx6464es *chip, u32 *rdsp_version)
+{
+       u16 ret;
+       unsigned long flags;
+
+       spin_lock_irqsave(&chip->msg_lock, flags);
+
+       lx_message_init(&chip->rmh, CMD_01_GET_SYS_CFG);
+       ret = lx_message_send_atomic(chip, &chip->rmh);
+
+       *rdsp_version = chip->rmh.stat[1];
+       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       return ret;
+}
+
+int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq)
+{
+       u16 ret = 0;
+       unsigned long flags;
+       u32 freq_raw = 0;
+       u32 freq = 0;
+       u32 frequency = 0;
+
+       spin_lock_irqsave(&chip->msg_lock, flags);
+
+       lx_message_init(&chip->rmh, CMD_01_GET_SYS_CFG);
+       ret = lx_message_send_atomic(chip, &chip->rmh);
+
+       if (ret == 0) {
+               freq_raw = chip->rmh.stat[0] >> FREQ_FIELD_OFFSET;
+               freq = freq_raw & XES_FREQ_COUNT8_MASK;
+
+               if ((freq < XES_FREQ_COUNT8_48_MAX) ||
+                   (freq > XES_FREQ_COUNT8_44_MIN))
+                       frequency = 0; /* unknown */
+               else if (freq >= XES_FREQ_COUNT8_44_MAX)
+                       frequency = 44100;
+               else
+                       frequency = 48000;
+       }
+
+       spin_unlock_irqrestore(&chip->msg_lock, flags);
+
+       *rfreq = frequency * chip->freq_ratio;
+
+       return ret;
+}
+
+int lx_dsp_get_mac(struct lx6464es *chip, u8 *mac_address)
+{
+       u32 macmsb, maclsb;
+
+       macmsb = lx_dsp_reg_read(chip, eReg_ADMACESMSB) & 0x00FFFFFF;
+       maclsb = lx_dsp_reg_read(chip, eReg_ADMACESLSB) & 0x00FFFFFF;
+
+       /* todo: endianess handling */
+       mac_address[5] = ((u8 *)(&maclsb))[0];
+       mac_address[4] = ((u8 *)(&maclsb))[1];
+       mac_address[3] = ((u8 *)(&maclsb))[2];
+       mac_address[2] = ((u8 *)(&macmsb))[0];
+       mac_address[1] = ((u8 *)(&macmsb))[1];
+       mac_address[0] = ((u8 *)(&macmsb))[2];
+
+       return 0;
+}
+
+
+int lx_dsp_set_granularity(struct lx6464es *chip, u32 gran)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&chip->msg_lock, flags);
+
+       lx_message_init(&chip->rmh, CMD_02_SET_GRANULARITY);
+       chip->rmh.cmd[0] |= gran;
+
+       ret = lx_message_send_atomic(chip, &chip->rmh);
+       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       return ret;
+}
+
+int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&chip->msg_lock, flags);
+
+       lx_message_init(&chip->rmh, CMD_04_GET_EVENT);
+       chip->rmh.stat_len = 9; /* we don't necessarily need the full length */
+
+       ret = lx_message_send_atomic(chip, &chip->rmh);
+
+       if (!ret)
+               memcpy(data, chip->rmh.stat, chip->rmh.stat_len * sizeof(u32));
+
+       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       return ret;
+}
+
+#define CSES_TIMEOUT        100     /* microseconds */
+#define CSES_CE             0x0001
+#define CSES_BROADCAST      0x0002
+#define CSES_UPDATE_LDSV    0x0004
+
+int lx_dsp_es_check_pipeline(struct lx6464es *chip)
+{
+       int i;
+
+       for (i = 0; i != CSES_TIMEOUT; ++i) {
+               /*
+                * le bit CSES_UPDATE_LDSV est ÃƒÂ  1 dés que le macprog
+                * est pret. il re-passe ÃƒÂ  0 lorsque le premier read a
+                * ÃƒÂ©té fait. pour l'instant on retire le test car ce bit
+                * passe a 1 environ 200 ÃƒÂ  400 ms aprés que le registre
+                * confES ÃƒÂ  ÃƒÂ©té ÃƒÂ©crit (kick du xilinx ES).
+                *
+                * On ne teste que le bit CE.
+                * */
+
+               u32 cses = lx_dsp_reg_read(chip, eReg_CSES);
+
+               if ((cses & CSES_CE) == 0)
+                       return 0;
+
+               udelay(1);
+       }
+
+       return -ETIMEDOUT;
+}
+
+
+#define PIPE_INFO_TO_CMD(capture, pipe)                                        \
+       ((u32)((u32)(pipe) | ((capture) ? ID_IS_CAPTURE : 0L)) << ID_OFFSET)
+
+
+
+/* low-level pipe handling */
+int lx_pipe_allocate(struct lx6464es *chip, u32 pipe, int is_capture,
+                    int channels)
+{
+       int err;
+       unsigned long flags;
+
+       u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+       spin_lock_irqsave(&chip->msg_lock, flags);
+       lx_message_init(&chip->rmh, CMD_06_ALLOCATE_PIPE);
+
+       chip->rmh.cmd[0] |= pipe_cmd;
+       chip->rmh.cmd[0] |= channels;
+
+       err = lx_message_send_atomic(chip, &chip->rmh);
+       spin_unlock_irqrestore(&chip->msg_lock, flags);
+
+       if (err != 0)
+               snd_printk(KERN_ERR "lx6464es: could not allocate pipe\n");
+
+       return err;
+}
+
+int lx_pipe_release(struct lx6464es *chip, u32 pipe, int is_capture)
+{
+       int err;
+       unsigned long flags;
+
+       u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+       spin_lock_irqsave(&chip->msg_lock, flags);
+       lx_message_init(&chip->rmh, CMD_07_RELEASE_PIPE);
+
+       chip->rmh.cmd[0] |= pipe_cmd;
+
+       err = lx_message_send_atomic(chip, &chip->rmh);
+       spin_unlock_irqrestore(&chip->msg_lock, flags);
+
+       return err;
+}
+
+int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
+                 u32 *r_needed, u32 *r_freed, u32 *size_array)
+{
+       int err;
+       unsigned long flags;
+
+       u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+#ifdef CONFIG_SND_DEBUG
+       if (size_array)
+               memset(size_array, 0, sizeof(u32)*MAX_STREAM_BUFFER);
+#endif
+
+       *r_needed = 0;
+       *r_freed = 0;
+
+       spin_lock_irqsave(&chip->msg_lock, flags);
+       lx_message_init(&chip->rmh, CMD_08_ASK_BUFFERS);
+
+       chip->rmh.cmd[0] |= pipe_cmd;
+
+       err = lx_message_send_atomic(chip, &chip->rmh);
+
+       if (!err) {
+               int i;
+               for (i = 0; i < MAX_STREAM_BUFFER; ++i) {
+                       u32 stat = chip->rmh.stat[i];
+                       if (stat & (BF_EOB << BUFF_FLAGS_OFFSET)) {
+                               /* finished */
+                               *r_freed += 1;
+                               if (size_array)
+                                       size_array[i] = stat & MASK_DATA_SIZE;
+                       } else if ((stat & (BF_VALID << BUFF_FLAGS_OFFSET))
+                                  == 0)
+                               /* free */
+                               *r_needed += 1;
+               }
+
+#if 0
+               snd_printdd(LXP "CMD_08_ASK_BUFFERS: needed %d, freed %d\n",
+                           *r_needed, *r_freed);
+               for (i = 0; i < MAX_STREAM_BUFFER; ++i) {
+                       for (i = 0; i != chip->rmh.stat_len; ++i)
+                               snd_printdd("  stat[%d]: %x, %x\n", i,
+                                           chip->rmh.stat[i],
+                                           chip->rmh.stat[i] & MASK_DATA_SIZE);
+               }
+#endif
+       }
+
+       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       return err;
+}
+
+
+int lx_pipe_stop(struct lx6464es *chip, u32 pipe, int is_capture)
+{
+       int err;
+       unsigned long flags;
+
+       u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+       spin_lock_irqsave(&chip->msg_lock, flags);
+       lx_message_init(&chip->rmh, CMD_09_STOP_PIPE);
+
+       chip->rmh.cmd[0] |= pipe_cmd;
+
+       err = lx_message_send_atomic(chip, &chip->rmh);
+
+       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       return err;
+}
+
+static int lx_pipe_toggle_state(struct lx6464es *chip, u32 pipe, int is_capture)
+{
+       int err;
+       unsigned long flags;
+
+       u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+       spin_lock_irqsave(&chip->msg_lock, flags);
+       lx_message_init(&chip->rmh, CMD_0B_TOGGLE_PIPE_STATE);
+
+       chip->rmh.cmd[0] |= pipe_cmd;
+
+       err = lx_message_send_atomic(chip, &chip->rmh);
+
+       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       return err;
+}
+
+
+int lx_pipe_start(struct lx6464es *chip, u32 pipe, int is_capture)
+{
+       int err;
+
+       err = lx_pipe_wait_for_idle(chip, pipe, is_capture);
+       if (err < 0)
+               return err;
+
+       err = lx_pipe_toggle_state(chip, pipe, is_capture);
+
+       return err;
+}
+
+int lx_pipe_pause(struct lx6464es *chip, u32 pipe, int is_capture)
+{
+       int err = 0;
+
+       err = lx_pipe_wait_for_start(chip, pipe, is_capture);
+       if (err < 0)
+               return err;
+
+       err = lx_pipe_toggle_state(chip, pipe, is_capture);
+
+       return err;
+}
+
+
+int lx_pipe_sample_count(struct lx6464es *chip, u32 pipe, int is_capture,
+                        u64 *rsample_count)
+{
+       int err;
+       unsigned long flags;
+
+       u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+       spin_lock_irqsave(&chip->msg_lock, flags);
+       lx_message_init(&chip->rmh, CMD_0A_GET_PIPE_SPL_COUNT);
+
+       chip->rmh.cmd[0] |= pipe_cmd;
+       chip->rmh.stat_len = 2; /* need all words here! */
+
+       err = lx_message_send_atomic(chip, &chip->rmh); /* don't sleep! */
+
+       if (err != 0)
+               snd_printk(KERN_ERR
+                          "lx6464es: could not query pipe's sample count\n");
+       else {
+               *rsample_count = ((u64)(chip->rmh.stat[0] & MASK_SPL_COUNT_HI)
+                                 << 24)     /* hi part */
+                       + chip->rmh.stat[1]; /* lo part */
+       }
+
+       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       return err;
+}
+
+int lx_pipe_state(struct lx6464es *chip, u32 pipe, int is_capture, u16 *rstate)
+{
+       int err;
+       unsigned long flags;
+
+       u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+       spin_lock_irqsave(&chip->msg_lock, flags);
+       lx_message_init(&chip->rmh, CMD_0A_GET_PIPE_SPL_COUNT);
+
+       chip->rmh.cmd[0] |= pipe_cmd;
+
+       err = lx_message_send_atomic(chip, &chip->rmh);
+
+       if (err != 0)
+               snd_printk(KERN_ERR "lx6464es: could not query pipe's state\n");
+       else
+               *rstate = (chip->rmh.stat[0] >> PSTATE_OFFSET) & 0x0F;
+
+       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       return err;
+}
+
+static int lx_pipe_wait_for_state(struct lx6464es *chip, u32 pipe,
+                                 int is_capture, u16 state)
+{
+       int i;
+
+       /* max 2*PCMOnlyGranularity = 2*1024 at 44100 = < 50 ms:
+        * timeout 50 ms */
+       for (i = 0; i != 50; ++i) {
+               u16 current_state;
+               int err = lx_pipe_state(chip, pipe, is_capture, &current_state);
+
+               if (err < 0)
+                       return err;
+
+               if (current_state == state)
+                       return 0;
+
+               mdelay(1);
+       }
+
+       return -ETIMEDOUT;
+}
+
+int lx_pipe_wait_for_start(struct lx6464es *chip, u32 pipe, int is_capture)
+{
+       return lx_pipe_wait_for_state(chip, pipe, is_capture, PSTATE_RUN);
+}
+
+int lx_pipe_wait_for_idle(struct lx6464es *chip, u32 pipe, int is_capture)
+{
+       return lx_pipe_wait_for_state(chip, pipe, is_capture, PSTATE_IDLE);
+}
+
+/* low-level stream handling */
+int lx_stream_set_state(struct lx6464es *chip, u32 pipe,
+                              int is_capture, enum stream_state_t state)
+{
+       int err;
+       unsigned long flags;
+
+       u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+       spin_lock_irqsave(&chip->msg_lock, flags);
+       lx_message_init(&chip->rmh, CMD_13_SET_STREAM_STATE);
+
+       chip->rmh.cmd[0] |= pipe_cmd;
+       chip->rmh.cmd[0] |= state;
+
+       err = lx_message_send_atomic(chip, &chip->rmh);
+       spin_unlock_irqrestore(&chip->msg_lock, flags);
+
+       return err;
+}
+
+int lx_stream_set_format(struct lx6464es *chip, struct snd_pcm_runtime *runtime,
+                        u32 pipe, int is_capture)
+{
+       int err;
+       unsigned long flags;
+
+       u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+       u32 channels = runtime->channels;
+
+       if (runtime->channels != channels)
+               snd_printk(KERN_ERR LXP "channel count mismatch: %d vs %d",
+                          runtime->channels, channels);
+
+       spin_lock_irqsave(&chip->msg_lock, flags);
+       lx_message_init(&chip->rmh, CMD_0C_DEF_STREAM);
+
+       chip->rmh.cmd[0] |= pipe_cmd;
+
+       if (runtime->sample_bits == 16)
+               /* 16 bit format */
+               chip->rmh.cmd[0] |= (STREAM_FMT_16b << STREAM_FMT_OFFSET);
+
+       if (snd_pcm_format_little_endian(runtime->format))
+               /* little endian/intel format */
+               chip->rmh.cmd[0] |= (STREAM_FMT_intel << STREAM_FMT_OFFSET);
+
+       chip->rmh.cmd[0] |= channels-1;
+
+       err = lx_message_send_atomic(chip, &chip->rmh);
+       spin_unlock_irqrestore(&chip->msg_lock, flags);
+
+       return err;
+}
+
+int lx_stream_state(struct lx6464es *chip, u32 pipe, int is_capture,
+                   int *rstate)
+{
+       int err;
+       unsigned long flags;
+
+       u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+       spin_lock_irqsave(&chip->msg_lock, flags);
+       lx_message_init(&chip->rmh, CMD_0E_GET_STREAM_SPL_COUNT);
+
+       chip->rmh.cmd[0] |= pipe_cmd;
+
+       err = lx_message_send_atomic(chip, &chip->rmh);
+
+       *rstate = (chip->rmh.stat[0] & SF_START) ? START_STATE : PAUSE_STATE;
+
+       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       return err;
+}
+
+int lx_stream_sample_position(struct lx6464es *chip, u32 pipe, int is_capture,
+                             u64 *r_bytepos)
+{
+       int err;
+       unsigned long flags;
+
+       u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+       spin_lock_irqsave(&chip->msg_lock, flags);
+       lx_message_init(&chip->rmh, CMD_0E_GET_STREAM_SPL_COUNT);
+
+       chip->rmh.cmd[0] |= pipe_cmd;
+
+       err = lx_message_send_atomic(chip, &chip->rmh);
+
+       *r_bytepos = ((u64) (chip->rmh.stat[0] & MASK_SPL_COUNT_HI)
+                     << 32)         /* hi part */
+               + chip->rmh.stat[1]; /* lo part */
+
+       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       return err;
+}
+
+/* low-level buffer handling */
+int lx_buffer_give(struct lx6464es *chip, u32 pipe, int is_capture,
+                  u32 buffer_size, u32 buf_address_lo, u32 buf_address_hi,
+                  u32 *r_buffer_index)
+{
+       int err;
+       unsigned long flags;
+
+       u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+       spin_lock_irqsave(&chip->msg_lock, flags);
+       lx_message_init(&chip->rmh, CMD_0F_UPDATE_BUFFER);
+
+       chip->rmh.cmd[0] |= pipe_cmd;
+       chip->rmh.cmd[0] |= BF_NOTIFY_EOB; /* request interrupt notification */
+
+       /* todo: pause request, circular buffer */
+
+       chip->rmh.cmd[1] = buffer_size & MASK_DATA_SIZE;
+       chip->rmh.cmd[2] = buf_address_lo;
+
+       if (buf_address_hi) {
+               chip->rmh.cmd_len = 4;
+               chip->rmh.cmd[3] = buf_address_hi;
+               chip->rmh.cmd[0] |= BF_64BITS_ADR;
+       }
+
+       err = lx_message_send_atomic(chip, &chip->rmh);
+
+       if (err == 0) {
+               *r_buffer_index = chip->rmh.stat[0];
+               goto done;
+       }
+
+       if (err == EB_RBUFFERS_TABLE_OVERFLOW)
+               snd_printk(LXP "lx_buffer_give EB_RBUFFERS_TABLE_OVERFLOW\n");
+
+       if (err == EB_INVALID_STREAM)
+               snd_printk(LXP "lx_buffer_give EB_INVALID_STREAM\n");
+
+       if (err == EB_CMD_REFUSED)
+               snd_printk(LXP "lx_buffer_give EB_CMD_REFUSED\n");
+
+ done:
+       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       return err;
+}
+
+int lx_buffer_free(struct lx6464es *chip, u32 pipe, int is_capture,
+                  u32 *r_buffer_size)
+{
+       int err;
+       unsigned long flags;
+
+       u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+       spin_lock_irqsave(&chip->msg_lock, flags);
+       lx_message_init(&chip->rmh, CMD_11_CANCEL_BUFFER);
+
+       chip->rmh.cmd[0] |= pipe_cmd;
+       chip->rmh.cmd[0] |= MASK_BUFFER_ID; /* ask for the current buffer: the
+                                            * microblaze will seek for it */
+
+       err = lx_message_send_atomic(chip, &chip->rmh);
+
+       if (err == 0)
+               *r_buffer_size = chip->rmh.stat[0]  & MASK_DATA_SIZE;
+
+       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       return err;
+}
+
+int lx_buffer_cancel(struct lx6464es *chip, u32 pipe, int is_capture,
+                    u32 buffer_index)
+{
+       int err;
+       unsigned long flags;
+
+       u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+       spin_lock_irqsave(&chip->msg_lock, flags);
+       lx_message_init(&chip->rmh, CMD_11_CANCEL_BUFFER);
+
+       chip->rmh.cmd[0] |= pipe_cmd;
+       chip->rmh.cmd[0] |= buffer_index;
+
+       err = lx_message_send_atomic(chip, &chip->rmh);
+
+       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       return err;
+}
+
+
+/* low-level gain/peak handling
+ *
+ * \todo: can we unmute capture/playback channels independently?
+ *
+ * */
+int lx_level_unmute(struct lx6464es *chip, int is_capture, int unmute)
+{
+       int err;
+       unsigned long flags;
+
+       /* bit set to 1: channel muted */
+       u64 mute_mask = unmute ? 0 : 0xFFFFFFFFFFFFFFFFLLU;
+
+       spin_lock_irqsave(&chip->msg_lock, flags);
+       lx_message_init(&chip->rmh, CMD_0D_SET_MUTE);
+
+       chip->rmh.cmd[0] |= PIPE_INFO_TO_CMD(is_capture, 0);
+
+       chip->rmh.cmd[1] = (u32)(mute_mask >> (u64)32);        /* hi part */
+       chip->rmh.cmd[2] = (u32)(mute_mask & (u64)0xFFFFFFFF); /* lo part */
+
+       snd_printk("mute %x %x %x\n", chip->rmh.cmd[0], chip->rmh.cmd[1],
+                  chip->rmh.cmd[2]);
+
+       err = lx_message_send_atomic(chip, &chip->rmh);
+
+       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       return err;
+}
+
+static u32 peak_map[] = {
+       0x00000109, /* -90.308dB */
+       0x0000083B, /* -72.247dB */
+       0x000020C4, /* -60.205dB */
+       0x00008273, /* -48.030dB */
+       0x00020756, /* -36.005dB */
+       0x00040C37, /* -30.001dB */
+       0x00081385, /* -24.002dB */
+       0x00101D3F, /* -18.000dB */
+       0x0016C310, /* -15.000dB */
+       0x002026F2, /* -12.001dB */
+       0x002D6A86, /* -9.000dB */
+       0x004026E6, /* -6.004dB */
+       0x005A9DF6, /* -3.000dB */
+       0x0065AC8B, /* -2.000dB */
+       0x00721481, /* -1.000dB */
+       0x007FFFFF, /* FS */
+};
+
+int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels,
+                  u32 *r_levels)
+{
+       int err = 0;
+       unsigned long flags;
+       int i;
+       spin_lock_irqsave(&chip->msg_lock, flags);
+
+       for (i = 0; i < channels; i += 4) {
+               u32 s0, s1, s2, s3;
+
+               lx_message_init(&chip->rmh, CMD_12_GET_PEAK);
+               chip->rmh.cmd[0] |= PIPE_INFO_TO_CMD(is_capture, i);
+
+               err = lx_message_send_atomic(chip, &chip->rmh);
+
+               if (err == 0) {
+                       s0 = peak_map[chip->rmh.stat[0] & 0x0F];
+                       s1 = peak_map[(chip->rmh.stat[0] >>  4) & 0xf];
+                       s2 = peak_map[(chip->rmh.stat[0] >>  8) & 0xf];
+                       s3 = peak_map[(chip->rmh.stat[0] >>  12) & 0xf];
+               } else
+                       s0 = s1 = s2 = s3 = 0;
+
+               r_levels[0] = s0;
+               r_levels[1] = s1;
+               r_levels[2] = s2;
+               r_levels[3] = s3;
+
+               r_levels += 4;
+       }
+
+       spin_unlock_irqrestore(&chip->msg_lock, flags);
+       return err;
+}
+
+/* interrupt handling */
+#define PCX_IRQ_NONE 0
+#define IRQCS_ACTIVE_PCIDB  0x00002000L         /* Bit nÃ\83¸ 13 */
+#define IRQCS_ENABLE_PCIIRQ 0x00000100L         /* Bit nÃ\83¸ 08 */
+#define IRQCS_ENABLE_PCIDB  0x00000200L         /* Bit nÃ\83¸ 09 */
+
+static u32 lx_interrupt_test_ack(struct lx6464es *chip)
+{
+       u32 irqcs = lx_plx_reg_read(chip, ePLX_IRQCS);
+
+       /* Test if PCI Doorbell interrupt is active */
+       if (irqcs & IRQCS_ACTIVE_PCIDB) {
+               u32 temp;
+               irqcs = PCX_IRQ_NONE;
+
+               while ((temp = lx_plx_reg_read(chip, ePLX_L2PCIDB))) {
+                       /* RAZ interrupt */
+                       irqcs |= temp;
+                       lx_plx_reg_write(chip, ePLX_L2PCIDB, temp);
+               }
+
+               return irqcs;
+       }
+       return PCX_IRQ_NONE;
+}
+
+static int lx_interrupt_ack(struct lx6464es *chip, u32 *r_irqsrc,
+                           int *r_async_pending, int *r_async_escmd)
+{
+       u32 irq_async;
+       u32 irqsrc = lx_interrupt_test_ack(chip);
+
+       if (irqsrc == PCX_IRQ_NONE)
+               return 0;
+
+       *r_irqsrc = irqsrc;
+
+       irq_async = irqsrc & MASK_SYS_ASYNC_EVENTS; /* + EtherSound response
+                                                    * (set by xilinx) + EOB */
+
+       if (irq_async & MASK_SYS_STATUS_ESA) {
+               irq_async &= ~MASK_SYS_STATUS_ESA;
+               *r_async_escmd = 1;
+       }
+
+       if (irqsrc & MASK_SYS_STATUS_CMD_DONE)
+               /* xilinx command notification */
+               atomic_set(&chip->send_message_locked, 0);
+
+       if (irq_async) {
+               /* snd_printd("interrupt: async event pending\n"); */
+               *r_async_pending = 1;
+       }
+
+       return 1;
+}
+
+static int lx_interrupt_handle_async_events(struct lx6464es *chip, u32 irqsrc,
+                                           int *r_freq_changed,
+                                           u64 *r_notified_in_pipe_mask,
+                                           u64 *r_notified_out_pipe_mask)
+{
+       int err;
+       u32 stat[9];            /* answer from CMD_04_GET_EVENT */
+
+       /* On peut optimiser pour ne pas lire les evenements vides
+        * les mots de rÃ\83©ponse sont dans l'ordre suivant :
+        * Stat[0]      mot de status gÃ\83©nÃ\83©ral
+        * Stat[1]      fin de buffer OUT pF
+        * Stat[2]      fin de buffer OUT pf
+        * Stat[3]      fin de buffer IN pF
+        * Stat[4]      fin de buffer IN pf
+        * Stat[5]      underrun poid fort
+        * Stat[6]      underrun poid faible
+        * Stat[7]      overrun poid fort
+        * Stat[8]      overrun poid faible
+        * */
+
+       u64 orun_mask;
+       u64 urun_mask;
+#if 0
+       int has_underrun   = (irqsrc & MASK_SYS_STATUS_URUN) ? 1 : 0;
+       int has_overrun    = (irqsrc & MASK_SYS_STATUS_ORUN) ? 1 : 0;
+#endif
+       int eb_pending_out = (irqsrc & MASK_SYS_STATUS_EOBO) ? 1 : 0;
+       int eb_pending_in  = (irqsrc & MASK_SYS_STATUS_EOBI) ? 1 : 0;
+
+       *r_freq_changed = (irqsrc & MASK_SYS_STATUS_FREQ) ? 1 : 0;
+
+       err = lx_dsp_read_async_events(chip, stat);
+       if (err < 0)
+               return err;
+
+       if (eb_pending_in) {
+               *r_notified_in_pipe_mask = ((u64)stat[3] << 32)
+                       + stat[4];
+               snd_printdd(LXP "interrupt: EOBI pending %llx\n",
+                           *r_notified_in_pipe_mask);
+       }
+       if (eb_pending_out) {
+               *r_notified_out_pipe_mask = ((u64)stat[1] << 32)
+                       + stat[2];
+               snd_printdd(LXP "interrupt: EOBO pending %llx\n",
+                           *r_notified_out_pipe_mask);
+       }
+
+       orun_mask = ((u64)stat[7] << 32) + stat[8];
+       urun_mask = ((u64)stat[5] << 32) + stat[6];
+
+       /* todo: handle xrun notification */
+
+       return err;
+}
+
+static int lx_interrupt_request_new_buffer(struct lx6464es *chip,
+                                          struct lx_stream *lx_stream)
+{
+       struct snd_pcm_substream *substream = lx_stream->stream;
+       int is_capture = lx_stream->is_capture;
+       int err;
+       unsigned long flags;
+
+       const u32 channels = substream->runtime->channels;
+       const u32 bytes_per_frame = channels * 3;
+       const u32 period_size = substream->runtime->period_size;
+       const u32 period_bytes = period_size * bytes_per_frame;
+       const u32 pos = lx_stream->frame_pos;
+       const u32 next_pos = ((pos+1) == substream->runtime->periods) ?
+               0 : pos + 1;
+
+       dma_addr_t buf = substream->dma_buffer.addr + pos * period_bytes;
+       u32 buf_hi = 0;
+       u32 buf_lo = 0;
+       u32 buffer_index = 0;
+
+       u32 needed, freed;
+       u32 size_array[MAX_STREAM_BUFFER];
+
+       snd_printdd("->lx_interrupt_request_new_buffer\n");
+
+       spin_lock_irqsave(&chip->lock, flags);
+
+       err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed, size_array);
+       snd_printdd(LXP "interrupt: needed %d, freed %d\n", needed, freed);
+
+       unpack_pointer(buf, &buf_lo, &buf_hi);
+       err = lx_buffer_give(chip, 0, is_capture, period_bytes, buf_lo, buf_hi,
+                            &buffer_index);
+       snd_printdd(LXP "interrupt: gave buffer index %x on %p (%d bytes)\n",
+                   buffer_index, (void *)buf, period_bytes);
+
+       lx_stream->frame_pos = next_pos;
+       spin_unlock_irqrestore(&chip->lock, flags);
+
+       return err;
+}
+
+void lx_tasklet_playback(unsigned long data)
+{
+       struct lx6464es *chip = (struct lx6464es *)data;
+       struct lx_stream *lx_stream = &chip->playback_stream;
+       int err;
+
+       snd_printdd("->lx_tasklet_playback\n");
+
+       err = lx_interrupt_request_new_buffer(chip, lx_stream);
+       if (err < 0)
+               snd_printk(KERN_ERR LXP
+                          "cannot request new buffer for playback\n");
+
+       snd_pcm_period_elapsed(lx_stream->stream);
+}
+
+void lx_tasklet_capture(unsigned long data)
+{
+       struct lx6464es *chip = (struct lx6464es *)data;
+       struct lx_stream *lx_stream = &chip->capture_stream;
+       int err;
+
+       snd_printdd("->lx_tasklet_capture\n");
+       err = lx_interrupt_request_new_buffer(chip, lx_stream);
+       if (err < 0)
+               snd_printk(KERN_ERR LXP
+                          "cannot request new buffer for capture\n");
+
+       snd_pcm_period_elapsed(lx_stream->stream);
+}
+
+
+
+static int lx_interrupt_handle_audio_transfer(struct lx6464es *chip,
+                                             u64 notified_in_pipe_mask,
+                                             u64 notified_out_pipe_mask)
+{
+       int err = 0;
+
+       if (notified_in_pipe_mask) {
+               snd_printdd(LXP "requesting audio transfer for capture\n");
+               tasklet_hi_schedule(&chip->tasklet_capture);
+       }
+
+       if (notified_out_pipe_mask) {
+               snd_printdd(LXP "requesting audio transfer for playback\n");
+               tasklet_hi_schedule(&chip->tasklet_playback);
+       }
+
+       return err;
+}
+
+
+irqreturn_t lx_interrupt(int irq, void *dev_id)
+{
+       struct lx6464es *chip = dev_id;
+       int async_pending, async_escmd;
+       u32 irqsrc;
+
+       spin_lock(&chip->lock);
+
+       snd_printdd("**************************************************\n");
+
+       if (!lx_interrupt_ack(chip, &irqsrc, &async_pending, &async_escmd)) {
+               spin_unlock(&chip->lock);
+               snd_printdd("IRQ_NONE\n");
+               return IRQ_NONE; /* this device did not cause the interrupt */
+       }
+
+       if (irqsrc & MASK_SYS_STATUS_CMD_DONE)
+               goto exit;
+
+#if 0
+       if (irqsrc & MASK_SYS_STATUS_EOBI)
+               snd_printdd(LXP "interrupt: EOBI\n");
+
+       if (irqsrc & MASK_SYS_STATUS_EOBO)
+               snd_printdd(LXP "interrupt: EOBO\n");
+
+       if (irqsrc & MASK_SYS_STATUS_URUN)
+               snd_printdd(LXP "interrupt: URUN\n");
+
+       if (irqsrc & MASK_SYS_STATUS_ORUN)
+               snd_printdd(LXP "interrupt: ORUN\n");
+#endif
+
+       if (async_pending) {
+               u64 notified_in_pipe_mask = 0;
+               u64 notified_out_pipe_mask = 0;
+               int freq_changed;
+               int err;
+
+               /* handle async events */
+               err = lx_interrupt_handle_async_events(chip, irqsrc,
+                                                      &freq_changed,
+                                                      &notified_in_pipe_mask,
+                                                      &notified_out_pipe_mask);
+               if (err)
+                       snd_printk(KERN_ERR LXP
+                                  "error handling async events\n");
+
+               err = lx_interrupt_handle_audio_transfer(chip,
+                                                        notified_in_pipe_mask,
+                                                        notified_out_pipe_mask
+                       );
+               if (err)
+                       snd_printk(KERN_ERR LXP
+                                  "error during audio transfer\n");
+       }
+
+       if (async_escmd) {
+#if 0
+               /* backdoor for ethersound commands
+                *
+                * for now, we do not need this
+                *
+                * */
+
+               snd_printdd("lx6464es: interrupt requests escmd handling\n");
+#endif
+       }
+
+exit:
+       spin_unlock(&chip->lock);
+       return IRQ_HANDLED;     /* this device caused the interrupt */
+}
+
+
+static void lx_irq_set(struct lx6464es *chip, int enable)
+{
+       u32 reg = lx_plx_reg_read(chip, ePLX_IRQCS);
+
+       /* enable/disable interrupts
+        *
+        * Set the Doorbell and PCI interrupt enable bits
+        *
+        * */
+       if (enable)
+               reg |=  (IRQCS_ENABLE_PCIIRQ | IRQCS_ENABLE_PCIDB);
+       else
+               reg &= ~(IRQCS_ENABLE_PCIIRQ | IRQCS_ENABLE_PCIDB);
+       lx_plx_reg_write(chip, ePLX_IRQCS, reg);
+}
+
+void lx_irq_enable(struct lx6464es *chip)
+{
+       snd_printdd("->lx_irq_enable\n");
+       lx_irq_set(chip, 1);
+}
+
+void lx_irq_disable(struct lx6464es *chip)
+{
+       snd_printdd("->lx_irq_disable\n");
+       lx_irq_set(chip, 0);
+}
diff --git a/sound/pci/lx6464es/lx_core.h b/sound/pci/lx6464es/lx_core.h
new file mode 100644 (file)
index 0000000..6bd9cbb
--- /dev/null
@@ -0,0 +1,242 @@
+/* -*- linux-c -*- *
+ *
+ * ALSA driver for the digigram lx6464es interface
+ * low-level interface
+ *
+ * Copyright (c) 2009 Tim Blechmann <tim@klingt.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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef LX_CORE_H
+#define LX_CORE_H
+
+#include <linux/interrupt.h>
+
+#include "lx_defs.h"
+
+#define REG_CRM_NUMBER         12
+
+struct lx6464es;
+
+/* low-level register access */
+
+/* dsp register access */
+enum {
+       eReg_BASE,
+       eReg_CSM,
+       eReg_CRM1,
+       eReg_CRM2,
+       eReg_CRM3,
+       eReg_CRM4,
+       eReg_CRM5,
+       eReg_CRM6,
+       eReg_CRM7,
+       eReg_CRM8,
+       eReg_CRM9,
+       eReg_CRM10,
+       eReg_CRM11,
+       eReg_CRM12,
+
+       eReg_ICR,
+       eReg_CVR,
+       eReg_ISR,
+       eReg_RXHTXH,
+       eReg_RXMTXM,
+       eReg_RHLTXL,
+       eReg_RESETDSP,
+
+       eReg_CSUF,
+       eReg_CSES,
+       eReg_CRESMSB,
+       eReg_CRESLSB,
+       eReg_ADMACESMSB,
+       eReg_ADMACESLSB,
+       eReg_CONFES,
+
+       eMaxPortLx
+};
+
+unsigned long lx_dsp_reg_read(struct lx6464es *chip, int port);
+void lx_dsp_reg_readbuf(struct lx6464es *chip, int port, u32 *data, u32 len);
+void lx_dsp_reg_write(struct lx6464es *chip, int port, unsigned data);
+void lx_dsp_reg_writebuf(struct lx6464es *chip, int port, const u32 *data,
+                        u32 len);
+
+/* plx register access */
+enum {
+    ePLX_PCICR,
+
+    ePLX_MBOX0,
+    ePLX_MBOX1,
+    ePLX_MBOX2,
+    ePLX_MBOX3,
+    ePLX_MBOX4,
+    ePLX_MBOX5,
+    ePLX_MBOX6,
+    ePLX_MBOX7,
+
+    ePLX_L2PCIDB,
+    ePLX_IRQCS,
+    ePLX_CHIPSC,
+
+    eMaxPort
+};
+
+unsigned long lx_plx_reg_read(struct lx6464es *chip, int port);
+void lx_plx_reg_write(struct lx6464es *chip, int port, u32 data);
+
+/* rhm */
+struct lx_rmh {
+       u16     cmd_len;        /* length of the command to send (WORDs) */
+       u16     stat_len;       /* length of the status received (WORDs) */
+       u16     dsp_stat;       /* status type, RMP_SSIZE_XXX */
+       u16     cmd_idx;        /* index of the command */
+       u32     cmd[REG_CRM_NUMBER];
+       u32     stat[REG_CRM_NUMBER];
+};
+
+
+/* low-level dsp access */
+int __devinit lx_dsp_get_version(struct lx6464es *chip, u32 *rdsp_version);
+int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq);
+int lx_dsp_set_granularity(struct lx6464es *chip, u32 gran);
+int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data);
+int lx_dsp_get_mac(struct lx6464es *chip, u8 *mac_address);
+
+
+/* low-level pipe handling */
+int lx_pipe_allocate(struct lx6464es *chip, u32 pipe, int is_capture,
+                    int channels);
+int lx_pipe_release(struct lx6464es *chip, u32 pipe, int is_capture);
+int lx_pipe_sample_count(struct lx6464es *chip, u32 pipe, int is_capture,
+                        u64 *rsample_count);
+int lx_pipe_state(struct lx6464es *chip, u32 pipe, int is_capture, u16 *rstate);
+int lx_pipe_stop(struct lx6464es *chip, u32 pipe, int is_capture);
+int lx_pipe_start(struct lx6464es *chip, u32 pipe, int is_capture);
+int lx_pipe_pause(struct lx6464es *chip, u32 pipe, int is_capture);
+
+int lx_pipe_wait_for_start(struct lx6464es *chip, u32 pipe, int is_capture);
+int lx_pipe_wait_for_idle(struct lx6464es *chip, u32 pipe, int is_capture);
+
+/* low-level stream handling */
+int lx_stream_set_format(struct lx6464es *chip, struct snd_pcm_runtime *runtime,
+                        u32 pipe, int is_capture);
+int lx_stream_state(struct lx6464es *chip, u32 pipe, int is_capture,
+                   int *rstate);
+int lx_stream_sample_position(struct lx6464es *chip, u32 pipe, int is_capture,
+                             u64 *r_bytepos);
+
+int lx_stream_set_state(struct lx6464es *chip, u32 pipe,
+                       int is_capture, enum stream_state_t state);
+
+static inline int lx_stream_start(struct lx6464es *chip, u32 pipe,
+                                 int is_capture)
+{
+       snd_printdd("->lx_stream_start\n");
+       return lx_stream_set_state(chip, pipe, is_capture, SSTATE_RUN);
+}
+
+static inline int lx_stream_pause(struct lx6464es *chip, u32 pipe,
+                                 int is_capture)
+{
+       snd_printdd("->lx_stream_pause\n");
+       return lx_stream_set_state(chip, pipe, is_capture, SSTATE_PAUSE);
+}
+
+static inline int lx_stream_stop(struct lx6464es *chip, u32 pipe,
+                                int is_capture)
+{
+       snd_printdd("->lx_stream_stop\n");
+       return lx_stream_set_state(chip, pipe, is_capture, SSTATE_STOP);
+}
+
+/* low-level buffer handling */
+int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
+                 u32 *r_needed, u32 *r_freed, u32 *size_array);
+int lx_buffer_give(struct lx6464es *chip, u32 pipe, int is_capture,
+                  u32 buffer_size, u32 buf_address_lo, u32 buf_address_hi,
+                  u32 *r_buffer_index);
+int lx_buffer_free(struct lx6464es *chip, u32 pipe, int is_capture,
+                  u32 *r_buffer_size);
+int lx_buffer_cancel(struct lx6464es *chip, u32 pipe, int is_capture,
+                    u32 buffer_index);
+
+/* low-level gain/peak handling */
+int lx_level_unmute(struct lx6464es *chip, int is_capture, int unmute);
+int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels,
+                  u32 *r_levels);
+
+
+/* interrupt handling */
+irqreturn_t lx_interrupt(int irq, void *dev_id);
+void lx_irq_enable(struct lx6464es *chip);
+void lx_irq_disable(struct lx6464es *chip);
+
+void lx_tasklet_capture(unsigned long data);
+void lx_tasklet_playback(unsigned long data);
+
+
+/* Stream Format Header Defines (for LIN and IEEE754) */
+#define HEADER_FMT_BASE                HEADER_FMT_BASE_LIN
+#define HEADER_FMT_BASE_LIN    0xFED00000
+#define HEADER_FMT_BASE_FLOAT  0xFAD00000
+#define HEADER_FMT_MONO                0x00000080 /* bit 23 in header_lo. WARNING: old
+                                           * bit 22 is ignored in float
+                                           * format */
+#define HEADER_FMT_INTEL       0x00008000
+#define HEADER_FMT_16BITS      0x00002000
+#define HEADER_FMT_24BITS      0x00004000
+#define HEADER_FMT_UPTO11      0x00000200 /* frequency is less or equ. to 11k.
+                                           * */
+#define HEADER_FMT_UPTO32      0x00000100 /* frequency is over 11k and less
+                                           * then 32k.*/
+
+
+#define BIT_FMP_HEADER          23
+#define BIT_FMP_SD              22
+#define BIT_FMP_MULTICHANNEL    19
+
+#define START_STATE             1
+#define PAUSE_STATE             0
+
+
+
+
+
+/* from PcxAll_e.h */
+/* Start/Pause condition for pipes (PCXStartPipe, PCXPausePipe) */
+#define START_PAUSE_IMMEDIATE           0
+#define START_PAUSE_ON_SYNCHRO          1
+#define START_PAUSE_ON_TIME_CODE        2
+
+
+/* Pipe / Stream state */
+#define START_STATE             1
+#define PAUSE_STATE             0
+
+static inline void unpack_pointer(dma_addr_t ptr, u32 *r_low, u32 *r_high)
+{
+       *r_low = (u32)(ptr & 0xffffffff);
+#if BITS_PER_LONG == 32
+       *r_high = 0;
+#else
+       *r_high = (u32)((u64)ptr>>32);
+#endif
+}
+
+#endif /* LX_CORE_H */
diff --git a/sound/pci/lx6464es/lx_defs.h b/sound/pci/lx6464es/lx_defs.h
new file mode 100644 (file)
index 0000000..49d36bd
--- /dev/null
@@ -0,0 +1,376 @@
+/* -*- linux-c -*- *
+ *
+ * ALSA driver for the digigram lx6464es interface
+ * adapted upstream headers
+ *
+ * Copyright (c) 2009 Tim Blechmann <tim@klingt.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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef LX_DEFS_H
+#define LX_DEFS_H
+
+/* code adapted from ethersound.h */
+#define        XES_FREQ_COUNT8_MASK    0x00001FFF /* compteur 25MHz entre 8 ech. */
+#define        XES_FREQ_COUNT8_44_MIN  0x00001288 /* 25M /
+                                           * [ 44k - ( 44.1k + 48k ) / 2 ]
+                                           * * 8 */
+#define        XES_FREQ_COUNT8_44_MAX  0x000010F0 /* 25M / [ ( 44.1k + 48k ) / 2 ]
+                                           * * 8 */
+#define        XES_FREQ_COUNT8_48_MAX  0x00000F08 /* 25M /
+                                           * [ 48k + ( 44.1k + 48k ) / 2 ]
+                                           * * 8 */
+
+/* code adapted from LXES_registers.h */
+
+#define IOCR_OUTPUTS_OFFSET 0  /* (rw) offset for the number of OUTs in the
+                                * ConfES register. */
+#define IOCR_INPUTS_OFFSET  8  /* (rw) offset for the number of INs in the
+                                * ConfES register. */
+#define FREQ_RATIO_OFFSET  19  /* (rw) offset for frequency ratio in the
+                                * ConfES register. */
+#define        FREQ_RATIO_SINGLE_MODE 0x01 /* value for single mode frequency ratio:
+                                    * sample rate = frequency rate. */
+
+#define CONFES_READ_PART_MASK  0x00070000
+#define CONFES_WRITE_PART_MASK 0x00F80000
+
+/* code adapted from if_drv_mb.h */
+
+#define MASK_SYS_STATUS_ERROR  (1L << 31) /* events that lead to a PCI irq if
+                                           * not yet pending */
+#define MASK_SYS_STATUS_URUN   (1L << 30)
+#define MASK_SYS_STATUS_ORUN   (1L << 29)
+#define MASK_SYS_STATUS_EOBO   (1L << 28)
+#define MASK_SYS_STATUS_EOBI   (1L << 27)
+#define MASK_SYS_STATUS_FREQ   (1L << 26)
+#define MASK_SYS_STATUS_ESA    (1L << 25) /* reserved, this is set by the
+                                           * XES */
+#define MASK_SYS_STATUS_TIMER  (1L << 24)
+
+#define MASK_SYS_ASYNC_EVENTS  (MASK_SYS_STATUS_ERROR |                \
+                                MASK_SYS_STATUS_URUN  |                \
+                                MASK_SYS_STATUS_ORUN  |                \
+                                MASK_SYS_STATUS_EOBO  |                \
+                                MASK_SYS_STATUS_EOBI  |                \
+                                MASK_SYS_STATUS_FREQ  |                \
+                                MASK_SYS_STATUS_ESA)
+
+#define MASK_SYS_PCI_EVENTS            (MASK_SYS_ASYNC_EVENTS |        \
+                                        MASK_SYS_STATUS_TIMER)
+
+#define MASK_SYS_TIMER_COUNT   0x0000FFFF
+
+#define MASK_SYS_STATUS_EOT_PLX                (1L << 22) /* event that remains
+                                                   * internal: reserved fo end
+                                                   * of plx dma */
+#define MASK_SYS_STATUS_XES            (1L << 21) /* event that remains
+                                                   * internal: pending XES
+                                                   * IRQ */
+#define MASK_SYS_STATUS_CMD_DONE       (1L << 20) /* alternate command
+                                                   * management: notify driver
+                                                   * instead of polling */
+
+
+#define MAX_STREAM_BUFFER 5    /* max amount of stream buffers. */
+
+#define MICROBLAZE_IBL_MIN              32
+#define MICROBLAZE_IBL_DEFAULT         128
+#define MICROBLAZE_IBL_MAX             512
+/* #define MASK_GRANULARITY            (2*MICROBLAZE_IBL_MAX-1) */
+
+
+
+/* command opcodes, see reference for details */
+
+/*
+ the capture bit position in the object_id field in driver commands
+ depends upon the number of managed channels. For now, 64 IN + 64 OUT are
+ supported. HOwever, the communication protocol forsees 1024 channels, hence
+ bit 10 indicates a capture (input) object).
+*/
+#define ID_IS_CAPTURE (1L << 10)
+#define ID_OFFSET      13      /* object ID is at the 13th bit in the
+                                * 1st command word.*/
+#define ID_CH_MASK    0x3F
+#define OPCODE_OFFSET  24      /* offset of the command opcode in the first
+                                * command word.*/
+
+enum cmd_mb_opcodes {
+       CMD_00_INFO_DEBUG               = 0x00,
+       CMD_01_GET_SYS_CFG              = 0x01,
+       CMD_02_SET_GRANULARITY          = 0x02,
+       CMD_03_SET_TIMER_IRQ            = 0x03,
+       CMD_04_GET_EVENT                = 0x04,
+       CMD_05_GET_PIPES                = 0x05,
+
+       CMD_06_ALLOCATE_PIPE            = 0x06,
+       CMD_07_RELEASE_PIPE             = 0x07,
+       CMD_08_ASK_BUFFERS              = 0x08,
+       CMD_09_STOP_PIPE                = 0x09,
+       CMD_0A_GET_PIPE_SPL_COUNT       = 0x0a,
+       CMD_0B_TOGGLE_PIPE_STATE        = 0x0b,
+
+       CMD_0C_DEF_STREAM               = 0x0c,
+       CMD_0D_SET_MUTE                 = 0x0d,
+       CMD_0E_GET_STREAM_SPL_COUNT     = 0x0e,
+       CMD_0F_UPDATE_BUFFER            = 0x0f,
+       CMD_10_GET_BUFFER               = 0x10,
+       CMD_11_CANCEL_BUFFER            = 0x11,
+       CMD_12_GET_PEAK                 = 0x12,
+       CMD_13_SET_STREAM_STATE         = 0x13,
+       CMD_14_INVALID                  = 0x14,
+};
+
+/* pipe states */
+enum pipe_state_t {
+       PSTATE_IDLE     = 0,    /* the pipe is not processed in the XES_IRQ
+                                * (free or stopped, or paused). */
+       PSTATE_RUN      = 1,    /* sustained play/record state. */
+       PSTATE_PURGE    = 2,    /* the ES channels are now off, render pipes do
+                                * not DMA, record pipe do a last DMA. */
+       PSTATE_ACQUIRE  = 3,    /* the ES channels are now on, render pipes do
+                                * not yet increase their sample count, record
+                                * pipes do not DMA. */
+       PSTATE_CLOSING  = 4,    /* the pipe is releasing, and may not yet
+                                * receive an "alloc" command. */
+};
+
+/* stream states */
+enum stream_state_t {
+       SSTATE_STOP     =  0x00,       /* setting to stop resets the stream spl
+                                       * count.*/
+       SSTATE_RUN      = (0x01 << 0), /* start DMA and spl count handling. */
+       SSTATE_PAUSE    = (0x01 << 1), /* pause DMA and spl count handling. */
+};
+
+/* buffer flags */
+enum buffer_flags {
+       BF_VALID        = 0x80, /* set if the buffer is valid, clear if free.*/
+       BF_CURRENT      = 0x40, /* set if this is the current buffer (there is
+                                * always a current buffer).*/
+       BF_NOTIFY_EOB   = 0x20, /* set if this buffer must cause a PCI event
+                                * when finished.*/
+       BF_CIRCULAR     = 0x10, /* set if buffer[1] must be copied to buffer[0]
+                                * by the end of this buffer.*/
+       BF_64BITS_ADR   = 0x08, /* set if the hi part of the address is valid.*/
+       BF_xx           = 0x04, /* future extension.*/
+       BF_EOB          = 0x02, /* set if finished, but not yet free.*/
+       BF_PAUSE        = 0x01, /* pause stream at buffer end.*/
+       BF_ZERO         = 0x00, /* no flags (init).*/
+};
+
+/**
+*      Stream Flags definitions
+*/
+enum stream_flags {
+       SF_ZERO         = 0x00000000, /* no flags (stream invalid). */
+       SF_VALID        = 0x10000000, /* the stream has a valid DMA_conf
+                                      * info (setstreamformat). */
+       SF_XRUN         = 0x20000000, /* the stream is un x-run state. */
+       SF_START        = 0x40000000, /* the DMA is running.*/
+       SF_ASIO         = 0x80000000, /* ASIO.*/
+};
+
+
+#define MASK_SPL_COUNT_HI 0x00FFFFFF /* 4 MSBits are status bits */
+#define PSTATE_OFFSET             28 /* 4 MSBits are status bits */
+
+
+#define MASK_STREAM_HAS_MAPPING        (1L << 12)
+#define MASK_STREAM_IS_ASIO    (1L <<  9)
+#define STREAM_FMT_OFFSET      10   /* the stream fmt bits start at the 10th
+                                     * bit in the command word. */
+
+#define STREAM_FMT_16b          0x02
+#define STREAM_FMT_intel        0x01
+
+#define FREQ_FIELD_OFFSET      15  /* offset of the freq field in the response
+                                    * word */
+
+#define BUFF_FLAGS_OFFSET        24 /*  offset of the buffer flags in the
+                                     *  response word. */
+#define MASK_DATA_SIZE   0x00FFFFFF /* this must match the field size of
+                                     * datasize in the buffer_t structure. */
+
+#define MASK_BUFFER_ID         0xFF /* the cancel command awaits a buffer ID,
+                                     * may be 0xFF for "current". */
+
+
+/* code adapted from PcxErr_e.h */
+
+/* Bits masks */
+
+#define ERROR_MASK              0x8000
+
+#define SOURCE_MASK             0x7800
+
+#define E_SOURCE_BOARD          0x4000 /* 8 >> 1 */
+#define E_SOURCE_DRV            0x2000 /* 4 >> 1 */
+#define E_SOURCE_API            0x1000 /* 2 >> 1 */
+/* Error tools */
+#define E_SOURCE_TOOLS          0x0800 /* 1 >> 1 */
+/* Error pcxaudio */
+#define E_SOURCE_AUDIO          0x1800 /* 3 >> 1 */
+/* Error virtual pcx */
+#define E_SOURCE_VPCX           0x2800 /* 5 >> 1 */
+/* Error dispatcher */
+#define E_SOURCE_DISPATCHER     0x3000 /* 6 >> 1 */
+/* Error from CobraNet firmware */
+#define E_SOURCE_COBRANET       0x3800 /* 7 >> 1 */
+
+#define E_SOURCE_USER           0x7800
+
+#define CLASS_MASK              0x0700
+
+#define CODE_MASK               0x00FF
+
+/* Bits values */
+
+/* Values for the error/warning bit */
+#define ERROR_VALUE             0x8000
+#define WARNING_VALUE           0x0000
+
+/* Class values */
+#define E_CLASS_GENERAL                  0x0000
+#define E_CLASS_INVALID_CMD              0x0100
+#define E_CLASS_INVALID_STD_OBJECT       0x0200
+#define E_CLASS_RSRC_IMPOSSIBLE          0x0300
+#define E_CLASS_WRONG_CONTEXT            0x0400
+#define E_CLASS_BAD_SPECIFIC_PARAMETER   0x0500
+#define E_CLASS_REAL_TIME_ERROR          0x0600
+#define E_CLASS_DIRECTSHOW               0x0700
+#define E_CLASS_FREE                     0x0700
+
+
+/* Complete DRV error code for the general class */
+#define ED_GN           (ERROR_VALUE | E_SOURCE_DRV | E_CLASS_GENERAL)
+#define ED_CONCURRENCY                  (ED_GN | 0x01)
+#define ED_DSP_CRASHED                  (ED_GN | 0x02)
+#define ED_UNKNOWN_BOARD                (ED_GN | 0x03)
+#define ED_NOT_INSTALLED                (ED_GN | 0x04)
+#define ED_CANNOT_OPEN_SVC_MANAGER      (ED_GN | 0x05)
+#define ED_CANNOT_READ_REGISTRY         (ED_GN | 0x06)
+#define ED_DSP_VERSION_MISMATCH         (ED_GN | 0x07)
+#define ED_UNAVAILABLE_FEATURE          (ED_GN | 0x08)
+#define ED_CANCELLED                    (ED_GN | 0x09)
+#define ED_NO_RESPONSE_AT_IRQA          (ED_GN | 0x10)
+#define ED_INVALID_ADDRESS              (ED_GN | 0x11)
+#define ED_DSP_CORRUPTED                (ED_GN | 0x12)
+#define ED_PENDING_OPERATION            (ED_GN | 0x13)
+#define ED_NET_ALLOCATE_MEMORY_IMPOSSIBLE   (ED_GN | 0x14)
+#define ED_NET_REGISTER_ERROR               (ED_GN | 0x15)
+#define ED_NET_THREAD_ERROR                 (ED_GN | 0x16)
+#define ED_NET_OPEN_ERROR                   (ED_GN | 0x17)
+#define ED_NET_CLOSE_ERROR                  (ED_GN | 0x18)
+#define ED_NET_NO_MORE_PACKET               (ED_GN | 0x19)
+#define ED_NET_NO_MORE_BUFFER               (ED_GN | 0x1A)
+#define ED_NET_SEND_ERROR                   (ED_GN | 0x1B)
+#define ED_NET_RECEIVE_ERROR                (ED_GN | 0x1C)
+#define ED_NET_WRONG_MSG_SIZE               (ED_GN | 0x1D)
+#define ED_NET_WAIT_ERROR                   (ED_GN | 0x1E)
+#define ED_NET_EEPROM_ERROR                 (ED_GN | 0x1F)
+#define ED_INVALID_RS232_COM_NUMBER         (ED_GN | 0x20)
+#define ED_INVALID_RS232_INIT               (ED_GN | 0x21)
+#define ED_FILE_ERROR                       (ED_GN | 0x22)
+#define ED_INVALID_GPIO_CMD                 (ED_GN | 0x23)
+#define ED_RS232_ALREADY_OPENED             (ED_GN | 0x24)
+#define ED_RS232_NOT_OPENED                 (ED_GN | 0x25)
+#define ED_GPIO_ALREADY_OPENED              (ED_GN | 0x26)
+#define ED_GPIO_NOT_OPENED                  (ED_GN | 0x27)
+#define ED_REGISTRY_ERROR                   (ED_GN | 0x28) /* <- NCX */
+#define ED_INVALID_SERVICE                  (ED_GN | 0x29) /* <- NCX */
+
+#define ED_READ_FILE_ALREADY_OPENED        (ED_GN | 0x2a) /* <- Decalage
+                                                           * pour RCX
+                                                           * (old 0x28)
+                                                           * */
+#define ED_READ_FILE_INVALID_COMMAND       (ED_GN | 0x2b) /* ~ */
+#define ED_READ_FILE_INVALID_PARAMETER     (ED_GN | 0x2c) /* ~ */
+#define ED_READ_FILE_ALREADY_CLOSED        (ED_GN | 0x2d) /* ~ */
+#define ED_READ_FILE_NO_INFORMATION        (ED_GN | 0x2e) /* ~ */
+#define ED_READ_FILE_INVALID_HANDLE        (ED_GN | 0x2f) /* ~ */
+#define ED_READ_FILE_END_OF_FILE           (ED_GN | 0x30) /* ~ */
+#define ED_READ_FILE_ERROR                 (ED_GN | 0x31) /* ~ */
+
+#define ED_DSP_CRASHED_EXC_DSPSTACK_OVERFLOW (ED_GN | 0x32) /* <- Decalage pour
+                                                            * PCX (old 0x14) */
+#define ED_DSP_CRASHED_EXC_SYSSTACK_OVERFLOW (ED_GN | 0x33) /* ~ */
+#define ED_DSP_CRASHED_EXC_ILLEGAL           (ED_GN | 0x34) /* ~ */
+#define ED_DSP_CRASHED_EXC_TIMER_REENTRY     (ED_GN | 0x35) /* ~ */
+#define ED_DSP_CRASHED_EXC_FATAL_ERROR       (ED_GN | 0x36) /* ~ */
+
+#define ED_FLASH_PCCARD_NOT_PRESENT          (ED_GN | 0x37)
+
+#define ED_NO_CURRENT_CLOCK                  (ED_GN | 0x38)
+
+/* Complete DRV error code for real time class */
+#define ED_RT           (ERROR_VALUE | E_SOURCE_DRV | E_CLASS_REAL_TIME_ERROR)
+#define ED_DSP_TIMED_OUT                (ED_RT | 0x01)
+#define ED_DSP_CHK_TIMED_OUT            (ED_RT | 0x02)
+#define ED_STREAM_OVERRUN               (ED_RT | 0x03)
+#define ED_DSP_BUSY                     (ED_RT | 0x04)
+#define ED_DSP_SEMAPHORE_TIME_OUT       (ED_RT | 0x05)
+#define ED_BOARD_TIME_OUT               (ED_RT | 0x06)
+#define ED_XILINX_ERROR                 (ED_RT | 0x07)
+#define ED_COBRANET_ITF_NOT_RESPONDING  (ED_RT | 0x08)
+
+/* Complete BOARD error code for the invaid standard object class */
+#define EB_ISO          (ERROR_VALUE | E_SOURCE_BOARD | \
+                        E_CLASS_INVALID_STD_OBJECT)
+#define EB_INVALID_EFFECT               (EB_ISO | 0x00)
+#define EB_INVALID_PIPE                 (EB_ISO | 0x40)
+#define EB_INVALID_STREAM               (EB_ISO | 0x80)
+#define EB_INVALID_AUDIO                (EB_ISO | 0xC0)
+
+/* Complete BOARD error code for impossible resource allocation class */
+#define EB_RI           (ERROR_VALUE | E_SOURCE_BOARD | E_CLASS_RSRC_IMPOSSIBLE)
+#define EB_ALLOCATE_ALL_STREAM_TRANSFERT_BUFFERS_IMPOSSIBLE (EB_RI | 0x01)
+#define EB_ALLOCATE_PIPE_SAMPLE_BUFFER_IMPOSSIBLE           (EB_RI | 0x02)
+
+#define EB_ALLOCATE_MEM_STREAM_IMPOSSIBLE              \
+       EB_ALLOCATE_ALL_STREAM_TRANSFERT_BUFFERS_IMPOSSIBLE
+#define EB_ALLOCATE_MEM_PIPE_IMPOSSIBLE                        \
+       EB_ALLOCATE_PIPE_SAMPLE_BUFFER_IMPOSSIBLE
+
+#define EB_ALLOCATE_DIFFERED_CMD_IMPOSSIBLE     (EB_RI | 0x03)
+#define EB_TOO_MANY_DIFFERED_CMD                (EB_RI | 0x04)
+#define EB_RBUFFERS_TABLE_OVERFLOW              (EB_RI | 0x05)
+#define EB_ALLOCATE_EFFECTS_IMPOSSIBLE          (EB_RI | 0x08)
+#define EB_ALLOCATE_EFFECT_POS_IMPOSSIBLE       (EB_RI | 0x09)
+#define EB_RBUFFER_NOT_AVAILABLE                (EB_RI | 0x0A)
+#define EB_ALLOCATE_CONTEXT_LIII_IMPOSSIBLE     (EB_RI | 0x0B)
+#define EB_STATUS_DIALOG_IMPOSSIBLE             (EB_RI | 0x1D)
+#define EB_CONTROL_CMD_IMPOSSIBLE               (EB_RI | 0x1E)
+#define EB_STATUS_SEND_IMPOSSIBLE               (EB_RI | 0x1F)
+#define EB_ALLOCATE_PIPE_IMPOSSIBLE             (EB_RI | 0x40)
+#define EB_ALLOCATE_STREAM_IMPOSSIBLE           (EB_RI | 0x80)
+#define EB_ALLOCATE_AUDIO_IMPOSSIBLE            (EB_RI | 0xC0)
+
+/* Complete BOARD error code for wrong call context class */
+#define EB_WCC          (ERROR_VALUE | E_SOURCE_BOARD | E_CLASS_WRONG_CONTEXT)
+#define EB_CMD_REFUSED                  (EB_WCC | 0x00)
+#define EB_START_STREAM_REFUSED         (EB_WCC | 0xFC)
+#define EB_SPC_REFUSED                  (EB_WCC | 0xFD)
+#define EB_CSN_REFUSED                  (EB_WCC | 0xFE)
+#define EB_CSE_REFUSED                  (EB_WCC | 0xFF)
+
+
+
+
+#endif /* LX_DEFS_H */
index c262049961e15a0dd9840645f753584373dac52a..3b5ca70c9d4d2745223ac6f66102f8fd78e11161 100644 (file)
@@ -487,10 +487,14 @@ static int oxygen_hw_free(struct snd_pcm_substream *substream)
 {
        struct oxygen *chip = snd_pcm_substream_chip(substream);
        unsigned int channel = oxygen_substream_channel(substream);
+       unsigned int channel_mask = 1 << channel;
 
        spin_lock_irq(&chip->reg_lock);
-       chip->interrupt_mask &= ~(1 << channel);
+       chip->interrupt_mask &= ~channel_mask;
        oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
+
+       oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
+       oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
        spin_unlock_irq(&chip->reg_lock);
 
        return snd_pcm_lib_free_pages(substream);
index bc5ce11c8b1444fa632b81ba2684ac4f0410bd08..bf971f7cfdc652984d4824576306e96aaa88b4d6 100644 (file)
  */
 
 /*
- * Xonar Essence STX
- * -----------------
+ * Xonar Essence ST (Deluxe)/STX
+ * -----------------------------
  *
  * CMI8788:
  *
@@ -180,6 +180,8 @@ enum {
        MODEL_DX,
        MODEL_HDAV,     /* without daughterboard */
        MODEL_HDAV_H6,  /* with H6 daughterboard */
+       MODEL_ST,
+       MODEL_ST_H6,
        MODEL_STX,
 };
 
@@ -188,8 +190,10 @@ static struct pci_device_id xonar_ids[] __devinitdata = {
        { OXYGEN_PCI_SUBID(0x1043, 0x8275), .driver_data = MODEL_DX },
        { OXYGEN_PCI_SUBID(0x1043, 0x82b7), .driver_data = MODEL_D2X },
        { OXYGEN_PCI_SUBID(0x1043, 0x8314), .driver_data = MODEL_HDAV },
+       { OXYGEN_PCI_SUBID(0x1043, 0x8327), .driver_data = MODEL_DX },
        { OXYGEN_PCI_SUBID(0x1043, 0x834f), .driver_data = MODEL_D1 },
        { OXYGEN_PCI_SUBID(0x1043, 0x835c), .driver_data = MODEL_STX },
+       { OXYGEN_PCI_SUBID(0x1043, 0x835d), .driver_data = MODEL_ST },
        { OXYGEN_PCI_SUBID_BROKEN_EEPROM },
        { }
 };
@@ -210,9 +214,9 @@ MODULE_DEVICE_TABLE(pci, xonar_ids);
 #define GPIO_DX_FRONT_PANEL    0x0002
 #define GPIO_DX_INPUT_ROUTE    0x0100
 
-#define GPIO_HDAV_DB_MASK      0x0030
-#define GPIO_HDAV_DB_H6                0x0000
-#define GPIO_HDAV_DB_XX                0x0020
+#define GPIO_DB_MASK           0x0030
+#define GPIO_DB_H6             0x0000
+#define GPIO_DB_XX             0x0020
 
 #define GPIO_ST_HP_REAR                0x0002
 #define GPIO_ST_HP             0x0080
@@ -530,7 +534,7 @@ static void xonar_hdav_init(struct oxygen *chip)
        snd_component_add(chip->card, "CS5381");
 }
 
-static void xonar_stx_init(struct oxygen *chip)
+static void xonar_st_init(struct oxygen *chip)
 {
        struct xonar_data *data = chip->model_data;
 
@@ -539,12 +543,11 @@ static void xonar_stx_init(struct oxygen *chip)
                       OXYGEN_2WIRE_INTERRUPT_MASK |
                       OXYGEN_2WIRE_SPEED_FAST);
 
+       if (chip->model.private_data == MODEL_ST_H6)
+               chip->model.dac_channels = 8;
        data->anti_pop_delay = 100;
-       data->dacs = 1;
+       data->dacs = chip->model.private_data == MODEL_ST_H6 ? 4 : 1;
        data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
-       data->ext_power_reg = OXYGEN_GPI_DATA;
-       data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
-       data->ext_power_bit = GPI_DX_EXT_POWER;
        data->pcm1796_oversampling = PCM1796_OS_64;
 
        pcm1796_init(chip);
@@ -560,6 +563,17 @@ static void xonar_stx_init(struct oxygen *chip)
        snd_component_add(chip->card, "CS5381");
 }
 
+static void xonar_stx_init(struct oxygen *chip)
+{
+       struct xonar_data *data = chip->model_data;
+
+       data->ext_power_reg = OXYGEN_GPI_DATA;
+       data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
+       data->ext_power_bit = GPI_DX_EXT_POWER;
+
+       xonar_st_init(chip);
+}
+
 static void xonar_disable_output(struct oxygen *chip)
 {
        struct xonar_data *data = chip->model_data;
@@ -1021,7 +1035,8 @@ static const struct oxygen_model model_xonar_hdav = {
        .model_data_size = sizeof(struct xonar_data),
        .device_config = PLAYBACK_0_TO_I2S |
                         PLAYBACK_1_TO_SPDIF |
-                        CAPTURE_0_FROM_I2S_2,
+                        CAPTURE_0_FROM_I2S_2 |
+                        CAPTURE_1_FROM_SPDIF,
        .dac_channels = 8,
        .dac_volume_min = 255 - 2*60,
        .dac_volume_max = 255,
@@ -1034,7 +1049,7 @@ static const struct oxygen_model model_xonar_hdav = {
 static const struct oxygen_model model_xonar_st = {
        .longname = "Asus Virtuoso 100",
        .chip = "AV200",
-       .init = xonar_stx_init,
+       .init = xonar_st_init,
        .control_filter = xonar_st_control_filter,
        .mixer_init = xonar_st_mixer_init,
        .cleanup = xonar_st_cleanup,
@@ -1067,6 +1082,7 @@ static int __devinit get_xonar_model(struct oxygen *chip,
                [MODEL_D2]      = &model_xonar_d2,
                [MODEL_D2X]     = &model_xonar_d2,
                [MODEL_HDAV]    = &model_xonar_hdav,
+               [MODEL_ST]      = &model_xonar_st,
                [MODEL_STX]     = &model_xonar_st,
        };
        static const char *const names[] = {
@@ -1076,6 +1092,8 @@ static int __devinit get_xonar_model(struct oxygen *chip,
                [MODEL_D2X]     = "Xonar D2X",
                [MODEL_HDAV]    = "Xonar HDAV1.3",
                [MODEL_HDAV_H6] = "Xonar HDAV1.3+H6",
+               [MODEL_ST]      = "Xonar Essence ST",
+               [MODEL_ST_H6]   = "Xonar Essence ST+H6",
                [MODEL_STX]     = "Xonar Essence STX",
        };
        unsigned int model = id->driver_data;
@@ -1092,21 +1110,27 @@ static int __devinit get_xonar_model(struct oxygen *chip,
                chip->model.init = xonar_dx_init;
                break;
        case MODEL_HDAV:
-               oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
-                                   GPIO_HDAV_DB_MASK);
-               switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) &
-                       GPIO_HDAV_DB_MASK) {
-               case GPIO_HDAV_DB_H6:
+               oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK);
+               switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) {
+               case GPIO_DB_H6:
                        model = MODEL_HDAV_H6;
                        break;
-               case GPIO_HDAV_DB_XX:
+               case GPIO_DB_XX:
                        snd_printk(KERN_ERR "unknown daughterboard\n");
                        return -ENODEV;
                }
                break;
+       case MODEL_ST:
+               oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK);
+               switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) {
+               case GPIO_DB_H6:
+                       model = MODEL_ST_H6;
+                       break;
+               }
+               break;
        case MODEL_STX:
-               oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
-                                   GPIO_HDAV_DB_MASK);
+               chip->model.init = xonar_stx_init;
+               oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK);
                break;
        }
 
index e51a5ef1954dc6f933e51e69a59bad674daaa372..235a71e5ac8de6782f9d24027a347b5dc7852046 100644 (file)
@@ -507,41 +507,19 @@ static int riptide_reset(struct cmdif *cif, struct snd_riptide *chip);
  */
 
 static struct pci_device_id snd_riptide_ids[] = {
-       {
-        .vendor = 0x127a,.device = 0x4310,
-        .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
-        },
-       {
-        .vendor = 0x127a,.device = 0x4320,
-        .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
-        },
-       {
-        .vendor = 0x127a,.device = 0x4330,
-        .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
-        },
-       {
-        .vendor = 0x127a,.device = 0x4340,
-        .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
-        },
+       { PCI_DEVICE(0x127a, 0x4310) },
+       { PCI_DEVICE(0x127a, 0x4320) },
+       { PCI_DEVICE(0x127a, 0x4330) },
+       { PCI_DEVICE(0x127a, 0x4340) },
        {0,},
 };
 
 #ifdef SUPPORT_JOYSTICK
 static struct pci_device_id snd_riptide_joystick_ids[] __devinitdata = {
-       {
-        .vendor = 0x127a,.device = 0x4312,
-        .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
-        },
-       {
-        .vendor = 0x127a,.device = 0x4322,
-        .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
-        },
-       {.vendor = 0x127a,.device = 0x4332,
-        .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
-        },
-       {.vendor = 0x127a,.device = 0x4342,
-        .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
-        },
+       { PCI_DEVICE(0x127a, 0x4312) },
+       { PCI_DEVICE(0x127a, 0x4322) },
+       { PCI_DEVICE(0x127a, 0x4332) },
+       { PCI_DEVICE(0x127a, 0x4342) },
        {0,},
 };
 #endif
@@ -1209,12 +1187,79 @@ static int riptide_resume(struct pci_dev *pci)
 }
 #endif
 
+static int try_to_load_firmware(struct cmdif *cif, struct snd_riptide *chip)
+{
+       union firmware_version firmware = { .ret = CMDRET_ZERO };
+       int i, timeout, err;
+
+       for (i = 0; i < 2; i++) {
+               WRITE_PORT_ULONG(cif->hwport->port[i].data1, 0);
+               WRITE_PORT_ULONG(cif->hwport->port[i].data2, 0);
+       }
+       SET_GRESET(cif->hwport);
+       udelay(100);
+       UNSET_GRESET(cif->hwport);
+       udelay(100);
+
+       for (timeout = 100000; --timeout; udelay(10)) {
+               if (IS_READY(cif->hwport) && !IS_GERR(cif->hwport))
+                       break;
+       }
+       if (!timeout) {
+               snd_printk(KERN_ERR
+                          "Riptide: device not ready, audio status: 0x%x "
+                          "ready: %d gerr: %d\n",
+                          READ_AUDIO_STATUS(cif->hwport),
+                          IS_READY(cif->hwport), IS_GERR(cif->hwport));
+               return -EIO;
+       } else {
+               snd_printdd
+                       ("Riptide: audio status: 0x%x ready: %d gerr: %d\n",
+                        READ_AUDIO_STATUS(cif->hwport),
+                        IS_READY(cif->hwport), IS_GERR(cif->hwport));
+       }
+
+       SEND_GETV(cif, &firmware.ret);
+       snd_printdd("Firmware version: ASIC: %d CODEC %d AUXDSP %d PROG %d\n",
+                   firmware.firmware.ASIC, firmware.firmware.CODEC,
+                   firmware.firmware.AUXDSP, firmware.firmware.PROG);
+
+       for (i = 0; i < FIRMWARE_VERSIONS; i++) {
+               if (!memcmp(&firmware_versions[i], &firmware, sizeof(firmware)))
+                       break;
+       }
+       if (i >= FIRMWARE_VERSIONS)
+               return 0; /* no match */
+
+       if (!chip)
+               return 1; /* OK */
+
+       snd_printdd("Writing Firmware\n");
+       if (!chip->fw_entry) {
+               err = request_firmware(&chip->fw_entry, "riptide.hex",
+                                      &chip->pci->dev);
+               if (err) {
+                       snd_printk(KERN_ERR
+                                  "Riptide: Firmware not available %d\n", err);
+                       return -EIO;
+               }
+       }
+       err = loadfirmware(cif, chip->fw_entry->data, chip->fw_entry->size);
+       if (err) {
+               snd_printk(KERN_ERR
+                          "Riptide: Could not load firmware %d\n", err);
+               return err;
+       }
+
+       chip->firmware = firmware;
+
+       return 1; /* OK */
+}
+
 static int riptide_reset(struct cmdif *cif, struct snd_riptide *chip)
 {
-       int timeout, tries;
        union cmdret rptr = CMDRET_ZERO;
-       union firmware_version firmware;
-       int i, j, err, has_firmware;
+       int err, tries;
 
        if (!cif)
                return -EINVAL;
@@ -1227,75 +1272,11 @@ static int riptide_reset(struct cmdif *cif, struct snd_riptide *chip)
        cif->is_reset = 0;
 
        tries = RESET_TRIES;
-       has_firmware = 0;
-       while (has_firmware == 0 && tries-- > 0) {
-               for (i = 0; i < 2; i++) {
-                       WRITE_PORT_ULONG(cif->hwport->port[i].data1, 0);
-                       WRITE_PORT_ULONG(cif->hwport->port[i].data2, 0);
-               }
-               SET_GRESET(cif->hwport);
-               udelay(100);
-               UNSET_GRESET(cif->hwport);
-               udelay(100);
-
-               for (timeout = 100000; --timeout; udelay(10)) {
-                       if (IS_READY(cif->hwport) && !IS_GERR(cif->hwport))
-                               break;
-               }
-               if (timeout == 0) {
-                       snd_printk(KERN_ERR
-                                  "Riptide: device not ready, audio status: 0x%x ready: %d gerr: %d\n",
-                                  READ_AUDIO_STATUS(cif->hwport),
-                                  IS_READY(cif->hwport), IS_GERR(cif->hwport));
-                       return -EIO;
-               } else {
-                       snd_printdd
-                           ("Riptide: audio status: 0x%x ready: %d gerr: %d\n",
-                            READ_AUDIO_STATUS(cif->hwport),
-                            IS_READY(cif->hwport), IS_GERR(cif->hwport));
-               }
-
-               SEND_GETV(cif, &rptr);
-               for (i = 0; i < 4; i++)
-                       firmware.ret.retwords[i] = rptr.retwords[i];
-
-               snd_printdd
-                   ("Firmware version: ASIC: %d CODEC %d AUXDSP %d PROG %d\n",
-                    firmware.firmware.ASIC, firmware.firmware.CODEC,
-                    firmware.firmware.AUXDSP, firmware.firmware.PROG);
-
-               for (j = 0; j < FIRMWARE_VERSIONS; j++) {
-                       has_firmware = 1;
-                       for (i = 0; i < 4; i++) {
-                               if (firmware_versions[j].ret.retwords[i] !=
-                                   firmware.ret.retwords[i])
-                                       has_firmware = 0;
-                       }
-                       if (has_firmware)
-                               break;
-               }
-
-               if (chip != NULL && has_firmware == 0) {
-                       snd_printdd("Writing Firmware\n");
-                       if (!chip->fw_entry) {
-                               if ((err =
-                                    request_firmware(&chip->fw_entry,
-                                                     "riptide.hex",
-                                                     &chip->pci->dev)) != 0) {
-                                       snd_printk(KERN_ERR
-                                                  "Riptide: Firmware not available %d\n",
-                                                  err);
-                                       return -EIO;
-                               }
-                       }
-                       err = loadfirmware(cif, chip->fw_entry->data,
-                                          chip->fw_entry->size);
-                       if (err)
-                               snd_printk(KERN_ERR
-                                          "Riptide: Could not load firmware %d\n",
-                                          err);
-               }
-       }
+       do {
+               err = try_to_load_firmware(cif, chip);
+               if (err < 0)
+                       return err;
+       } while (!err && --tries);
 
        SEND_SACR(cif, 0, AC97_RESET);
        SEND_RACR(cif, AC97_RESET, &rptr);
@@ -1337,11 +1318,6 @@ static int riptide_reset(struct cmdif *cif, struct snd_riptide *chip)
        SET_AIE(cif->hwport);
        SET_AIACK(cif->hwport);
        cif->is_reset = 1;
-       if (chip) {
-               for (i = 0; i < 4; i++)
-                       chip->firmware.ret.retwords[i] =
-                           firmware.ret.retwords[i];
-       }
 
        return 0;
 }
@@ -2038,14 +2014,12 @@ static int __devinit snd_riptide_mixer(struct snd_riptide *chip)
 }
 
 #ifdef SUPPORT_JOYSTICK
-static int have_joystick;
-static struct pci_dev *riptide_gameport_pci;
-static struct gameport *riptide_gameport;
 
 static int __devinit
 snd_riptide_joystick_probe(struct pci_dev *pci, const struct pci_device_id *id)
 {
        static int dev;
+       struct gameport *gameport;
 
        if (dev >= SNDRV_CARDS)
                return -ENODEV;
@@ -2054,36 +2028,33 @@ snd_riptide_joystick_probe(struct pci_dev *pci, const struct pci_device_id *id)
                return -ENOENT;
        }
 
-       if (joystick_port[dev]) {
-               riptide_gameport = gameport_allocate_port();
-               if (riptide_gameport) {
-                       if (!request_region
-                           (joystick_port[dev], 8, "Riptide gameport")) {
-                               snd_printk(KERN_WARNING
-                                          "Riptide: cannot grab gameport 0x%x\n",
-                                          joystick_port[dev]);
-                               gameport_free_port(riptide_gameport);
-                               riptide_gameport = NULL;
-                       } else {
-                               riptide_gameport_pci = pci;
-                               riptide_gameport->io = joystick_port[dev];
-                               gameport_register_port(riptide_gameport);
-                       }
-               }
+       if (!joystick_port[dev++])
+               return 0;
+
+       gameport = gameport_allocate_port();
+       if (!gameport)
+               return -ENOMEM;
+       if (!request_region(joystick_port[dev], 8, "Riptide gameport")) {
+               snd_printk(KERN_WARNING
+                          "Riptide: cannot grab gameport 0x%x\n",
+                          joystick_port[dev]);
+               gameport_free_port(gameport);
+               return -EBUSY;
        }
-       dev++;
+
+       gameport->io = joystick_port[dev];
+       gameport_register_port(gameport);
+       pci_set_drvdata(pci, gameport);
        return 0;
 }
 
 static void __devexit snd_riptide_joystick_remove(struct pci_dev *pci)
 {
-       if (riptide_gameport) {
-               if (riptide_gameport_pci == pci) {
-                       release_region(riptide_gameport->io, 8);
-                       riptide_gameport_pci = NULL;
-                       gameport_unregister_port(riptide_gameport);
-                       riptide_gameport = NULL;
-               }
+       struct gameport *gameport = pci_get_drvdata(pci);
+       if (gameport) {
+               release_region(gameport->io, 8);
+               gameport_unregister_port(gameport);
+               pci_set_drvdata(pci, NULL);
        }
 }
 #endif
@@ -2094,8 +2065,8 @@ snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
        static int dev;
        struct snd_card *card;
        struct snd_riptide *chip;
-       unsigned short addr;
-       int err = 0;
+       unsigned short val;
+       int err;
 
        if (dev >= SNDRV_CARDS)
                return -ENODEV;
@@ -2107,60 +2078,63 @@ snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
        err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
        if (err < 0)
                return err;
-       if ((err = snd_riptide_create(card, pci, &chip)) < 0) {
-               snd_card_free(card);
-               return err;
-       }
+       err = snd_riptide_create(card, pci, &chip);
+       if (err < 0)
+               goto error;
        card->private_data = chip;
-       if ((err = snd_riptide_pcm(chip, 0, NULL)) < 0) {
-               snd_card_free(card);
-               return err;
-       }
-       if ((err = snd_riptide_mixer(chip)) < 0) {
-               snd_card_free(card);
-               return err;
-       }
-       pci_write_config_word(chip->pci, PCI_EXT_Legacy_Mask, LEGACY_ENABLE_ALL
-                             | (opl3_port[dev] ? LEGACY_ENABLE_FM : 0)
+       err = snd_riptide_pcm(chip, 0, NULL);
+       if (err < 0)
+               goto error;
+       err = snd_riptide_mixer(chip);
+       if (err < 0)
+               goto error;
+
+       val = LEGACY_ENABLE_ALL;
+       if (opl3_port[dev])
+               val |= LEGACY_ENABLE_FM;
 #ifdef SUPPORT_JOYSTICK
-                             | (joystick_port[dev] ? LEGACY_ENABLE_GAMEPORT :
-                                0)
+       if (joystick_port[dev])
+               val |= LEGACY_ENABLE_GAMEPORT;
 #endif
-                             | (mpu_port[dev]
-                                ? (LEGACY_ENABLE_MPU_INT | LEGACY_ENABLE_MPU) :
-                                0)
-                             | ((chip->irq << 4) & 0xF0));
-       if ((addr = mpu_port[dev]) != 0) {
-               pci_write_config_word(chip->pci, PCI_EXT_MPU_Base, addr);
-               if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_RIPTIDE,
-                                              addr, 0, chip->irq, 0,
-                                              &chip->rmidi)) < 0)
+       if (mpu_port[dev])
+               val |= LEGACY_ENABLE_MPU_INT | LEGACY_ENABLE_MPU;
+       val |= (chip->irq << 4) & 0xf0;
+       pci_write_config_word(chip->pci, PCI_EXT_Legacy_Mask, val);
+       if (mpu_port[dev]) {
+               val = mpu_port[dev];
+               pci_write_config_word(chip->pci, PCI_EXT_MPU_Base, val);
+               err = snd_mpu401_uart_new(card, 0, MPU401_HW_RIPTIDE,
+                                         val, 0, chip->irq, 0,
+                                         &chip->rmidi);
+               if (err < 0)
                        snd_printk(KERN_WARNING
                                   "Riptide: Can't Allocate MPU at 0x%x\n",
-                                  addr);
+                                  val);
                else
-                       chip->mpuaddr = addr;
+                       chip->mpuaddr = val;
        }
-       if ((addr = opl3_port[dev]) != 0) {
-               pci_write_config_word(chip->pci, PCI_EXT_FM_Base, addr);
-               if ((err = snd_opl3_create(card, addr, addr + 2,
-                                          OPL3_HW_RIPTIDE, 0,
-                                          &chip->opl3)) < 0)
+       if (opl3_port[dev]) {
+               val = opl3_port[dev];
+               pci_write_config_word(chip->pci, PCI_EXT_FM_Base, val);
+               err = snd_opl3_create(card, val, val + 2,
+                                     OPL3_HW_RIPTIDE, 0, &chip->opl3);
+               if (err < 0)
                        snd_printk(KERN_WARNING
                                   "Riptide: Can't Allocate OPL3 at 0x%x\n",
-                                  addr);
+                                  val);
                else {
-                       chip->opladdr = addr;
-                       if ((err =
-                            snd_opl3_hwdep_new(chip->opl3, 0, 1, NULL)) < 0)
+                       chip->opladdr = val;
+                       err = snd_opl3_hwdep_new(chip->opl3, 0, 1, NULL);
+                       if (err < 0)
                                snd_printk(KERN_WARNING
                                           "Riptide: Can't Allocate OPL3-HWDEP\n");
                }
        }
 #ifdef SUPPORT_JOYSTICK
-       if ((addr = joystick_port[dev]) != 0) {
-               pci_write_config_word(chip->pci, PCI_EXT_Game_Base, addr);
-               chip->gameaddr = addr;
+       if (joystick_port[dev]) {
+               val = joystick_port[dev];
+               pci_write_config_word(chip->pci, PCI_EXT_Game_Base, val);
+               chip->gameaddr = val;
        }
 #endif
 
@@ -2178,13 +2152,16 @@ snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
                 chip->opladdr);
 #endif
        snd_riptide_proc_init(chip);
-       if ((err = snd_card_register(card)) < 0) {
-               snd_card_free(card);
-               return err;
-       }
+       err = snd_card_register(card);
+       if (err < 0)
+               goto error;
        pci_set_drvdata(pci, card);
        dev++;
        return 0;
+
+ error:
+       snd_card_free(card);
+       return err;
 }
 
 static void __devexit snd_card_riptide_remove(struct pci_dev *pci)
@@ -2216,14 +2193,11 @@ static struct pci_driver joystick_driver = {
 static int __init alsa_card_riptide_init(void)
 {
        int err;
-       if ((err = pci_register_driver(&driver)) < 0)
+       err = pci_register_driver(&driver);
+       if (err < 0)
                return err;
 #if defined(SUPPORT_JOYSTICK)
-       if (pci_register_driver(&joystick_driver) < 0) {
-               have_joystick = 0;
-               snd_printk(KERN_INFO "no joystick found\n");
-       } else
-               have_joystick = 1;
+       pci_register_driver(&joystick_driver);
 #endif
        return 0;
 }
@@ -2232,8 +2206,7 @@ static void __exit alsa_card_riptide_exit(void)
 {
        pci_unregister_driver(&driver);
 #if defined(SUPPORT_JOYSTICK)
-       if (have_joystick)
-               pci_unregister_driver(&joystick_driver);
+       pci_unregister_driver(&joystick_driver);
 #endif
 }
 
index 314e73531bd176121dead403390c1cdfca4dc13f..3da5c029f93bc763ad25322ee0cfe7e1eb356728 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/pci.h>
 #include <linux/firmware.h>
 #include <linux/moduleparam.h>
+#include <linux/math64.h>
 
 #include <sound/core.h>
 #include <sound/control.h>
@@ -402,9 +403,9 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin");
 #define HDSP_DMA_AREA_BYTES ((HDSP_MAX_CHANNELS+1) * HDSP_CHANNEL_BUFFER_BYTES)
 #define HDSP_DMA_AREA_KILOBYTES (HDSP_DMA_AREA_BYTES/1024)
 
-/* use hotplug firmeare loader? */
+/* use hotplug firmware loader? */
 #if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
-#if !defined(HDSP_USE_HWDEP_LOADER) && !defined(CONFIG_SND_HDSP)
+#if !defined(HDSP_USE_HWDEP_LOADER)
 #define HDSP_FW_LOADER
 #endif
 #endif
@@ -1047,7 +1048,6 @@ static int hdsp_set_interrupt_interval(struct hdsp *s, unsigned int frames)
 static void hdsp_set_dds_value(struct hdsp *hdsp, int rate)
 {
        u64 n;
-       u32 r;
 
        if (rate >= 112000)
                rate /= 4;
@@ -1055,7 +1055,7 @@ static void hdsp_set_dds_value(struct hdsp *hdsp, int rate)
                rate /= 2;
 
        n = DDS_NUMERATOR;
-       div64_32(&n, rate, &r);
+       n = div_u64(n, rate);
        /* n should be less than 2^32 for being written to FREQ register */
        snd_BUG_ON(n >> 32);
        /* HDSP_freqReg and HDSP_resetPointer are the same, so keep the DDS
@@ -3097,7 +3097,6 @@ static int snd_hdsp_get_adat_sync_check(struct snd_kcontrol *kcontrol, struct sn
 static int hdsp_dds_offset(struct hdsp *hdsp)
 {
        u64 n;
-       u32 r;
        unsigned int dds_value = hdsp->dds_value;
        int system_sample_rate = hdsp->system_sample_rate;
 
@@ -3109,7 +3108,7 @@ static int hdsp_dds_offset(struct hdsp *hdsp)
         * dds_value = n / rate
         * rate = n / dds_value
         */
-       div64_32(&n, dds_value, &r);
+       n = div_u64(n, dds_value);
        if (system_sample_rate >= 112000)
                n *= 4;
        else if (system_sample_rate >= 56000)
index bac2dc0c5d85698bd7bb6796525b6f622d2fbe3d..0dce331a2a3b105e565e7deaa315fdb39c575d4d 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/moduleparam.h>
 #include <linux/slab.h>
 #include <linux/pci.h>
+#include <linux/math64.h>
 #include <asm/io.h>
 
 #include <sound/core.h>
@@ -831,7 +832,6 @@ static int hdspm_set_interrupt_interval(struct hdspm * s, unsigned int frames)
 static void hdspm_set_dds_value(struct hdspm *hdspm, int rate)
 {
        u64 n;
-       u32 r;
        
        if (rate >= 112000)
                rate /= 4;
@@ -844,7 +844,7 @@ static void hdspm_set_dds_value(struct hdspm *hdspm, int rate)
         */        
        /* n = 104857600000000ULL; */ /*  =  2^20 * 10^8 */
        n = 110100480000000ULL;    /* Value checked for AES32 and MADI */
-       div64_32(&n, rate, &r);
+       n = div_u64(n, rate);
        /* n should be less than 2^32 for being written to FREQ register */
        snd_BUG_ON(n >> 32);
        hdspm_write(hdspm, HDSPM_freqReg, (u32)n);
index 80df9b1f651e01fbfec1302d77cd8673308a7b31..2cc0eda4f20ea4cf760fb7158617a46c69e6da6e 100644 (file)
@@ -477,7 +477,7 @@ static int snd_pmac_awacs_put_master_amp(struct snd_kcontrol *kcontrol,
 #define AMP_CH_SPK     0
 #define AMP_CH_HD      1
 
-static struct snd_kcontrol_new snd_pmac_awacs_amp_vol[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_amp_vol[] __devinitdata = {
        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
          .name = "PC Speaker Playback Volume",
          .info = snd_pmac_awacs_info_volume_amp,
@@ -514,7 +514,7 @@ static struct snd_kcontrol_new snd_pmac_awacs_amp_vol[] __initdata = {
        },
 };
 
-static struct snd_kcontrol_new snd_pmac_awacs_amp_hp_sw __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_amp_hp_sw __devinitdata = {
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
        .name = "Headphone Playback Switch",
        .info = snd_pmac_boolean_stereo_info,
@@ -523,7 +523,7 @@ static struct snd_kcontrol_new snd_pmac_awacs_amp_hp_sw __initdata = {
        .private_value = AMP_CH_HD,
 };
 
-static struct snd_kcontrol_new snd_pmac_awacs_amp_spk_sw __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_amp_spk_sw __devinitdata = {
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
        .name = "PC Speaker Playback Switch",
        .info = snd_pmac_boolean_stereo_info,
@@ -595,46 +595,46 @@ static int snd_pmac_screamer_mic_boost_put(struct snd_kcontrol *kcontrol,
 /*
  * lists of mixer elements
  */
-static struct snd_kcontrol_new snd_pmac_awacs_mixers[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_mixers[] __devinitdata = {
        AWACS_SWITCH("Master Capture Switch", 1, SHIFT_LOOPTHRU, 0),
        AWACS_VOLUME("Master Capture Volume", 0, 4, 0),
 /*     AWACS_SWITCH("Unknown Playback Switch", 6, SHIFT_PAROUT0, 0), */
 };
 
-static struct snd_kcontrol_new snd_pmac_screamer_mixers_beige[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_screamer_mixers_beige[] __devinitdata = {
        AWACS_VOLUME("Master Playback Volume", 2, 6, 1),
        AWACS_VOLUME("Play-through Playback Volume", 5, 6, 1),
        AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
        AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_LINE, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_screamer_mixers_lo[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_screamer_mixers_lo[] __devinitdata = {
        AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
 };
 
-static struct snd_kcontrol_new snd_pmac_screamer_mixers_imac[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_screamer_mixers_imac[] __devinitdata = {
        AWACS_VOLUME("Play-through Playback Volume", 5, 6, 1),
        AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_screamer_mixers_g4agp[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_screamer_mixers_g4agp[] __devinitdata = {
        AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
        AWACS_VOLUME("Master Playback Volume", 5, 6, 1),
        AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
        AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac7500[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac7500[] __devinitdata = {
        AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
        AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
        AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac5500[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac5500[] __devinitdata = {
        AWACS_VOLUME("Headphone Playback Volume", 2, 6, 1),
 };
 
-static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac[] __devinitdata = {
        AWACS_VOLUME("Master Playback Volume", 2, 6, 1),
        AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
 };
@@ -642,34 +642,34 @@ static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac[] __initdata = {
 /* FIXME: is this correct order?
  * screamer (powerbook G3 pismo) seems to have different bits...
  */
-static struct snd_kcontrol_new snd_pmac_awacs_mixers2[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_mixers2[] __devinitdata = {
        AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_LINE, 0),
        AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_MIC, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_screamer_mixers2[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_screamer_mixers2[] __devinitdata = {
        AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
        AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_LINE, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_awacs_mixers2_pmac5500[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_mixers2_pmac5500[] __devinitdata = {
        AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_awacs_master_sw __initdata =
+static struct snd_kcontrol_new snd_pmac_awacs_master_sw __devinitdata =
 AWACS_SWITCH("Master Playback Switch", 1, SHIFT_HDMUTE, 1);
 
-static struct snd_kcontrol_new snd_pmac_awacs_master_sw_imac __initdata =
+static struct snd_kcontrol_new snd_pmac_awacs_master_sw_imac __devinitdata =
 AWACS_SWITCH("Line out Playback Switch", 1, SHIFT_HDMUTE, 1);
 
-static struct snd_kcontrol_new snd_pmac_awacs_master_sw_pmac5500 __initdata =
+static struct snd_kcontrol_new snd_pmac_awacs_master_sw_pmac5500 __devinitdata =
 AWACS_SWITCH("Headphone Playback Switch", 1, SHIFT_HDMUTE, 1);
 
-static struct snd_kcontrol_new snd_pmac_awacs_mic_boost[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_mic_boost[] __devinitdata = {
        AWACS_SWITCH("Mic Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_screamer_mic_boost[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_screamer_mic_boost[] __devinitdata = {
        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
          .name = "Mic Boost Capture Volume",
          .info = snd_pmac_screamer_mic_boost_info,
@@ -678,34 +678,34 @@ static struct snd_kcontrol_new snd_pmac_screamer_mic_boost[] __initdata = {
        },
 };
 
-static struct snd_kcontrol_new snd_pmac_awacs_mic_boost_pmac7500[] __initdata =
+static struct snd_kcontrol_new snd_pmac_awacs_mic_boost_pmac7500[] __devinitdata =
 {
        AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_screamer_mic_boost_beige[] __initdata =
+static struct snd_kcontrol_new snd_pmac_screamer_mic_boost_beige[] __devinitdata =
 {
        AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
        AWACS_SWITCH("CD Boost Capture Switch", 6, SHIFT_MIC_BOOST, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_screamer_mic_boost_imac[] __initdata =
+static struct snd_kcontrol_new snd_pmac_screamer_mic_boost_imac[] __devinitdata =
 {
        AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
        AWACS_SWITCH("Mic Boost Capture Switch", 6, SHIFT_MIC_BOOST, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_awacs_speaker_vol[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_speaker_vol[] __devinitdata = {
        AWACS_VOLUME("PC Speaker Playback Volume", 4, 6, 1),
 };
 
-static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw __initdata =
+static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw __devinitdata =
 AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1);
 
-static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac1 __initdata =
+static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac1 __devinitdata =
 AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_PAROUT1, 1);
 
-static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac2 __initdata =
+static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac2 __devinitdata =
 AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_PAROUT1, 0);
 
 
@@ -872,7 +872,7 @@ static void snd_pmac_awacs_update_automute(struct snd_pmac *chip, int do_notify)
 /*
  * initialize chip
  */
-int __init
+int __devinit
 snd_pmac_awacs_init(struct snd_pmac *chip)
 {
        int pm7500 = IS_PM7500;
index 89f5c328acfe04952c1dcb4a2c0c66b0a16a70ea..a9d350789f5563a22f1f3184826125599ef8b36e 100644 (file)
@@ -215,7 +215,7 @@ static struct snd_kcontrol_new snd_pmac_beep_mixer = {
 };
 
 /* Initialize beep stuff */
-int __init snd_pmac_attach_beep(struct snd_pmac *chip)
+int __devinit snd_pmac_attach_beep(struct snd_pmac *chip)
 {
        struct pmac_beep *beep;
        struct input_dev *input_dev;
index 45a76297c38d2224abd7fee9b3994f04fd2b2dcf..16ed240e423cfc5ebdb3da1272915e24f0a9c509 100644 (file)
@@ -46,12 +46,12 @@ snd_pmac_burgundy_extend_wait(struct snd_pmac *chip)
        timeout = 50;
        while (!(in_le32(&chip->awacs->codec_stat) & MASK_EXTEND) && timeout--)
                udelay(1);
-       if (! timeout)
+       if (timeout < 0)
                printk(KERN_DEBUG "burgundy_extend_wait: timeout #1\n");
        timeout = 50;
        while ((in_le32(&chip->awacs->codec_stat) & MASK_EXTEND) && timeout--)
                udelay(1);
-       if (! timeout)
+       if (timeout < 0)
                printk(KERN_DEBUG "burgundy_extend_wait: timeout #2\n");
 }
 
@@ -468,7 +468,7 @@ static int snd_pmac_burgundy_put_switch_b(struct snd_kcontrol *kcontrol,
 /*
  * Burgundy mixers
  */
-static struct snd_kcontrol_new snd_pmac_burgundy_mixers[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_burgundy_mixers[] __devinitdata = {
        BURGUNDY_VOLUME_W("Master Playback Volume", 0,
                        MASK_ADDR_BURGUNDY_MASTER_VOLUME, 8),
        BURGUNDY_VOLUME_W("CD Capture Volume", 0,
@@ -496,7 +496,7 @@ static struct snd_kcontrol_new snd_pmac_burgundy_mixers[] __initdata = {
  */    BURGUNDY_SWITCH_B("PCM Capture Switch", 0,
                        MASK_ADDR_BURGUNDY_HOSTIFEH, 0x01, 0, 0)
 };
-static struct snd_kcontrol_new snd_pmac_burgundy_mixers_imac[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_burgundy_mixers_imac[] __devinitdata = {
        BURGUNDY_VOLUME_W("Line in Capture Volume", 0,
                        MASK_ADDR_BURGUNDY_VOLLINE, 16),
        BURGUNDY_VOLUME_W("Mic Capture Volume", 0,
@@ -522,7 +522,7 @@ static struct snd_kcontrol_new snd_pmac_burgundy_mixers_imac[] __initdata = {
        BURGUNDY_SWITCH_B("Mic Boost Capture Switch", 0,
                        MASK_ADDR_BURGUNDY_INPBOOST, 0x40, 0x80, 1)
 };
-static struct snd_kcontrol_new snd_pmac_burgundy_mixers_pmac[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_burgundy_mixers_pmac[] __devinitdata = {
        BURGUNDY_VOLUME_W("Line in Capture Volume", 0,
                        MASK_ADDR_BURGUNDY_VOLMIC, 16),
        BURGUNDY_VOLUME_B("Line in Gain Capture Volume", 0,
@@ -538,33 +538,33 @@ static struct snd_kcontrol_new snd_pmac_burgundy_mixers_pmac[] __initdata = {
 /*     BURGUNDY_SWITCH_B("Line in Boost Capture Switch", 0,
  *             MASK_ADDR_BURGUNDY_INPBOOST, 0x40, 0x80, 1) */
 };
-static struct snd_kcontrol_new snd_pmac_burgundy_master_sw_imac __initdata =
+static struct snd_kcontrol_new snd_pmac_burgundy_master_sw_imac __devinitdata =
 BURGUNDY_SWITCH_B("Master Playback Switch", 0,
        MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
        BURGUNDY_OUTPUT_LEFT | BURGUNDY_LINEOUT_LEFT | BURGUNDY_HP_LEFT,
        BURGUNDY_OUTPUT_RIGHT | BURGUNDY_LINEOUT_RIGHT | BURGUNDY_HP_RIGHT, 1);
-static struct snd_kcontrol_new snd_pmac_burgundy_master_sw_pmac __initdata =
+static struct snd_kcontrol_new snd_pmac_burgundy_master_sw_pmac __devinitdata =
 BURGUNDY_SWITCH_B("Master Playback Switch", 0,
        MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
        BURGUNDY_OUTPUT_INTERN
        | BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1);
-static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_imac __initdata =
+static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_imac __devinitdata =
 BURGUNDY_SWITCH_B("PC Speaker Playback Switch", 0,
        MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
        BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1);
-static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_pmac __initdata =
+static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_pmac __devinitdata =
 BURGUNDY_SWITCH_B("PC Speaker Playback Switch", 0,
        MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
        BURGUNDY_OUTPUT_INTERN, 0, 0);
-static struct snd_kcontrol_new snd_pmac_burgundy_line_sw_imac __initdata =
+static struct snd_kcontrol_new snd_pmac_burgundy_line_sw_imac __devinitdata =
 BURGUNDY_SWITCH_B("Line out Playback Switch", 0,
        MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
        BURGUNDY_LINEOUT_LEFT, BURGUNDY_LINEOUT_RIGHT, 1);
-static struct snd_kcontrol_new snd_pmac_burgundy_line_sw_pmac __initdata =
+static struct snd_kcontrol_new snd_pmac_burgundy_line_sw_pmac __devinitdata =
 BURGUNDY_SWITCH_B("Line out Playback Switch", 0,
        MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
        BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1);
-static struct snd_kcontrol_new snd_pmac_burgundy_hp_sw_imac __initdata =
+static struct snd_kcontrol_new snd_pmac_burgundy_hp_sw_imac __devinitdata =
 BURGUNDY_SWITCH_B("Headphone Playback Switch", 0,
        MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
        BURGUNDY_HP_LEFT, BURGUNDY_HP_RIGHT, 1);
@@ -618,7 +618,7 @@ static void snd_pmac_burgundy_update_automute(struct snd_pmac *chip, int do_noti
 /*
  * initialize burgundy
  */
-int __init snd_pmac_burgundy_init(struct snd_pmac *chip)
+int __devinit snd_pmac_burgundy_init(struct snd_pmac *chip)
 {
        int imac = machine_is_compatible("iMac");
        int i, err;
index f8d478c2da62b252d719b067ddff75805a8aa561..24200b7bdacec5a74ab335fbb4ec0fc78062d2d3 100644 (file)
@@ -244,7 +244,7 @@ static void daca_cleanup(struct snd_pmac *chip)
 }
 
 /* exported */
-int __init snd_pmac_daca_init(struct snd_pmac *chip)
+int __devinit snd_pmac_daca_init(struct snd_pmac *chip)
 {
        int i, err;
        struct pmac_daca *mix;
index a5afb2682e7fe7f5196621d62f84379a112dfde2..835fa19ed461864018fb152aa3c41eb57261cb4b 100644 (file)
 static struct pmac_keywest *keywest_ctx;
 
 
-#ifndef i2c_device_name
-#define i2c_device_name(x)     ((x)->name)
-#endif
-
 static int keywest_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
@@ -56,7 +52,7 @@ static int keywest_attach_adapter(struct i2c_adapter *adapter)
        if (! keywest_ctx)
                return -EINVAL;
 
-       if (strncmp(i2c_device_name(adapter), "mac-io", 6))
+       if (strncmp(adapter->name, "mac-io", 6))
                return 0; /* ignored */
 
        memset(&info, 0, sizeof(struct i2c_board_info));
@@ -109,7 +105,7 @@ void snd_pmac_keywest_cleanup(struct pmac_keywest *i2c)
        }
 }
 
-int __init snd_pmac_tumbler_post_init(void)
+int __devinit snd_pmac_tumbler_post_init(void)
 {
        int err;
        
@@ -124,7 +120,7 @@ int __init snd_pmac_tumbler_post_init(void)
 }
 
 /* exported */
-int __init snd_pmac_keywest_init(struct pmac_keywest *i2c)
+int __devinit snd_pmac_keywest_init(struct pmac_keywest *i2c)
 {
        int err;
 
index 9b4e9c316695e2b44cc08099427d3cf015695876..7bc492ee77ecf1978f659e5180e62e55fb990cd9 100644 (file)
@@ -702,7 +702,7 @@ static struct snd_pcm_ops snd_pmac_capture_ops = {
        .pointer =      snd_pmac_capture_pointer,
 };
 
-int __init snd_pmac_pcm_new(struct snd_pmac *chip)
+int __devinit snd_pmac_pcm_new(struct snd_pmac *chip)
 {
        struct snd_pcm *pcm;
        int err;
@@ -908,7 +908,7 @@ static int snd_pmac_dev_free(struct snd_device *device)
  * check the machine support byteswap (little-endian)
  */
 
-static void __init detect_byte_swap(struct snd_pmac *chip)
+static void __devinit detect_byte_swap(struct snd_pmac *chip)
 {
        struct device_node *mio;
 
@@ -934,7 +934,7 @@ static void __init detect_byte_swap(struct snd_pmac *chip)
 /*
  * detect a sound chip
  */
-static int __init snd_pmac_detect(struct snd_pmac *chip)
+static int __devinit snd_pmac_detect(struct snd_pmac *chip)
 {
        struct device_node *sound;
        struct device_node *dn;
@@ -1143,7 +1143,7 @@ static int pmac_hp_detect_get(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
-static struct snd_kcontrol_new auto_mute_controls[] __initdata = {
+static struct snd_kcontrol_new auto_mute_controls[] __devinitdata = {
        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
          .name = "Auto Mute Switch",
          .info = snd_pmac_boolean_mono_info,
@@ -1158,7 +1158,7 @@ static struct snd_kcontrol_new auto_mute_controls[] __initdata = {
        },
 };
 
-int __init snd_pmac_add_automute(struct snd_pmac *chip)
+int __devinit snd_pmac_add_automute(struct snd_pmac *chip)
 {
        int err;
        chip->auto_mute = 1;
@@ -1175,7 +1175,7 @@ int __init snd_pmac_add_automute(struct snd_pmac *chip)
 /*
  * create and detect a pmac chip record
  */
-int __init snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
+int __devinit snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
 {
        struct snd_pmac *chip;
        struct device_node *np;
index f361c26506aacefd1b5a683c232163294c6f9480..53c81a547613ee0482c8dd64b9c3bb0a7b88f2e6 100644 (file)
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
 #include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/io.h>
 #include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include <sound/asound.h>
+#include <sound/control.h>
 #include <sound/core.h>
 #include <sound/initval.h>
-#include <sound/pcm.h>
-#include <sound/asound.h>
 #include <sound/memalloc.h>
+#include <sound/pcm.h>
 #include <sound/pcm_params.h>
-#include <sound/control.h>
-#include <linux/dmapool.h>
-#include <linux/dma-mapping.h>
-#include <asm/firmware.h>
+
 #include <asm/dma.h>
+#include <asm/firmware.h>
 #include <asm/lv1call.h>
 #include <asm/ps3.h>
 #include <asm/ps3av.h>
 
-#include "snd_ps3_reg.h"
 #include "snd_ps3.h"
+#include "snd_ps3_reg.h"
 
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("PS3 sound driver");
-MODULE_AUTHOR("Sony Computer Entertainment Inc.");
-
-/* module  entries */
-static int __init snd_ps3_init(void);
-static void __exit snd_ps3_exit(void);
-
-/* ALSA snd driver ops */
-static int snd_ps3_pcm_open(struct snd_pcm_substream *substream);
-static int snd_ps3_pcm_close(struct snd_pcm_substream *substream);
-static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream);
-static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream,
-                                int cmd);
-static snd_pcm_uframes_t snd_ps3_pcm_pointer(struct snd_pcm_substream
-                                            *substream);
-static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream,
-                                struct snd_pcm_hw_params *hw_params);
-static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream);
-
-
-/* ps3_system_bus_driver entries */
-static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev);
-static int snd_ps3_driver_remove(struct ps3_system_bus_device *dev);
-
-/* address setup */
-static int snd_ps3_map_mmio(void);
-static void snd_ps3_unmap_mmio(void);
-static int snd_ps3_allocate_irq(void);
-static void snd_ps3_free_irq(void);
-static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start);
-
-/* interrupt handler */
-static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id);
-
-
-/* set sampling rate/format */
-static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream);
-/* take effect parameter change */
-static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card);
-/* initialize avsetting and take it effect */
-static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card);
-/* setup dma */
-static int snd_ps3_program_dma(struct snd_ps3_card_info *card,
-                              enum snd_ps3_dma_filltype filltype);
-static void snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card);
-
-static dma_addr_t v_to_bus(struct snd_ps3_card_info *, void  *vaddr, int ch);
-
-
-module_init(snd_ps3_init);
-module_exit(snd_ps3_exit);
 
 /*
  * global
@@ -165,25 +115,13 @@ static const struct snd_pcm_hardware snd_ps3_pcm_hw = {
        .fifo_size = PS3_AUDIO_FIFO_SIZE
 };
 
-static struct snd_pcm_ops snd_ps3_pcm_spdif_ops =
-{
-       .open = snd_ps3_pcm_open,
-       .close = snd_ps3_pcm_close,
-       .prepare = snd_ps3_pcm_prepare,
-       .ioctl = snd_pcm_lib_ioctl,
-       .trigger = snd_ps3_pcm_trigger,
-       .pointer = snd_ps3_pcm_pointer,
-       .hw_params = snd_ps3_pcm_hw_params,
-       .hw_free = snd_ps3_pcm_hw_free
-};
-
 static int snd_ps3_verify_dma_stop(struct snd_ps3_card_info *card,
                                   int count, int force_stop)
 {
        int dma_ch, done, retries, stop_forced = 0;
        uint32_t status;
 
-       for (dma_ch = 0; dma_ch < 8; dma_ch ++) {
+       for (dma_ch = 0; dma_ch < 8; dma_ch++) {
                retries = count;
                do {
                        status = read_reg(PS3_AUDIO_KICK(dma_ch)) &
@@ -259,9 +197,7 @@ static void snd_ps3_kick_dma(struct snd_ps3_card_info *card)
 /*
  * convert virtual addr to ioif bus addr.
  */
-static dma_addr_t v_to_bus(struct snd_ps3_card_info *card,
-                          void * paddr,
-                          int ch)
+static dma_addr_t v_to_bus(struct snd_ps3_card_info *card, void *paddr, int ch)
 {
        return card->dma_start_bus_addr[ch] +
                (paddr - card->dma_start_vaddr[ch]);
@@ -321,7 +257,7 @@ static int snd_ps3_program_dma(struct snd_ps3_card_info *card,
        spin_lock_irqsave(&card->dma_lock, irqsave);
        for (ch = 0; ch < 2; ch++) {
                start_vaddr = card->dma_next_transfer_vaddr[0];
-               for (stage = 0; stage < fill_stages; stage ++) {
+               for (stage = 0; stage < fill_stages; stage++) {
                        dma_ch = stage * 2 + ch;
                        if (silent)
                                dma_addr = card->null_buffer_start_dma_addr;
@@ -371,6 +307,71 @@ static int snd_ps3_program_dma(struct snd_ps3_card_info *card,
        return 0;
 }
 
+/*
+ * Interrupt handler
+ */
+static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id)
+{
+
+       uint32_t port_intr;
+       int underflow_occured = 0;
+       struct snd_ps3_card_info *card = dev_id;
+
+       if (!card->running) {
+               update_reg(PS3_AUDIO_AX_IS, 0);
+               update_reg(PS3_AUDIO_INTR_0, 0);
+               return IRQ_HANDLED;
+       }
+
+       port_intr = read_reg(PS3_AUDIO_AX_IS);
+       /*
+        *serial buffer empty detected (every 4 times),
+        *program next dma and kick it
+        */
+       if (port_intr & PS3_AUDIO_AX_IE_ASOBEIE(0)) {
+               write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBEIE(0));
+               if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) {
+                       write_reg(PS3_AUDIO_AX_IS, port_intr);
+                       underflow_occured = 1;
+               }
+               if (card->silent) {
+                       /* we are still in silent time */
+                       snd_ps3_program_dma(card,
+                               (underflow_occured) ?
+                               SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL :
+                               SND_PS3_DMA_FILLTYPE_SILENT_RUNNING);
+                       snd_ps3_kick_dma(card);
+                       card->silent--;
+               } else {
+                       snd_ps3_program_dma(card,
+                               (underflow_occured) ?
+                               SND_PS3_DMA_FILLTYPE_FIRSTFILL :
+                               SND_PS3_DMA_FILLTYPE_RUNNING);
+                       snd_ps3_kick_dma(card);
+                       snd_pcm_period_elapsed(card->substream);
+               }
+       } else if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) {
+               write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBUIE(0));
+               /*
+                * serial out underflow, but buffer empty not detected.
+                * in this case, fill fifo with 0 to recover.  After
+                * filling dummy data, serial automatically start to
+                * consume them and then will generate normal buffer
+                * empty interrupts.
+                * If both buffer underflow and buffer empty are occured,
+                * it is better to do nomal data transfer than empty one
+                */
+               snd_ps3_program_dma(card,
+                                   SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
+               snd_ps3_kick_dma(card);
+               snd_ps3_program_dma(card,
+                                   SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
+               snd_ps3_kick_dma(card);
+       }
+       /* clear interrupt cause */
+       return IRQ_HANDLED;
+};
+
 /*
  * audio mute on/off
  * mute_on : 0 output enabled
@@ -381,6 +382,142 @@ static int snd_ps3_mute(int mute_on)
        return ps3av_audio_mute(mute_on);
 }
 
+/*
+ * av setting
+ * NOTE: calling this function may generate audio interrupt.
+ */
+static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card)
+{
+       int ret, retries, i;
+       pr_debug("%s: start\n", __func__);
+
+       ret = ps3av_set_audio_mode(card->avs.avs_audio_ch,
+                                 card->avs.avs_audio_rate,
+                                 card->avs.avs_audio_width,
+                                 card->avs.avs_audio_format,
+                                 card->avs.avs_audio_source);
+       /*
+        * Reset the following unwanted settings:
+        */
+
+       /* disable all 3wire buffers */
+       update_mask_reg(PS3_AUDIO_AO_3WMCTRL,
+                       ~(PS3_AUDIO_AO_3WMCTRL_ASOEN(0) |
+                         PS3_AUDIO_AO_3WMCTRL_ASOEN(1) |
+                         PS3_AUDIO_AO_3WMCTRL_ASOEN(2) |
+                         PS3_AUDIO_AO_3WMCTRL_ASOEN(3)),
+                       0);
+       wmb();  /* ensure the hardware sees the change */
+       /* wait for actually stopped */
+       retries = 1000;
+       while ((read_reg(PS3_AUDIO_AO_3WMCTRL) &
+               (PS3_AUDIO_AO_3WMCTRL_ASORUN(0) |
+                PS3_AUDIO_AO_3WMCTRL_ASORUN(1) |
+                PS3_AUDIO_AO_3WMCTRL_ASORUN(2) |
+                PS3_AUDIO_AO_3WMCTRL_ASORUN(3))) &&
+              --retries) {
+               udelay(1);
+       }
+
+       /* reset buffer pointer */
+       for (i = 0; i < 4; i++) {
+               update_reg(PS3_AUDIO_AO_3WCTRL(i),
+                          PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET);
+               udelay(10);
+       }
+       wmb(); /* ensure the hardware actually start resetting */
+
+       /* enable 3wire#0 buffer */
+       update_reg(PS3_AUDIO_AO_3WMCTRL, PS3_AUDIO_AO_3WMCTRL_ASOEN(0));
+
+
+       /* In 24bit mode,ALSA inserts a zero byte at first byte of per sample */
+       update_mask_reg(PS3_AUDIO_AO_3WCTRL(0),
+                       ~PS3_AUDIO_AO_3WCTRL_ASODF,
+                       PS3_AUDIO_AO_3WCTRL_ASODF_LSB);
+       update_mask_reg(PS3_AUDIO_AO_SPDCTRL(0),
+                       ~PS3_AUDIO_AO_SPDCTRL_SPODF,
+                       PS3_AUDIO_AO_SPDCTRL_SPODF_LSB);
+       /* ensure all the setting above is written back to register */
+       wmb();
+       /* avsetting driver altered AX_IE, caller must reset it if you want */
+       pr_debug("%s: end\n", __func__);
+       return ret;
+}
+
+/*
+ *  set sampling rate according to the substream
+ */
+static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream)
+{
+       struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
+       struct snd_ps3_avsetting_info avs;
+       int ret;
+
+       avs = card->avs;
+
+       pr_debug("%s: called freq=%d width=%d\n", __func__,
+                substream->runtime->rate,
+                snd_pcm_format_width(substream->runtime->format));
+
+       pr_debug("%s: before freq=%d width=%d\n", __func__,
+                card->avs.avs_audio_rate, card->avs.avs_audio_width);
+
+       /* sample rate */
+       switch (substream->runtime->rate) {
+       case 44100:
+               avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_44K;
+               break;
+       case 48000:
+               avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K;
+               break;
+       case 88200:
+               avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_88K;
+               break;
+       case 96000:
+               avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_96K;
+               break;
+       default:
+               pr_info("%s: invalid rate %d\n", __func__,
+                       substream->runtime->rate);
+               return 1;
+       }
+
+       /* width */
+       switch (snd_pcm_format_width(substream->runtime->format)) {
+       case 16:
+               avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16;
+               break;
+       case 24:
+               avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_24;
+               break;
+       default:
+               pr_info("%s: invalid width %d\n", __func__,
+                       snd_pcm_format_width(substream->runtime->format));
+               return 1;
+       }
+
+       memcpy(avs.avs_cs_info, ps3av_mode_cs_info, 8);
+
+       if (memcmp(&card->avs, &avs, sizeof(avs))) {
+               pr_debug("%s: after freq=%d width=%d\n", __func__,
+                        card->avs.avs_audio_rate, card->avs.avs_audio_width);
+
+               card->avs = avs;
+               snd_ps3_change_avsetting(card);
+               ret = 0;
+       } else
+               ret = 1;
+
+       /* check CS non-audio bit and mute accordingly */
+       if (avs.avs_cs_info[0] & 0x02)
+               ps3av_audio_mute_analog(1); /* mute if non-audio */
+       else
+               ps3av_audio_mute_analog(0);
+
+       return ret;
+}
+
 /*
  * PCM operators
  */
@@ -406,6 +543,13 @@ static int snd_ps3_pcm_open(struct snd_pcm_substream *substream)
        return 0;
 };
 
+static int snd_ps3_pcm_close(struct snd_pcm_substream *substream)
+{
+       /* mute on */
+       snd_ps3_mute(1);
+       return 0;
+};
+
 static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream,
                                 struct snd_pcm_hw_params *hw_params)
 {
@@ -417,6 +561,13 @@ static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream,
        return 0;
 };
 
+static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       int ret;
+       ret = snd_pcm_lib_free_pages(substream);
+       return ret;
+};
+
 static int snd_ps3_delay_to_bytes(struct snd_pcm_substream *substream,
                                  unsigned int delay_ms)
 {
@@ -473,284 +624,88 @@ static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream)
                card->dma_last_transfer_vaddr[SND_PS3_CH_R] =
                        card->dma_next_transfer_vaddr[SND_PS3_CH_R] =
                        card->dma_start_vaddr[SND_PS3_CH_R] =
-                       runtime->dma_area + (runtime->dma_bytes / 2);
-               card->dma_start_bus_addr[SND_PS3_CH_R] =
-                       runtime->dma_addr + (runtime->dma_bytes / 2);
-
-               pr_debug("%s: vaddr=%p bus=%#llx\n", __func__,
-                        card->dma_start_vaddr[SND_PS3_CH_L],
-                        card->dma_start_bus_addr[SND_PS3_CH_L]);
-
-       }
-       spin_unlock_irqrestore(&card->dma_lock, irqsave);
-
-       /* ensure the hardware sees the change */
-       mb();
-
-       return 0;
-};
-
-static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream,
-                              int cmd)
-{
-       struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
-       int ret = 0;
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-               /* clear outstanding interrupts  */
-               update_reg(PS3_AUDIO_AX_IS, 0);
-
-               spin_lock(&card->dma_lock);
-               {
-                       card->running = 1;
-               }
-               spin_unlock(&card->dma_lock);
-
-               snd_ps3_program_dma(card,
-                                   SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
-               snd_ps3_kick_dma(card);
-               while (read_reg(PS3_AUDIO_KICK(7)) &
-                      PS3_AUDIO_KICK_STATUS_MASK) {
-                       udelay(1);
-               }
-               snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_RUNNING);
-               snd_ps3_kick_dma(card);
-               break;
-
-       case SNDRV_PCM_TRIGGER_STOP:
-               spin_lock(&card->dma_lock);
-               {
-                       card->running = 0;
-               }
-               spin_unlock(&card->dma_lock);
-               snd_ps3_wait_for_dma_stop(card);
-               break;
-       default:
-               break;
-
-       }
-
-       return ret;
-};
-
-/*
- * report current pointer
- */
-static snd_pcm_uframes_t snd_ps3_pcm_pointer(
-       struct snd_pcm_substream *substream)
-{
-       struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
-       size_t bytes;
-       snd_pcm_uframes_t ret;
-
-       spin_lock(&card->dma_lock);
-       {
-               bytes = (size_t)(card->dma_last_transfer_vaddr[SND_PS3_CH_L] -
-                                card->dma_start_vaddr[SND_PS3_CH_L]);
-       }
-       spin_unlock(&card->dma_lock);
-
-       ret = bytes_to_frames(substream->runtime, bytes * 2);
-
-       return ret;
-};
-
-static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream)
-{
-       int ret;
-       ret = snd_pcm_lib_free_pages(substream);
-       return ret;
-};
-
-static int snd_ps3_pcm_close(struct snd_pcm_substream *substream)
-{
-       /* mute on */
-       snd_ps3_mute(1);
-       return 0;
-};
-
-static void snd_ps3_audio_fixup(struct snd_ps3_card_info *card)
-{
-       /*
-        * avsetting driver seems to never change the followings
-        * so, init them here once
-        */
-
-       /* no dma interrupt needed */
-       write_reg(PS3_AUDIO_INTR_EN_0, 0);
-
-       /* use every 4 buffer empty interrupt */
-       update_mask_reg(PS3_AUDIO_AX_IC,
-                       PS3_AUDIO_AX_IC_AASOIMD_MASK,
-                       PS3_AUDIO_AX_IC_AASOIMD_EVERY4);
-
-       /* enable 3wire clocks */
-       update_mask_reg(PS3_AUDIO_AO_3WMCTRL,
-                       ~(PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED |
-                         PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED),
-                       0);
-       update_reg(PS3_AUDIO_AO_3WMCTRL,
-                  PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT);
-}
-
-/*
- * av setting
- * NOTE: calling this function may generate audio interrupt.
- */
-static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card)
-{
-       int ret, retries, i;
-       pr_debug("%s: start\n", __func__);
-
-       ret = ps3av_set_audio_mode(card->avs.avs_audio_ch,
-                                 card->avs.avs_audio_rate,
-                                 card->avs.avs_audio_width,
-                                 card->avs.avs_audio_format,
-                                 card->avs.avs_audio_source);
-       /*
-        * Reset the following unwanted settings:
-        */
-
-       /* disable all 3wire buffers */
-       update_mask_reg(PS3_AUDIO_AO_3WMCTRL,
-                       ~(PS3_AUDIO_AO_3WMCTRL_ASOEN(0) |
-                         PS3_AUDIO_AO_3WMCTRL_ASOEN(1) |
-                         PS3_AUDIO_AO_3WMCTRL_ASOEN(2) |
-                         PS3_AUDIO_AO_3WMCTRL_ASOEN(3)),
-                       0);
-       wmb();  /* ensure the hardware sees the change */
-       /* wait for actually stopped */
-       retries = 1000;
-       while ((read_reg(PS3_AUDIO_AO_3WMCTRL) &
-               (PS3_AUDIO_AO_3WMCTRL_ASORUN(0) |
-                PS3_AUDIO_AO_3WMCTRL_ASORUN(1) |
-                PS3_AUDIO_AO_3WMCTRL_ASORUN(2) |
-                PS3_AUDIO_AO_3WMCTRL_ASORUN(3))) &&
-              --retries) {
-               udelay(1);
-       }
-
-       /* reset buffer pointer */
-       for (i = 0; i < 4; i++) {
-               update_reg(PS3_AUDIO_AO_3WCTRL(i),
-                          PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET);
-               udelay(10);
-       }
-       wmb(); /* ensure the hardware actually start resetting */
-
-       /* enable 3wire#0 buffer */
-       update_reg(PS3_AUDIO_AO_3WMCTRL, PS3_AUDIO_AO_3WMCTRL_ASOEN(0));
-
-
-       /* In 24bit mode,ALSA inserts a zero byte at first byte of per sample */
-       update_mask_reg(PS3_AUDIO_AO_3WCTRL(0),
-                       ~PS3_AUDIO_AO_3WCTRL_ASODF,
-                       PS3_AUDIO_AO_3WCTRL_ASODF_LSB);
-       update_mask_reg(PS3_AUDIO_AO_SPDCTRL(0),
-                       ~PS3_AUDIO_AO_SPDCTRL_SPODF,
-                       PS3_AUDIO_AO_SPDCTRL_SPODF_LSB);
-       /* ensure all the setting above is written back to register */
-       wmb();
-       /* avsetting driver altered AX_IE, caller must reset it if you want */
-       pr_debug("%s: end\n", __func__);
-       return ret;
-}
+                       runtime->dma_area + (runtime->dma_bytes / 2);
+               card->dma_start_bus_addr[SND_PS3_CH_R] =
+                       runtime->dma_addr + (runtime->dma_bytes / 2);
 
-static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card)
-{
-       int ret;
-       pr_debug("%s: start\n", __func__);
-       card->avs.avs_audio_ch = PS3AV_CMD_AUDIO_NUM_OF_CH_2;
-       card->avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K;
-       card->avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16;
-       card->avs.avs_audio_format = PS3AV_CMD_AUDIO_FORMAT_PCM;
-       card->avs.avs_audio_source = PS3AV_CMD_AUDIO_SOURCE_SERIAL;
-       memcpy(card->avs.avs_cs_info, ps3av_mode_cs_info, 8);
+               pr_debug("%s: vaddr=%p bus=%#llx\n", __func__,
+                        card->dma_start_vaddr[SND_PS3_CH_L],
+                        card->dma_start_bus_addr[SND_PS3_CH_L]);
 
-       ret = snd_ps3_change_avsetting(card);
+       }
+       spin_unlock_irqrestore(&card->dma_lock, irqsave);
 
-       snd_ps3_audio_fixup(card);
+       /* ensure the hardware sees the change */
+       mb();
 
-       /* to start to generate SPDIF signal, fill data */
-       snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
-       snd_ps3_kick_dma(card);
-       pr_debug("%s: end\n", __func__);
-       return ret;
-}
+       return 0;
+};
 
-/*
- *  set sampling rate according to the substream
- */
-static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream)
+static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream,
+                              int cmd)
 {
        struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
-       struct snd_ps3_avsetting_info avs;
-       int ret;
-
-       avs = card->avs;
+       int ret = 0;
 
-       pr_debug("%s: called freq=%d width=%d\n", __func__,
-                substream->runtime->rate,
-                snd_pcm_format_width(substream->runtime->format));
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               /* clear outstanding interrupts  */
+               update_reg(PS3_AUDIO_AX_IS, 0);
 
-       pr_debug("%s: before freq=%d width=%d\n", __func__,
-                card->avs.avs_audio_rate, card->avs.avs_audio_width);
+               spin_lock(&card->dma_lock);
+               {
+                       card->running = 1;
+               }
+               spin_unlock(&card->dma_lock);
 
-       /* sample rate */
-       switch (substream->runtime->rate) {
-       case 44100:
-               avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_44K;
-               break;
-       case 48000:
-               avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K;
-               break;
-       case 88200:
-               avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_88K;
-               break;
-       case 96000:
-               avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_96K;
+               snd_ps3_program_dma(card,
+                                   SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
+               snd_ps3_kick_dma(card);
+               while (read_reg(PS3_AUDIO_KICK(7)) &
+                      PS3_AUDIO_KICK_STATUS_MASK) {
+                       udelay(1);
+               }
+               snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_RUNNING);
+               snd_ps3_kick_dma(card);
                break;
-       default:
-               pr_info("%s: invalid rate %d\n", __func__,
-                       substream->runtime->rate);
-               return 1;
-       }
 
-       /* width */
-       switch (snd_pcm_format_width(substream->runtime->format)) {
-       case 16:
-               avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16;
-               break;
-       case 24:
-               avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_24;
+       case SNDRV_PCM_TRIGGER_STOP:
+               spin_lock(&card->dma_lock);
+               {
+                       card->running = 0;
+               }
+               spin_unlock(&card->dma_lock);
+               snd_ps3_wait_for_dma_stop(card);
                break;
        default:
-               pr_info("%s: invalid width %d\n", __func__,
-                       snd_pcm_format_width(substream->runtime->format));
-               return 1;
+               break;
+
        }
 
-       memcpy(avs.avs_cs_info, ps3av_mode_cs_info, 8);
+       return ret;
+};
 
-       if (memcmp(&card->avs, &avs, sizeof(avs))) {
-               pr_debug("%s: after freq=%d width=%d\n", __func__,
-                        card->avs.avs_audio_rate, card->avs.avs_audio_width);
+/*
+ * report current pointer
+ */
+static snd_pcm_uframes_t snd_ps3_pcm_pointer(
+       struct snd_pcm_substream *substream)
+{
+       struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
+       size_t bytes;
+       snd_pcm_uframes_t ret;
 
-               card->avs = avs;
-               snd_ps3_change_avsetting(card);
-               ret = 0;
-       } else
-               ret = 1;
+       spin_lock(&card->dma_lock);
+       {
+               bytes = (size_t)(card->dma_last_transfer_vaddr[SND_PS3_CH_L] -
+                                card->dma_start_vaddr[SND_PS3_CH_L]);
+       }
+       spin_unlock(&card->dma_lock);
 
-       /* check CS non-audio bit and mute accordingly */
-       if (avs.avs_cs_info[0] & 0x02)
-               ps3av_audio_mute_analog(1); /* mute if non-audio */
-       else
-               ps3av_audio_mute_analog(0);
+       ret = bytes_to_frames(substream->runtime, bytes * 2);
 
        return ret;
-}
+};
 
 /*
  * SPDIF status bits controls
@@ -798,28 +753,39 @@ static struct snd_kcontrol_new spdif_ctls[] = {
        {
                .access = SNDRV_CTL_ELEM_ACCESS_READ,
                .iface = SNDRV_CTL_ELEM_IFACE_PCM,
-               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+               .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
                .info = snd_ps3_spdif_mask_info,
                .get = snd_ps3_spdif_cmask_get,
        },
        {
                .access = SNDRV_CTL_ELEM_ACCESS_READ,
                .iface = SNDRV_CTL_ELEM_IFACE_PCM,
-               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+               .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK),
                .info = snd_ps3_spdif_mask_info,
                .get = snd_ps3_spdif_pmask_get,
        },
        {
                .iface = SNDRV_CTL_ELEM_IFACE_PCM,
-               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+               .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
                .info = snd_ps3_spdif_mask_info,
                .get = snd_ps3_spdif_default_get,
                .put = snd_ps3_spdif_default_put,
        },
 };
 
+static struct snd_pcm_ops snd_ps3_pcm_spdif_ops = {
+       .open = snd_ps3_pcm_open,
+       .close = snd_ps3_pcm_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = snd_ps3_pcm_hw_params,
+       .hw_free = snd_ps3_pcm_hw_free,
+       .prepare = snd_ps3_pcm_prepare,
+       .trigger = snd_ps3_pcm_trigger,
+       .pointer = snd_ps3_pcm_pointer,
+};
+
 
-static int snd_ps3_map_mmio(void)
+static int __devinit snd_ps3_map_mmio(void)
 {
        the_card.mapped_mmio_vaddr =
                ioremap(the_card.ps3_dev->m_region->bus_addr,
@@ -841,7 +807,7 @@ static void snd_ps3_unmap_mmio(void)
        the_card.mapped_mmio_vaddr = NULL;
 }
 
-static int snd_ps3_allocate_irq(void)
+static int __devinit snd_ps3_allocate_irq(void)
 {
        int ret;
        u64 lpar_addr, lpar_size;
@@ -899,7 +865,7 @@ static void snd_ps3_free_irq(void)
        ps3_irq_plug_destroy(the_card.irq_no);
 }
 
-static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start)
+static void __devinit snd_ps3_audio_set_base_addr(uint64_t ioaddr_start)
 {
        uint64_t val;
        int ret;
@@ -915,7 +881,53 @@ static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start)
                        ret);
 }
 
-static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
+static void __devinit snd_ps3_audio_fixup(struct snd_ps3_card_info *card)
+{
+       /*
+        * avsetting driver seems to never change the followings
+        * so, init them here once
+        */
+
+       /* no dma interrupt needed */
+       write_reg(PS3_AUDIO_INTR_EN_0, 0);
+
+       /* use every 4 buffer empty interrupt */
+       update_mask_reg(PS3_AUDIO_AX_IC,
+                       PS3_AUDIO_AX_IC_AASOIMD_MASK,
+                       PS3_AUDIO_AX_IC_AASOIMD_EVERY4);
+
+       /* enable 3wire clocks */
+       update_mask_reg(PS3_AUDIO_AO_3WMCTRL,
+                       ~(PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED |
+                         PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED),
+                       0);
+       update_reg(PS3_AUDIO_AO_3WMCTRL,
+                  PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT);
+}
+
+static int __devinit snd_ps3_init_avsetting(struct snd_ps3_card_info *card)
+{
+       int ret;
+       pr_debug("%s: start\n", __func__);
+       card->avs.avs_audio_ch = PS3AV_CMD_AUDIO_NUM_OF_CH_2;
+       card->avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K;
+       card->avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16;
+       card->avs.avs_audio_format = PS3AV_CMD_AUDIO_FORMAT_PCM;
+       card->avs.avs_audio_source = PS3AV_CMD_AUDIO_SOURCE_SERIAL;
+       memcpy(card->avs.avs_cs_info, ps3av_mode_cs_info, 8);
+
+       ret = snd_ps3_change_avsetting(card);
+
+       snd_ps3_audio_fixup(card);
+
+       /* to start to generate SPDIF signal, fill data */
+       snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
+       snd_ps3_kick_dma(card);
+       pr_debug("%s: end\n", __func__);
+       return ret;
+}
+
+static int __devinit snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
 {
        int i, ret;
        u64 lpar_addr, lpar_size;
@@ -1020,11 +1032,12 @@ static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
         * its size should be lager than PS3_AUDIO_FIFO_STAGE_SIZE * 2
         * PAGE_SIZE is enogh
         */
-       if (!(the_card.null_buffer_start_vaddr =
-             dma_alloc_coherent(&the_card.ps3_dev->core,
-                                PAGE_SIZE,
-                                &the_card.null_buffer_start_dma_addr,
-                                GFP_KERNEL))) {
+       the_card.null_buffer_start_vaddr =
+               dma_alloc_coherent(&the_card.ps3_dev->core,
+                                  PAGE_SIZE,
+                                  &the_card.null_buffer_start_dma_addr,
+                                  GFP_KERNEL);
+       if (!the_card.null_buffer_start_vaddr) {
                pr_info("%s: nullbuffer alloc failed\n", __func__);
                goto clean_preallocate;
        }
@@ -1114,71 +1127,6 @@ static struct ps3_system_bus_driver snd_ps3_bus_driver_info = {
 };
 
 
-/*
- * Interrupt handler
- */
-static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id)
-{
-
-       uint32_t port_intr;
-       int underflow_occured = 0;
-       struct snd_ps3_card_info *card = dev_id;
-
-       if (!card->running) {
-               update_reg(PS3_AUDIO_AX_IS, 0);
-               update_reg(PS3_AUDIO_INTR_0, 0);
-               return IRQ_HANDLED;
-       }
-
-       port_intr = read_reg(PS3_AUDIO_AX_IS);
-       /*
-        *serial buffer empty detected (every 4 times),
-        *program next dma and kick it
-        */
-       if (port_intr & PS3_AUDIO_AX_IE_ASOBEIE(0)) {
-               write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBEIE(0));
-               if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) {
-                       write_reg(PS3_AUDIO_AX_IS, port_intr);
-                       underflow_occured = 1;
-               }
-               if (card->silent) {
-                       /* we are still in silent time */
-                       snd_ps3_program_dma(card,
-                               (underflow_occured) ?
-                               SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL :
-                               SND_PS3_DMA_FILLTYPE_SILENT_RUNNING);
-                       snd_ps3_kick_dma(card);
-                       card->silent --;
-               } else {
-                       snd_ps3_program_dma(card,
-                               (underflow_occured) ?
-                               SND_PS3_DMA_FILLTYPE_FIRSTFILL :
-                               SND_PS3_DMA_FILLTYPE_RUNNING);
-                       snd_ps3_kick_dma(card);
-                       snd_pcm_period_elapsed(card->substream);
-               }
-       } else if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) {
-               write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBUIE(0));
-               /*
-                * serial out underflow, but buffer empty not detected.
-                * in this case, fill fifo with 0 to recover.  After
-                * filling dummy data, serial automatically start to
-                * consume them and then will generate normal buffer
-                * empty interrupts.
-                * If both buffer underflow and buffer empty are occured,
-                * it is better to do nomal data transfer than empty one
-                */
-               snd_ps3_program_dma(card,
-                                   SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
-               snd_ps3_kick_dma(card);
-               snd_ps3_program_dma(card,
-                                   SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
-               snd_ps3_kick_dma(card);
-       }
-       /* clear interrupt cause */
-       return IRQ_HANDLED;
-};
-
 /*
  * module/subsystem initialize/terminate
  */
@@ -1197,10 +1145,15 @@ static int __init snd_ps3_init(void)
 
        return ret;
 }
+module_init(snd_ps3_init);
 
 static void __exit snd_ps3_exit(void)
 {
        ps3_system_bus_driver_unregister(&snd_ps3_bus_driver_info);
 }
+module_exit(snd_ps3_exit);
 
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PS3 sound driver");
+MODULE_AUTHOR("Sony Computer Entertainment Inc.");
 MODULE_ALIAS(PS3_MODULE_ALIAS_SOUND);
index 40222fcc08783048bd56c06659d027cdb903ce54..08e584d1453af17d22d43547cebf7d0bdf50d75f 100644 (file)
@@ -838,7 +838,7 @@ static int snapper_put_capture_source(struct snd_kcontrol *kcontrol,
 
 /*
  */
-static struct snd_kcontrol_new tumbler_mixers[] __initdata = {
+static struct snd_kcontrol_new tumbler_mixers[] __devinitdata = {
        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
          .name = "Master Playback Volume",
          .info = tumbler_info_master_volume,
@@ -862,7 +862,7 @@ static struct snd_kcontrol_new tumbler_mixers[] __initdata = {
        },
 };
 
-static struct snd_kcontrol_new snapper_mixers[] __initdata = {
+static struct snd_kcontrol_new snapper_mixers[] __devinitdata = {
        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
          .name = "Master Playback Volume",
          .info = tumbler_info_master_volume,
@@ -895,7 +895,7 @@ static struct snd_kcontrol_new snapper_mixers[] __initdata = {
        },
 };
 
-static struct snd_kcontrol_new tumbler_hp_sw __initdata = {
+static struct snd_kcontrol_new tumbler_hp_sw __devinitdata = {
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
        .name = "Headphone Playback Switch",
        .info = snd_pmac_boolean_mono_info,
@@ -903,7 +903,7 @@ static struct snd_kcontrol_new tumbler_hp_sw __initdata = {
        .put = tumbler_put_mute_switch,
        .private_value = TUMBLER_MUTE_HP,
 };
-static struct snd_kcontrol_new tumbler_speaker_sw __initdata = {
+static struct snd_kcontrol_new tumbler_speaker_sw __devinitdata = {
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
        .name = "PC Speaker Playback Switch",
        .info = snd_pmac_boolean_mono_info,
@@ -911,7 +911,7 @@ static struct snd_kcontrol_new tumbler_speaker_sw __initdata = {
        .put = tumbler_put_mute_switch,
        .private_value = TUMBLER_MUTE_AMP,
 };
-static struct snd_kcontrol_new tumbler_lineout_sw __initdata = {
+static struct snd_kcontrol_new tumbler_lineout_sw __devinitdata = {
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
        .name = "Line Out Playback Switch",
        .info = snd_pmac_boolean_mono_info,
@@ -919,7 +919,7 @@ static struct snd_kcontrol_new tumbler_lineout_sw __initdata = {
        .put = tumbler_put_mute_switch,
        .private_value = TUMBLER_MUTE_LINE,
 };
-static struct snd_kcontrol_new tumbler_drc_sw __initdata = {
+static struct snd_kcontrol_new tumbler_drc_sw __devinitdata = {
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
        .name = "DRC Switch",
        .info = snd_pmac_boolean_mono_info,
@@ -1269,7 +1269,7 @@ static void tumbler_resume(struct snd_pmac *chip)
 #endif
 
 /* initialize tumbler */
-static int __init tumbler_init(struct snd_pmac *chip)
+static int __devinit tumbler_init(struct snd_pmac *chip)
 {
        int irq;
        struct pmac_tumbler *mix = chip->mixer_data;
@@ -1339,7 +1339,7 @@ static void tumbler_cleanup(struct snd_pmac *chip)
 }
 
 /* exported */
-int __init snd_pmac_tumbler_init(struct snd_pmac *chip)
+int __devinit snd_pmac_tumbler_init(struct snd_pmac *chip)
 {
        int i, err;
        struct pmac_tumbler *mix;
index 3d2bb6fc6dcc46ad7f95ef5703059d6144be7849..d3e786a9a0a7ec08a69564700910ef49ac4f4927 100644 (file)
@@ -32,7 +32,9 @@ source "sound/soc/fsl/Kconfig"
 source "sound/soc/omap/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/s3c24xx/Kconfig"
+source "sound/soc/s6000/Kconfig"
 source "sound/soc/sh/Kconfig"
+source "sound/soc/txx9/Kconfig"
 
 # Supported codecs
 source "sound/soc/codecs/Kconfig"
index 0237879fd4125b47330a7a65a24449643fdaf3a7..6f1e28de23cf325383824299f9eb33fdfe43bec3 100644 (file)
@@ -10,4 +10,6 @@ obj-$(CONFIG_SND_SOC) += fsl/
 obj-$(CONFIG_SND_SOC)  += omap/
 obj-$(CONFIG_SND_SOC)  += pxa/
 obj-$(CONFIG_SND_SOC)  += s3c24xx/
+obj-$(CONFIG_SND_SOC)  += s6000/
 obj-$(CONFIG_SND_SOC)  += sh/
+obj-$(CONFIG_SND_SOC)  += txx9/
index a608d7009dbd61117444570a45f7e1627ae30ff4..e720d5e6f04cfdd135ea42e5b74ed74e75724bed 100644 (file)
@@ -41,3 +41,11 @@ config SND_AT32_SOC_PLAYPAQ_SLAVE
           and FRAME signals on the PlayPaq.  Unless you want to play
           with the AT32 as the SSC master, you probably want to say N here,
           as this will give you better sound quality.
+
+config SND_AT91_SOC_AFEB9260
+       tristate "SoC Audio support for AFEB9260 board"
+       depends on ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
+       select SND_ATMEL_SOC_SSC
+       select SND_SOC_TLV320AIC23
+       help
+         Say Y here to support sound on AFEB9260 board.
index f54a7cc68e663bffe6c504a6bfb46fea13a5ed3b..e7ea56bd5f82a94de94d4169c464e809d34dfb5e 100644 (file)
@@ -13,3 +13,4 @@ snd-soc-playpaq-objs := playpaq_wm8510.o
 
 obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
 obj-$(CONFIG_SND_AT32_SOC_PLAYPAQ) += snd-soc-playpaq.o
+obj-$(CONFIG_SND_AT91_SOC_AFEB9260) += snd-soc-afeb9260.o
index 70657534e6b1f68ad3d9a89d4bc7e8698a4fafe5..9eb610c2ba917d42f8e81afc0972879302986928 100644 (file)
@@ -117,7 +117,7 @@ static struct ssc_clock_data playpaq_wm8510_calc_ssc_clock(
         * Find actual rate, compare to requested rate
         */
        actual_rate = (cd.ssc_rate / (cd.cmr_div * 2)) / (2 * (cd.period + 1));
-       pr_debug("playpaq_wm8510: Request rate = %d, actual rate = %d\n",
+       pr_debug("playpaq_wm8510: Request rate = %u, actual rate = %u\n",
                 rate, actual_rate);
 
 
diff --git a/sound/soc/atmel/snd-soc-afeb9260.c b/sound/soc/atmel/snd-soc-afeb9260.c
new file mode 100644 (file)
index 0000000..23349de
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * afeb9260.c  --  SoC audio for AFEB9260
+ *
+ * Copyright (C) 2009 Sergey Lapin <slapin@ossfans.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.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include <linux/atmel-ssc.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <linux/gpio.h>
+
+#include "../codecs/tlv320aic23.h"
+#include "atmel-pcm.h"
+#include "atmel_ssc_dai.h"
+
+#define CODEC_CLOCK    12000000
+
+static int afeb9260_hw_params(struct snd_pcm_substream *substream,
+                        struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       int err;
+
+       /* Set codec DAI configuration */
+       err = snd_soc_dai_set_fmt(codec_dai,
+                                 SND_SOC_DAIFMT_I2S|
+                                 SND_SOC_DAIFMT_NB_IF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (err < 0) {
+               printk(KERN_ERR "can't set codec DAI configuration\n");
+               return err;
+       }
+
+       /* Set cpu DAI configuration */
+       err = snd_soc_dai_set_fmt(cpu_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_IF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (err < 0) {
+               printk(KERN_ERR "can't set cpu DAI configuration\n");
+               return err;
+       }
+
+       /* Set the codec system clock for DAC and ADC */
+       err =
+           snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN);
+
+       if (err < 0) {
+               printk(KERN_ERR "can't set codec system clock\n");
+               return err;
+       }
+
+       return err;
+}
+
+static struct snd_soc_ops afeb9260_ops = {
+       .hw_params = afeb9260_hw_params,
+};
+
+static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+       SND_SOC_DAPM_LINE("Line In", NULL),
+       SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+       {"Headphone Jack", NULL, "LHPOUT"},
+       {"Headphone Jack", NULL, "RHPOUT"},
+
+       {"LLINEIN", NULL, "Line In"},
+       {"RLINEIN", NULL, "Line In"},
+
+       {"MICIN", NULL, "Mic Jack"},
+};
+
+static int afeb9260_tlv320aic23_init(struct snd_soc_codec *codec)
+{
+
+       /* Add afeb9260 specific widgets */
+       snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets,
+                                 ARRAY_SIZE(tlv320aic23_dapm_widgets));
+
+       /* Set up afeb9260 specific audio path audio_map */
+       snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+       snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+       snd_soc_dapm_enable_pin(codec, "Line In");
+       snd_soc_dapm_enable_pin(codec, "Mic Jack");
+
+       snd_soc_dapm_sync(codec);
+
+       return 0;
+}
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link afeb9260_dai = {
+       .name = "TLV320AIC23",
+       .stream_name = "AIC23",
+       .cpu_dai = &atmel_ssc_dai[0],
+       .codec_dai = &tlv320aic23_dai,
+       .init = afeb9260_tlv320aic23_init,
+       .ops = &afeb9260_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_machine_afeb9260 = {
+       .name = "AFEB9260",
+       .platform = &atmel_soc_platform,
+       .dai_link = &afeb9260_dai,
+       .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device afeb9260_snd_devdata = {
+       .card = &snd_soc_machine_afeb9260,
+       .codec_dev = &soc_codec_dev_tlv320aic23,
+};
+
+static struct platform_device *afeb9260_snd_device;
+
+static int __init afeb9260_soc_init(void)
+{
+       int err;
+       struct device *dev;
+       struct atmel_ssc_info *ssc_p = afeb9260_dai.cpu_dai->private_data;
+       struct ssc_device *ssc = NULL;
+
+       if (!(machine_is_afeb9260()))
+               return -ENODEV;
+
+       ssc = ssc_request(0);
+       if (IS_ERR(ssc)) {
+               printk(KERN_ERR "ASoC: Failed to request SSC 0\n");
+               err = PTR_ERR(ssc);
+               ssc = NULL;
+               goto err_ssc;
+       }
+       ssc_p->ssc = ssc;
+
+       afeb9260_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!afeb9260_snd_device) {
+               printk(KERN_ERR "ASoC: Platform device allocation failed\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(afeb9260_snd_device, &afeb9260_snd_devdata);
+       afeb9260_snd_devdata.dev = &afeb9260_snd_device->dev;
+       err = platform_device_add(afeb9260_snd_device);
+       if (err)
+               goto err1;
+
+       dev = &afeb9260_snd_device->dev;
+
+       return 0;
+err1:
+       platform_device_del(afeb9260_snd_device);
+       platform_device_put(afeb9260_snd_device);
+err_ssc:
+       return err;
+
+}
+
+static void __exit afeb9260_soc_exit(void)
+{
+       platform_device_unregister(afeb9260_snd_device);
+}
+
+module_init(afeb9260_soc_init);
+module_exit(afeb9260_soc_exit);
+
+MODULE_AUTHOR("Sergey Lapin <slapin@ossfans.org>");
+MODULE_DESCRIPTION("ALSA SoC for AFEB9260");
+MODULE_LICENSE("GPL");
+
index 8a935f2d17674683186fc26d48d890c835ad3f3c..b1ed423fabd51f56bb278e165e2d8c0369406caa 100644 (file)
 #include "bf5xx-sport.h"
 #include "bf5xx-ac97.h"
 
+/* Anomaly notes:
+ *  05000250 - AD1980 is running in TDM mode and RFS/TFS are generated by SPORT
+ *             contrtoller. But, RFSDIV and TFSDIV are always set to 16*16-1,
+ *             while the max AC97 data size is 13*16. The DIV is always larger
+ *             than data size. AD73311 and ad2602 are not running in TDM mode.
+ *             AD1836 and AD73322 depend on external RFS/TFS only. So, this
+ *             anomaly does not affect blackfin sound drivers.
+*/
+
 static int *cmd_count;
 static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
 
index b7953c8cf8382a05df6ef9b280b0403a7b214f77..469ce7fab20ca6aad3d6474d8c63deeacb3f5101 100644 (file)
@@ -190,7 +190,7 @@ static inline int sport_hook_rx_dummy(struct sport_device *sport)
        desc = get_dma_next_desc_ptr(sport->dma_rx_chan);
        /* Copy the descriptor which will be damaged to backup */
        temp_desc = *desc;
-       desc->x_count = 0xa;
+       desc->x_count = sport->dummy_count / 2;
        desc->y_count = 0;
        desc->next_desc_addr = sport->dummy_rx_desc;
        local_irq_restore(flags);
@@ -309,7 +309,7 @@ static inline int sport_hook_tx_dummy(struct sport_device *sport)
        desc = get_dma_next_desc_ptr(sport->dma_tx_chan);
        /* Store the descriptor which will be damaged */
        temp_desc = *desc;
-       desc->x_count = 0xa;
+       desc->x_count = sport->dummy_count / 2;
        desc->y_count = 0;
        desc->next_desc_addr = sport->dummy_tx_desc;
        local_irq_restore(flags);
index b6c7f7a01cb03756a321651b02f5135eccdd2a4a..bbc97fd7664893b58871e9d026cc20e9d9863774 100644 (file)
@@ -18,7 +18,9 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_AK4535 if I2C
        select SND_SOC_CS4270 if I2C
        select SND_SOC_PCM3008
+       select SND_SOC_SPDIF
        select SND_SOC_SSM2602 if I2C
+       select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
        select SND_SOC_TLV320AIC23 if I2C
        select SND_SOC_TLV320AIC26 if SPI_MASTER
        select SND_SOC_TLV320AIC3X if I2C
@@ -35,8 +37,12 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8900 if I2C
        select SND_SOC_WM8903 if I2C
+       select SND_SOC_WM8940 if I2C
+       select SND_SOC_WM8960 if I2C
        select SND_SOC_WM8971 if I2C
+       select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8990 if I2C
+       select SND_SOC_WM9081 if I2C
        select SND_SOC_WM9705 if SND_SOC_AC97_BUS
        select SND_SOC_WM9712 if SND_SOC_AC97_BUS
        select SND_SOC_WM9713 if SND_SOC_AC97_BUS
@@ -86,9 +92,15 @@ config SND_SOC_L3
 config SND_SOC_PCM3008
        tristate
 
+config SND_SOC_SPDIF
+       tristate
+
 config SND_SOC_SSM2602
        tristate
 
+config SND_SOC_STAC9766
+       tristate
+
 config SND_SOC_TLV320AIC23
        tristate
 
@@ -138,12 +150,24 @@ config SND_SOC_WM8900
 config SND_SOC_WM8903
        tristate
 
+config SND_SOC_WM8940
+        tristate
+
+config SND_SOC_WM8960
+       tristate
+
 config SND_SOC_WM8971
        tristate
 
+config SND_SOC_WM8988
+       tristate
+
 config SND_SOC_WM8990
        tristate
 
+config SND_SOC_WM9081
+       tristate
+
 config SND_SOC_WM9705
        tristate
 
index f2653803ede8faf37be341e40c3ae0f5d5169956..8b7530546f4dece45018f54a4894eaa3875d954f 100644 (file)
@@ -6,7 +6,9 @@ snd-soc-ak4535-objs := ak4535.o
 snd-soc-cs4270-objs := cs4270.o
 snd-soc-l3-objs := l3.o
 snd-soc-pcm3008-objs := pcm3008.o
+snd-soc-spdif-objs := spdif_transciever.o
 snd-soc-ssm2602-objs := ssm2602.o
+snd-soc-stac9766-objs := stac9766.o
 snd-soc-tlv320aic23-objs := tlv320aic23.o
 snd-soc-tlv320aic26-objs := tlv320aic26.o
 snd-soc-tlv320aic3x-objs := tlv320aic3x.o
@@ -23,8 +25,12 @@ snd-soc-wm8750-objs := wm8750.o
 snd-soc-wm8753-objs := wm8753.o
 snd-soc-wm8900-objs := wm8900.o
 snd-soc-wm8903-objs := wm8903.o
+snd-soc-wm8940-objs := wm8940.o
+snd-soc-wm8960-objs := wm8960.o
 snd-soc-wm8971-objs := wm8971.o
+snd-soc-wm8988-objs := wm8988.o
 snd-soc-wm8990-objs := wm8990.o
+snd-soc-wm9081-objs := wm9081.o
 snd-soc-wm9705-objs := wm9705.o
 snd-soc-wm9712-objs := wm9712.o
 snd-soc-wm9713-objs := wm9713.o
@@ -37,7 +43,9 @@ obj-$(CONFIG_SND_SOC_AK4535)  += snd-soc-ak4535.o
 obj-$(CONFIG_SND_SOC_CS4270)   += snd-soc-cs4270.o
 obj-$(CONFIG_SND_SOC_L3)       += snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_PCM3008)  += snd-soc-pcm3008.o
+obj-$(CONFIG_SND_SOC_SPDIF)    += snd-soc-spdif.o
 obj-$(CONFIG_SND_SOC_SSM2602)  += snd-soc-ssm2602.o
+obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23)      += snd-soc-tlv320aic23.o
 obj-$(CONFIG_SND_SOC_TLV320AIC26)      += snd-soc-tlv320aic26.o
 obj-$(CONFIG_SND_SOC_TLV320AIC3X)      += snd-soc-tlv320aic3x.o
@@ -55,7 +63,11 @@ obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
 obj-$(CONFIG_SND_SOC_WM8900)   += snd-soc-wm8900.o
 obj-$(CONFIG_SND_SOC_WM8903)   += snd-soc-wm8903.o
 obj-$(CONFIG_SND_SOC_WM8971)   += snd-soc-wm8971.o
+obj-$(CONFIG_SND_SOC_WM8940)   += snd-soc-wm8940.o
+obj-$(CONFIG_SND_SOC_WM8960)   += snd-soc-wm8960.o
+obj-$(CONFIG_SND_SOC_WM8988)   += snd-soc-wm8988.o
 obj-$(CONFIG_SND_SOC_WM8990)   += snd-soc-wm8990.o
+obj-$(CONFIG_SND_SOC_WM9081)   += snd-soc-wm9081.o
 obj-$(CONFIG_SND_SOC_WM9705)   += snd-soc-wm9705.o
 obj-$(CONFIG_SND_SOC_WM9712)   += snd-soc-wm9712.o
 obj-$(CONFIG_SND_SOC_WM9713)   += snd-soc-wm9713.o
index b0d4af145b873900509ce18239e2c85899aa9069..932299bb5d1e006a7f0108bd7d7ab0000b3969bc 100644 (file)
@@ -53,13 +53,13 @@ struct snd_soc_dai ac97_dai = {
                .channels_min = 1,
                .channels_max = 2,
                .rates = STD_AC97_RATES,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+               .formats = SND_SOC_STD_AC97_FMTS,},
        .capture = {
                .stream_name = "AC97 Capture",
                .channels_min = 1,
                .channels_max = 2,
                .rates = STD_AC97_RATES,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+               .formats = SND_SOC_STD_AC97_FMTS,},
        .ops = &ac97_dai_ops,
 };
 EXPORT_SYMBOL_GPL(ac97_dai);
index ddb3b08ac23c441dd93112295a7182881c856041..d7440a982d22794928940c3590cde1765fe56d9a 100644 (file)
@@ -137,13 +137,13 @@ struct snd_soc_dai ad1980_dai = {
                .channels_min = 2,
                .channels_max = 6,
                .rates = SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE, },
+               .formats = SND_SOC_STD_AC97_FMTS, },
        .capture = {
                .stream_name = "Capture",
                .channels_min = 2,
                .channels_max = 2,
                .rates = SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE, },
+               .formats = SND_SOC_STD_AC97_FMTS, },
 };
 EXPORT_SYMBOL_GPL(ad1980_dai);
 
index 7fa09a387622cbff69b28b88e83819faa876d4e9..a32b8226c8a43bc19a2f3852ef24b3e802238c05 100644 (file)
@@ -18,7 +18,7 @@
  * - The machine driver's 'startup' function must call
  *   cs4270_set_dai_sysclk() with the value of MCLK.
  * - Only I2S and left-justified modes are supported
- * - Power management is not supported
+ * - Power management is supported
  */
 
 #include <linux/module.h>
@@ -27,6 +27,7 @@
 #include <sound/soc.h>
 #include <sound/initval.h>
 #include <linux/i2c.h>
+#include <linux/delay.h>
 
 #include "cs4270.h"
 
@@ -56,6 +57,7 @@
 #define CS4270_FIRSTREG        0x01
 #define CS4270_LASTREG 0x08
 #define CS4270_NUMREGS (CS4270_LASTREG - CS4270_FIRSTREG + 1)
+#define CS4270_I2C_INCR        0x80
 
 /* Bit masks for the CS4270 registers */
 #define CS4270_CHIPID_ID       0xF0
@@ -64,6 +66,8 @@
 #define CS4270_PWRCTL_PDN_ADC  0x20
 #define CS4270_PWRCTL_PDN_DAC  0x02
 #define CS4270_PWRCTL_PDN      0x01
+#define CS4270_PWRCTL_PDN_ALL  \
+       (CS4270_PWRCTL_PDN_ADC | CS4270_PWRCTL_PDN_DAC | CS4270_PWRCTL_PDN)
 #define CS4270_MODE_SPEED_MASK 0x30
 #define CS4270_MODE_1X         0x00
 #define CS4270_MODE_2X         0x10
@@ -109,6 +113,7 @@ struct cs4270_private {
        unsigned int mclk; /* Input frequency of the MCLK pin */
        unsigned int mode; /* The mode (I2S or left-justified) */
        unsigned int slave_mode;
+       unsigned int manual_mute;
 };
 
 /**
@@ -295,7 +300,7 @@ static int cs4270_fill_cache(struct snd_soc_codec *codec)
        s32 length;
 
        length = i2c_smbus_read_i2c_block_data(i2c_client,
-               CS4270_FIRSTREG | 0x80, CS4270_NUMREGS, cache);
+               CS4270_FIRSTREG | CS4270_I2C_INCR, CS4270_NUMREGS, cache);
 
        if (length != CS4270_NUMREGS) {
                dev_err(codec->dev, "i2c read failure, addr=0x%x\n",
@@ -453,7 +458,7 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
 }
 
 /**
- * cs4270_mute - enable/disable the CS4270 external mute
+ * cs4270_dai_mute - enable/disable the CS4270 external mute
  * @dai: the SOC DAI
  * @mute: 0 = disable mute, 1 = enable mute
  *
@@ -462,21 +467,52 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
  * board does not have the MUTEA or MUTEB pins connected to such circuitry,
  * then this function will do nothing.
  */
-static int cs4270_mute(struct snd_soc_dai *dai, int mute)
+static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute)
 {
        struct snd_soc_codec *codec = dai->codec;
+       struct cs4270_private *cs4270 = codec->private_data;
        int reg6;
 
        reg6 = snd_soc_read(codec, CS4270_MUTE);
 
        if (mute)
                reg6 |= CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B;
-       else
+       else {
                reg6 &= ~(CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B);
+               reg6 |= cs4270->manual_mute;
+       }
 
        return snd_soc_write(codec, CS4270_MUTE, reg6);
 }
 
+/**
+ * cs4270_soc_put_mute - put callback for the 'Master Playback switch'
+ *                      alsa control.
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * This function basically passes the arguments on to the generic
+ * snd_soc_put_volsw() function and saves the mute information in
+ * our private data structure. This is because we want to prevent
+ * cs4270_dai_mute() neglecting the user's decision to manually
+ * mute the codec's output.
+ *
+ * Returns 0 for success.
+ */
+static int cs4270_soc_put_mute(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct cs4270_private *cs4270 = codec->private_data;
+       int left = !ucontrol->value.integer.value[0];
+       int right = !ucontrol->value.integer.value[1];
+
+       cs4270->manual_mute = (left ? CS4270_MUTE_DAC_A : 0) |
+                             (right ? CS4270_MUTE_DAC_B : 0);
+
+       return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
 /* A list of non-DAPM controls that the CS4270 supports */
 static const struct snd_kcontrol_new cs4270_snd_controls[] = {
        SOC_DOUBLE_R("Master Playback Volume",
@@ -486,7 +522,9 @@ static const struct snd_kcontrol_new cs4270_snd_controls[] = {
        SOC_SINGLE("Zero Cross Switch", CS4270_TRANS, 5, 1, 0),
        SOC_SINGLE("Popguard Switch", CS4270_MODE, 0, 1, 1),
        SOC_SINGLE("Auto-Mute Switch", CS4270_MUTE, 5, 1, 0),
-       SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 0)
+       SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 1),
+       SOC_DOUBLE_EXT("Master Playback Switch", CS4270_MUTE, 0, 1, 1, 1,
+               snd_soc_get_volsw, cs4270_soc_put_mute),
 };
 
 /*
@@ -506,7 +544,7 @@ static struct snd_soc_dai_ops cs4270_dai_ops = {
        .hw_params      = cs4270_hw_params,
        .set_sysclk     = cs4270_set_dai_sysclk,
        .set_fmt        = cs4270_set_dai_fmt,
-       .digital_mute   = cs4270_mute,
+       .digital_mute   = cs4270_dai_mute,
 };
 
 struct snd_soc_dai cs4270_dai = {
@@ -753,6 +791,57 @@ static struct i2c_device_id cs4270_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, cs4270_id);
 
+#ifdef CONFIG_PM
+
+/* This suspend/resume implementation can handle both - a simple standby
+ * where the codec remains powered, and a full suspend, where the voltage
+ * domain the codec is connected to is teared down and/or any other hardware
+ * reset condition is asserted.
+ *
+ * The codec's own power saving features are enabled in the suspend callback,
+ * and all registers are written back to the hardware when resuming.
+ */
+
+static int cs4270_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+       struct cs4270_private *cs4270 = i2c_get_clientdata(client);
+       struct snd_soc_codec *codec = &cs4270->codec;
+       int reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
+
+       return snd_soc_write(codec, CS4270_PWRCTL, reg);
+}
+
+static int cs4270_i2c_resume(struct i2c_client *client)
+{
+       struct cs4270_private *cs4270 = i2c_get_clientdata(client);
+       struct snd_soc_codec *codec = &cs4270->codec;
+       int reg;
+
+       /* In case the device was put to hard reset during sleep, we need to
+        * wait 500ns here before any I2C communication. */
+       ndelay(500);
+
+       /* first restore the entire register cache ... */
+       for (reg = CS4270_FIRSTREG; reg <= CS4270_LASTREG; reg++) {
+               u8 val = snd_soc_read(codec, reg);
+
+               if (i2c_smbus_write_byte_data(client, reg, val)) {
+                       dev_err(codec->dev, "i2c write failed\n");
+                       return -EIO;
+               }
+       }
+
+       /* ... then disable the power-down bits */
+       reg = snd_soc_read(codec, CS4270_PWRCTL);
+       reg &= ~CS4270_PWRCTL_PDN_ALL;
+
+       return snd_soc_write(codec, CS4270_PWRCTL, reg);
+}
+#else
+#define cs4270_i2c_suspend     NULL
+#define cs4270_i2c_resume      NULL
+#endif /* CONFIG_PM */
+
 /*
  * cs4270_i2c_driver - I2C device identification
  *
@@ -767,6 +856,8 @@ static struct i2c_driver cs4270_i2c_driver = {
        .id_table = cs4270_id,
        .probe = cs4270_i2c_probe,
        .remove = cs4270_i2c_remove,
+       .suspend = cs4270_i2c_suspend,
+       .resume = cs4270_i2c_resume,
 };
 
 /*
diff --git a/sound/soc/codecs/spdif_transciever.c b/sound/soc/codecs/spdif_transciever.c
new file mode 100644 (file)
index 0000000..218b33a
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * ALSA SoC SPDIF DIT driver
+ *
+ *  This driver is used by controllers which can operate in DIT (SPDI/F) where
+ *  no codec is needed.  This file provides stub codec that can be used
+ *  in these configurations. TI DaVinci Audio controller uses this driver.
+ *
+ * Author:      Steve Chen,  <schen@mvista.com>
+ * Copyright:   (C) 2009 MontaVista Software, Inc., <source@mvista.com>
+ * Copyright:   (C) 2009  Texas Instruments, India
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+
+#include "spdif_transciever.h"
+
+#define STUB_RATES     SNDRV_PCM_RATE_8000_96000
+#define STUB_FORMATS   SNDRV_PCM_FMTBIT_S16_LE
+
+struct snd_soc_dai dit_stub_dai = {
+       .name           = "DIT",
+       .playback       = {
+               .stream_name    = "Playback",
+               .channels_min   = 1,
+               .channels_max   = 384,
+               .rates          = STUB_RATES,
+               .formats        = STUB_FORMATS,
+       },
+};
+
+static int spdif_dit_probe(struct platform_device *pdev)
+{
+       dit_stub_dai.dev = &pdev->dev;
+       return snd_soc_register_dai(&dit_stub_dai);
+}
+
+static int spdif_dit_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_dai(&dit_stub_dai);
+       return 0;
+}
+
+static struct platform_driver spdif_dit_driver = {
+       .probe          = spdif_dit_probe,
+       .remove         = spdif_dit_remove,
+       .driver         = {
+               .name   = "spdif-dit",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init dit_modinit(void)
+{
+       return platform_driver_register(&spdif_dit_driver);
+}
+
+static void __exit dit_exit(void)
+{
+       platform_driver_unregister(&spdif_dit_driver);
+}
+
+module_init(dit_modinit);
+module_exit(dit_exit);
+
diff --git a/sound/soc/codecs/spdif_transciever.h b/sound/soc/codecs/spdif_transciever.h
new file mode 100644 (file)
index 0000000..296f2eb
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * ALSA SoC DIT/DIR driver header
+ *
+ * Author:      Steve Chen,  <schen@mvista.com>
+ * Copyright:   (C) 2008 MontaVista Software, Inc., <source@mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef CODEC_STUBS_H
+#define CODEC_STUBS_H
+
+extern struct snd_soc_dai dit_stub_dai;
+
+#endif /* CODEC_STUBS_H */
index 87f606c7682292af24e5f213cf7c919465116fd1..1fc4c8e0899c18ec1f7f12750cf80023140cd5b7 100644 (file)
@@ -336,15 +336,17 @@ static int ssm2602_startup(struct snd_pcm_substream *substream,
                        master_runtime->sample_bits,
                        master_runtime->rate);
 
-               snd_pcm_hw_constraint_minmax(substream->runtime,
-                                            SNDRV_PCM_HW_PARAM_RATE,
-                                            master_runtime->rate,
-                                            master_runtime->rate);
-
-               snd_pcm_hw_constraint_minmax(substream->runtime,
-                                            SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
-                                            master_runtime->sample_bits,
-                                            master_runtime->sample_bits);
+               if (master_runtime->rate != 0)
+                       snd_pcm_hw_constraint_minmax(substream->runtime,
+                                                    SNDRV_PCM_HW_PARAM_RATE,
+                                                    master_runtime->rate,
+                                                    master_runtime->rate);
+
+               if (master_runtime->sample_bits != 0)
+                       snd_pcm_hw_constraint_minmax(substream->runtime,
+                                                    SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+                                                    master_runtime->sample_bits,
+                                                    master_runtime->sample_bits);
 
                ssm2602->slave_substream = substream;
        } else
@@ -372,6 +374,11 @@ static void ssm2602_shutdown(struct snd_pcm_substream *substream,
        struct snd_soc_device *socdev = rtd->socdev;
        struct snd_soc_codec *codec = socdev->card->codec;
        struct ssm2602_priv *ssm2602 = codec->private_data;
+
+       if (ssm2602->master_substream == substream)
+               ssm2602->master_substream = ssm2602->slave_substream;
+
+       ssm2602->slave_substream = NULL;
        /* deactivate */
        if (!codec->active)
                ssm2602_write(codec, SSM2602_ACTIVE, 0);
@@ -497,11 +504,9 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
-#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
-               SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
-               SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
-               SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
-               SNDRV_PCM_RATE_96000)
+#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_32000 |\
+               SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+               SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
 
 #define SSM2602_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
                SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
new file mode 100644 (file)
index 0000000..8ad4b7b
--- /dev/null
@@ -0,0 +1,463 @@
+/*
+ * stac9766.c  --  ALSA SoC STAC9766 codec support
+ *
+ * Copyright 2009 Jon Smirl, Digispeaker
+ * Author: Jon Smirl <jonsmirl@gmail.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.
+ *
+ *  Features:-
+ *
+ *   o Support for AC97 Codec, S/PDIF
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/soc-of-simple.h>
+
+#include "stac9766.h"
+
+#define STAC9766_VERSION "0.10"
+
+/*
+ * STAC9766 register cache
+ */
+static const u16 stac9766_reg[] = {
+       0x6A90, 0x8000, 0x8000, 0x8000, /* 6 */
+       0x0000, 0x0000, 0x8008, 0x8008, /* e */
+       0x8808, 0x8808, 0x8808, 0x8808, /* 16 */
+       0x8808, 0x0000, 0x8000, 0x0000, /* 1e */
+       0x0000, 0x0000, 0x0000, 0x000f, /* 26 */
+       0x0a05, 0x0400, 0xbb80, 0x0000, /* 2e */
+       0x0000, 0xbb80, 0x0000, 0x0000, /* 36 */
+       0x0000, 0x2000, 0x0000, 0x0100, /* 3e */
+       0x0000, 0x0000, 0x0080, 0x0000, /* 46 */
+       0x0000, 0x0000, 0x0003, 0xffff, /* 4e */
+       0x0000, 0x0000, 0x0000, 0x0000, /* 56 */
+       0x4000, 0x0000, 0x0000, 0x0000, /* 5e */
+       0x1201, 0xFFFF, 0xFFFF, 0x0000, /* 66 */
+       0x0000, 0x0000, 0x0000, 0x0000, /* 6e */
+       0x0000, 0x0000, 0x0000, 0x0006, /* 76 */
+       0x0000, 0x0000, 0x0000, 0x0000, /* 7e */
+};
+
+static const char *stac9766_record_mux[] = {"Mic", "CD", "Video", "AUX",
+                       "Line", "Stereo Mix", "Mono Mix", "Phone"};
+static const char *stac9766_mono_mux[] = {"Mix", "Mic"};
+static const char *stac9766_mic_mux[] = {"Mic1", "Mic2"};
+static const char *stac9766_SPDIF_mux[] = {"PCM", "ADC Record"};
+static const char *stac9766_popbypass_mux[] = {"Normal", "Bypass Mixer"};
+static const char *stac9766_record_all_mux[] = {"All analog",
+       "Analog plus DAC"};
+static const char *stac9766_boost1[] = {"0dB", "10dB"};
+static const char *stac9766_boost2[] = {"0dB", "20dB"};
+static const char *stac9766_stereo_mic[] = {"Off", "On"};
+
+static const struct soc_enum stac9766_record_enum =
+       SOC_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 8, stac9766_record_mux);
+static const struct soc_enum stac9766_mono_enum =
+       SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 9, 2, stac9766_mono_mux);
+static const struct soc_enum stac9766_mic_enum =
+       SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 8, 2, stac9766_mic_mux);
+static const struct soc_enum stac9766_SPDIF_enum =
+       SOC_ENUM_SINGLE(AC97_STAC_DA_CONTROL, 1, 2, stac9766_SPDIF_mux);
+static const struct soc_enum stac9766_popbypass_enum =
+       SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, stac9766_popbypass_mux);
+static const struct soc_enum stac9766_record_all_enum =
+       SOC_ENUM_SINGLE(AC97_STAC_ANALOG_SPECIAL, 12, 2,
+                       stac9766_record_all_mux);
+static const struct soc_enum stac9766_boost1_enum =
+       SOC_ENUM_SINGLE(AC97_MIC, 6, 2, stac9766_boost1); /* 0/10dB */
+static const struct soc_enum stac9766_boost2_enum =
+       SOC_ENUM_SINGLE(AC97_STAC_ANALOG_SPECIAL, 2, 2, stac9766_boost2); /* 0/20dB */
+static const struct soc_enum stac9766_stereo_mic_enum =
+       SOC_ENUM_SINGLE(AC97_STAC_STEREO_MIC, 2, 1, stac9766_stereo_mic);
+
+static const DECLARE_TLV_DB_LINEAR(master_tlv, -4600, 0);
+static const DECLARE_TLV_DB_LINEAR(record_tlv, 0, 2250);
+static const DECLARE_TLV_DB_LINEAR(beep_tlv, -4500, 0);
+static const DECLARE_TLV_DB_LINEAR(mix_tlv, -3450, 1200);
+
+static const struct snd_kcontrol_new stac9766_snd_ac97_controls[] = {
+       SOC_DOUBLE_TLV("Speaker Volume", AC97_MASTER, 8, 0, 31, 1, master_tlv),
+       SOC_SINGLE("Speaker Switch", AC97_MASTER, 15, 1, 1),
+       SOC_DOUBLE_TLV("Headphone Volume", AC97_HEADPHONE, 8, 0, 31, 1,
+                      master_tlv),
+       SOC_SINGLE("Headphone Switch", AC97_HEADPHONE, 15, 1, 1),
+       SOC_SINGLE_TLV("Mono Out Volume", AC97_MASTER_MONO, 0, 31, 1,
+                      master_tlv),
+       SOC_SINGLE("Mono Out Switch", AC97_MASTER_MONO, 15, 1, 1),
+
+       SOC_DOUBLE_TLV("Record Volume", AC97_REC_GAIN, 8, 0, 15, 0, record_tlv),
+       SOC_SINGLE("Record Switch", AC97_REC_GAIN, 15, 1, 1),
+
+
+       SOC_SINGLE_TLV("Beep Volume", AC97_PC_BEEP, 1, 15, 1, beep_tlv),
+       SOC_SINGLE("Beep Switch", AC97_PC_BEEP, 15, 1, 1),
+       SOC_SINGLE("Beep Frequency", AC97_PC_BEEP, 5, 127, 1),
+       SOC_SINGLE_TLV("Phone Volume", AC97_PHONE, 0, 31, 1, mix_tlv),
+       SOC_SINGLE("Phone Switch", AC97_PHONE, 15, 1, 1),
+
+       SOC_ENUM("Mic Boost1", stac9766_boost1_enum),
+       SOC_ENUM("Mic Boost2", stac9766_boost2_enum),
+       SOC_SINGLE_TLV("Mic Volume", AC97_MIC, 0, 31, 1, mix_tlv),
+       SOC_SINGLE("Mic Switch", AC97_MIC, 15, 1, 1),
+       SOC_ENUM("Stereo Mic", stac9766_stereo_mic_enum),
+
+       SOC_DOUBLE_TLV("Line Volume", AC97_LINE, 8, 0, 31, 1, mix_tlv),
+       SOC_SINGLE("Line Switch", AC97_LINE, 15, 1, 1),
+       SOC_DOUBLE_TLV("CD Volume", AC97_CD, 8, 0, 31, 1, mix_tlv),
+       SOC_SINGLE("CD Switch", AC97_CD, 15, 1, 1),
+       SOC_DOUBLE_TLV("AUX Volume", AC97_AUX, 8, 0, 31, 1, mix_tlv),
+       SOC_SINGLE("AUX Switch", AC97_AUX, 15, 1, 1),
+       SOC_DOUBLE_TLV("Video Volume", AC97_VIDEO, 8, 0, 31, 1, mix_tlv),
+       SOC_SINGLE("Video Switch", AC97_VIDEO, 15, 1, 1),
+
+       SOC_DOUBLE_TLV("DAC Volume", AC97_PCM, 8, 0, 31, 1, mix_tlv),
+       SOC_SINGLE("DAC Switch", AC97_PCM, 15, 1, 1),
+       SOC_SINGLE("Loopback Test Switch", AC97_GENERAL_PURPOSE, 7, 1, 0),
+       SOC_SINGLE("3D Volume", AC97_3D_CONTROL, 3, 2, 1),
+       SOC_SINGLE("3D Switch", AC97_GENERAL_PURPOSE, 13, 1, 0),
+
+       SOC_ENUM("SPDIF Mux", stac9766_SPDIF_enum),
+       SOC_ENUM("Mic1/2 Mux", stac9766_mic_enum),
+       SOC_ENUM("Record All Mux", stac9766_record_all_enum),
+       SOC_ENUM("Record Mux", stac9766_record_enum),
+       SOC_ENUM("Mono Mux", stac9766_mono_enum),
+       SOC_ENUM("Pop Bypass Mux", stac9766_popbypass_enum),
+};
+
+static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
+                              unsigned int val)
+{
+       u16 *cache = codec->reg_cache;
+
+       if (reg > AC97_STAC_PAGE0) {
+               stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
+               soc_ac97_ops.write(codec->ac97, reg, val);
+               stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
+               return 0;
+       }
+       if (reg / 2 > ARRAY_SIZE(stac9766_reg))
+               return -EIO;
+
+       soc_ac97_ops.write(codec->ac97, reg, val);
+       cache[reg / 2] = val;
+       return 0;
+}
+
+static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
+                                      unsigned int reg)
+{
+       u16 val = 0, *cache = codec->reg_cache;
+
+       if (reg > AC97_STAC_PAGE0) {
+               stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
+               val = soc_ac97_ops.read(codec->ac97, reg - AC97_STAC_PAGE0);
+               stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
+               return val;
+       }
+       if (reg / 2 > ARRAY_SIZE(stac9766_reg))
+               return -EIO;
+
+       if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
+               reg == AC97_INT_PAGING || reg == AC97_VENDOR_ID1 ||
+               reg == AC97_VENDOR_ID2) {
+
+               val = soc_ac97_ops.read(codec->ac97, reg);
+               return val;
+       }
+       return cache[reg / 2];
+}
+
+static int ac97_analog_prepare(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       unsigned short reg, vra;
+
+       vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS);
+
+       vra |= 0x1; /* enable variable rate audio */
+
+       stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               reg = AC97_PCM_FRONT_DAC_RATE;
+       else
+               reg = AC97_PCM_LR_ADC_RATE;
+
+       return stac9766_ac97_write(codec, reg, runtime->rate);
+}
+
+static int ac97_digital_prepare(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       unsigned short reg, vra;
+
+       stac9766_ac97_write(codec, AC97_SPDIF, 0x2002);
+
+       vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS);
+       vra |= 0x5; /* Enable VRA and SPDIF out */
+
+       stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra);
+
+       reg = AC97_PCM_FRONT_DAC_RATE;
+
+       return stac9766_ac97_write(codec, reg, runtime->rate);
+}
+
+static int ac97_digital_trigger(struct snd_pcm_substream *substream,
+                               int cmd, struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       unsigned short vra;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_STOP:
+               vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS);
+               vra &= !0x04;
+               stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra);
+               break;
+       }
+       return 0;
+}
+
+static int stac9766_set_bias_level(struct snd_soc_codec *codec,
+                                  enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON: /* full On */
+       case SND_SOC_BIAS_PREPARE: /* partial On */
+       case SND_SOC_BIAS_STANDBY: /* Off, with power */
+               stac9766_ac97_write(codec, AC97_POWERDOWN, 0x0000);
+               break;
+       case SND_SOC_BIAS_OFF: /* Off, without power */
+               /* disable everything including AC link */
+               stac9766_ac97_write(codec, AC97_POWERDOWN, 0xffff);
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+static int stac9766_reset(struct snd_soc_codec *codec, int try_warm)
+{
+       if (try_warm && soc_ac97_ops.warm_reset) {
+               soc_ac97_ops.warm_reset(codec->ac97);
+               if (stac9766_ac97_read(codec, 0) == stac9766_reg[0])
+                       return 1;
+       }
+
+       soc_ac97_ops.reset(codec->ac97);
+       if (soc_ac97_ops.warm_reset)
+               soc_ac97_ops.warm_reset(codec->ac97);
+       if (stac9766_ac97_read(codec, 0) != stac9766_reg[0])
+               return -EIO;
+       return 0;
+}
+
+static int stac9766_codec_suspend(struct platform_device *pdev,
+                                 pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       stac9766_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int stac9766_codec_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+       u16 id, reset;
+
+       reset = 0;
+       /* give the codec an AC97 warm reset to start the link */
+reset:
+       if (reset > 5) {
+               printk(KERN_ERR "stac9766 failed to resume");
+               return -EIO;
+       }
+       codec->ac97->bus->ops->warm_reset(codec->ac97);
+       id = soc_ac97_ops.read(codec->ac97, AC97_VENDOR_ID2);
+       if (id != 0x4c13) {
+               stac9766_reset(codec, 0);
+               reset++;
+               goto reset;
+       }
+       stac9766_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
+               stac9766_set_bias_level(codec, SND_SOC_BIAS_ON);
+
+       return 0;
+}
+
+static struct snd_soc_dai_ops stac9766_dai_ops_analog = {
+       .prepare = ac97_analog_prepare,
+};
+
+static struct snd_soc_dai_ops stac9766_dai_ops_digital = {
+       .prepare = ac97_digital_prepare,
+       .trigger = ac97_digital_trigger,
+};
+
+struct snd_soc_dai stac9766_dai[] = {
+{
+       .name = "stac9766 analog",
+       .id = 0,
+       .ac97_control = 1,
+
+       /* stream cababilities */
+       .playback = {
+               .stream_name = "stac9766 analog",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_48000,
+               .formats = SND_SOC_STD_AC97_FMTS,
+       },
+       .capture = {
+               .stream_name = "stac9766 analog",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_48000,
+               .formats = SND_SOC_STD_AC97_FMTS,
+       },
+       /* alsa ops */
+       .ops = &stac9766_dai_ops_analog,
+},
+{
+       .name = "stac9766 IEC958",
+       .id = 1,
+       .ac97_control = 1,
+
+       /* stream cababilities */
+       .playback = {
+               .stream_name = "stac9766 IEC958",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_32000 | \
+                       SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE,
+       },
+       /* alsa ops */
+       .ops = &stac9766_dai_ops_digital,
+}
+};
+EXPORT_SYMBOL_GPL(stac9766_dai);
+
+static int stac9766_codec_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       int ret = 0;
+
+       printk(KERN_INFO "STAC9766 SoC Audio Codec %s\n", STAC9766_VERSION);
+
+       socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+       if (socdev->card->codec == NULL)
+               return -ENOMEM;
+       codec = socdev->card->codec;
+       mutex_init(&codec->mutex);
+
+       codec->reg_cache = kmemdup(stac9766_reg, sizeof(stac9766_reg),
+                                  GFP_KERNEL);
+       if (codec->reg_cache == NULL) {
+               ret = -ENOMEM;
+               goto cache_err;
+       }
+       codec->reg_cache_size = sizeof(stac9766_reg);
+       codec->reg_cache_step = 2;
+
+       codec->name = "STAC9766";
+       codec->owner = THIS_MODULE;
+       codec->dai = stac9766_dai;
+       codec->num_dai = ARRAY_SIZE(stac9766_dai);
+       codec->write = stac9766_ac97_write;
+       codec->read = stac9766_ac97_read;
+       codec->set_bias_level = stac9766_set_bias_level;
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+       if (ret < 0)
+               goto codec_err;
+
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0)
+               goto pcm_err;
+
+       /* do a cold reset for the controller and then try
+        * a warm reset followed by an optional cold reset for codec */
+       stac9766_reset(codec, 0);
+       ret = stac9766_reset(codec, 1);
+       if (ret < 0) {
+               printk(KERN_ERR "Failed to reset STAC9766: AC97 link error\n");
+               goto reset_err;
+       }
+
+       stac9766_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       snd_soc_add_controls(codec, stac9766_snd_ac97_controls,
+                            ARRAY_SIZE(stac9766_snd_ac97_controls));
+
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0)
+               goto reset_err;
+       return 0;
+
+reset_err:
+       snd_soc_free_pcms(socdev);
+pcm_err:
+       snd_soc_free_ac97_codec(codec);
+codec_err:
+       kfree(codec->private_data);
+cache_err:
+       kfree(socdev->card->codec);
+       socdev->card->codec = NULL;
+       return ret;
+}
+
+static int stac9766_codec_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       if (codec == NULL)
+               return 0;
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_free_ac97_codec(codec);
+       kfree(codec->reg_cache);
+       kfree(codec);
+       return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_stac9766 = {
+       .probe = stac9766_codec_probe,
+       .remove = stac9766_codec_remove,
+       .suspend = stac9766_codec_suspend,
+       .resume = stac9766_codec_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_stac9766);
+
+MODULE_DESCRIPTION("ASoC stac9766 driver");
+MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/stac9766.h b/sound/soc/codecs/stac9766.h
new file mode 100644 (file)
index 0000000..65642eb
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * stac9766.h  --  STAC9766 Soc Audio driver
+ */
+
+#ifndef _STAC9766_H
+#define _STAC9766_H
+
+#define AC97_STAC_PAGE0 0x1000
+#define AC97_STAC_DA_CONTROL (AC97_STAC_PAGE0 | 0x6A)
+#define AC97_STAC_ANALOG_SPECIAL (AC97_STAC_PAGE0 | 0x6E)
+#define AC97_STAC_STEREO_MIC 0x78
+
+/* STAC9766 DAI ID's */
+#define STAC9766_DAI_AC97_ANALOG               0
+#define STAC9766_DAI_AC97_DIGITAL              1
+
+extern struct snd_soc_dai stac9766_dai[];
+extern struct snd_soc_codec_device soc_codec_dev_stac9766;
+
+
+#endif
index c3f4afb5d0173a581d0bcb18e8670d88c2d4d498..0b8dcb5cd729281bfd80264acdb5a58b9025636e 100644 (file)
@@ -86,7 +86,7 @@ static int tlv320aic23_write(struct snd_soc_codec *codec, unsigned int reg,
         */
 
        if ((reg < 0 || reg > 9) && (reg != 15)) {
-               printk(KERN_WARNING "%s Invalid register R%d\n", __func__, reg);
+               printk(KERN_WARNING "%s Invalid register R%u\n", __func__, reg);
                return -1;
        }
 
@@ -98,7 +98,7 @@ static int tlv320aic23_write(struct snd_soc_codec *codec, unsigned int reg,
        if (codec->hw_write(codec->control_data, data, 2) == 2)
                return 0;
 
-       printk(KERN_ERR "%s cannot write %03x to register R%d\n", __func__,
+       printk(KERN_ERR "%s cannot write %03x to register R%u\n", __func__,
               value, reg);
 
        return -EIO;
@@ -273,14 +273,14 @@ static const unsigned short sr_valid_mask[] = {
  * Every divisor is a factor of 11*12
  */
 #define SR_MULT (11*12)
-#define A(x) (x) ? (SR_MULT/x) : 0
+#define A(x) (SR_MULT/x)
 static const unsigned char sr_adc_mult_table[] = {
-       A(2), A(2), A(12), A(12),  A(0), A(0), A(3), A(1),
-       A(2), A(2), A(11), A(11),  A(0), A(0), A(0), A(1)
+       A(2), A(2), A(12), A(12),  0, 0, A(3), A(1),
+       A(2), A(2), A(11), A(11),  0, 0, 0, A(1)
 };
 static const unsigned char sr_dac_mult_table[] = {
-       A(2), A(12), A(2), A(12),  A(0), A(0), A(3), A(1),
-       A(2), A(11), A(2), A(11),  A(0), A(0), A(0), A(1)
+       A(2), A(12), A(2), A(12),  0, 0, A(3), A(1),
+       A(2), A(11), A(2), A(11),  0, 0, 0, A(1)
 };
 
 static unsigned get_score(int adc, int adc_l, int adc_h, int need_adc,
@@ -523,6 +523,8 @@ static int tlv320aic23_set_dai_fmt(struct snd_soc_dai *codec_dai,
        case SND_SOC_DAIFMT_I2S:
                iface_reg |= TLV320AIC23_FOR_I2S;
                break;
+       case SND_SOC_DAIFMT_DSP_A:
+               iface_reg |= TLV320AIC23_LRP_ON;
        case SND_SOC_DAIFMT_DSP_B:
                iface_reg |= TLV320AIC23_FOR_DSP;
                break;
index df7c8c281d2f8bec0aff69307bb7f1a5985fe1d3..4dbb853eef5ad056fec1e6cca89f0becbe579251 100644 (file)
@@ -115,6 +115,7 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
        0x00, /* REG_VIBRA_PWM_SET      (0x47)  */
        0x00, /* REG_ANAMIC_GAIN        (0x48)  */
        0x00, /* REG_MISC_SET_2         (0x49)  */
+       0x00, /* REG_SW_SHADOW          (0x4A)  - Shadow, non HW register */
 };
 
 /* codec private data */
@@ -125,6 +126,17 @@ struct twl4030_priv {
 
        struct snd_pcm_substream *master_substream;
        struct snd_pcm_substream *slave_substream;
+
+       unsigned int configured;
+       unsigned int rate;
+       unsigned int sample_bits;
+       unsigned int channels;
+
+       unsigned int sysclk;
+
+       /* Headset output state handling */
+       unsigned int hsl_enabled;
+       unsigned int hsr_enabled;
 };
 
 /*
@@ -161,7 +173,11 @@ static int twl4030_write(struct snd_soc_codec *codec,
                        unsigned int reg, unsigned int value)
 {
        twl4030_write_reg_cache(codec, reg, value);
-       return twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg);
+       if (likely(reg < TWL4030_REG_SW_SHADOW))
+               return twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value,
+                                           reg);
+       else
+               return 0;
 }
 
 static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
@@ -188,6 +204,7 @@ static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
 
 static void twl4030_init_chip(struct snd_soc_codec *codec)
 {
+       u8 *cache = codec->reg_cache;
        int i;
 
        /* clear CODECPDZ prior to setting register defaults */
@@ -195,7 +212,7 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
 
        /* set all audio section registers to reasonable defaults */
        for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++)
-               twl4030_write(codec, i, twl4030_reg[i]);
+               twl4030_write(codec, i, cache[i]);
 
 }
 
@@ -232,7 +249,7 @@ static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute)
                                        TWL4030_REG_PRECKL_CTL);
                reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL);
                twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
-                                       reg_val & (~TWL4030_PRECKL_GAIN),
+                                       reg_val & (~TWL4030_PRECKR_GAIN),
                                        TWL4030_REG_PRECKR_CTL);
 
                /* Disable PLL */
@@ -316,104 +333,60 @@ static void twl4030_power_down(struct snd_soc_codec *codec)
 }
 
 /* Earpiece */
-static const char *twl4030_earpiece_texts[] =
-               {"Off", "DACL1", "DACL2", "DACR1"};
-
-static const unsigned int twl4030_earpiece_values[] =
-               {0x0, 0x1, 0x2, 0x4};
-
-static const struct soc_enum twl4030_earpiece_enum =
-       SOC_VALUE_ENUM_SINGLE(TWL4030_REG_EAR_CTL, 1, 0x7,
-                       ARRAY_SIZE(twl4030_earpiece_texts),
-                       twl4030_earpiece_texts,
-                       twl4030_earpiece_values);
-
-static const struct snd_kcontrol_new twl4030_dapm_earpiece_control =
-SOC_DAPM_VALUE_ENUM("Route", twl4030_earpiece_enum);
+static const struct snd_kcontrol_new twl4030_dapm_earpiece_controls[] = {
+       SOC_DAPM_SINGLE("Voice", TWL4030_REG_EAR_CTL, 0, 1, 0),
+       SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_EAR_CTL, 1, 1, 0),
+       SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_EAR_CTL, 2, 1, 0),
+       SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_EAR_CTL, 3, 1, 0),
+};
 
 /* PreDrive Left */
-static const char *twl4030_predrivel_texts[] =
-               {"Off", "DACL1", "DACL2", "DACR2"};
-
-static const unsigned int twl4030_predrivel_values[] =
-               {0x0, 0x1, 0x2, 0x4};
-
-static const struct soc_enum twl4030_predrivel_enum =
-       SOC_VALUE_ENUM_SINGLE(TWL4030_REG_PREDL_CTL, 1, 0x7,
-                       ARRAY_SIZE(twl4030_predrivel_texts),
-                       twl4030_predrivel_texts,
-                       twl4030_predrivel_values);
-
-static const struct snd_kcontrol_new twl4030_dapm_predrivel_control =
-SOC_DAPM_VALUE_ENUM("Route", twl4030_predrivel_enum);
+static const struct snd_kcontrol_new twl4030_dapm_predrivel_controls[] = {
+       SOC_DAPM_SINGLE("Voice", TWL4030_REG_PREDL_CTL, 0, 1, 0),
+       SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_PREDL_CTL, 1, 1, 0),
+       SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PREDL_CTL, 2, 1, 0),
+       SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PREDL_CTL, 3, 1, 0),
+};
 
 /* PreDrive Right */
-static const char *twl4030_predriver_texts[] =
-               {"Off", "DACR1", "DACR2", "DACL2"};
-
-static const unsigned int twl4030_predriver_values[] =
-               {0x0, 0x1, 0x2, 0x4};
-
-static const struct soc_enum twl4030_predriver_enum =
-       SOC_VALUE_ENUM_SINGLE(TWL4030_REG_PREDR_CTL, 1, 0x7,
-                       ARRAY_SIZE(twl4030_predriver_texts),
-                       twl4030_predriver_texts,
-                       twl4030_predriver_values);
-
-static const struct snd_kcontrol_new twl4030_dapm_predriver_control =
-SOC_DAPM_VALUE_ENUM("Route", twl4030_predriver_enum);
+static const struct snd_kcontrol_new twl4030_dapm_predriver_controls[] = {
+       SOC_DAPM_SINGLE("Voice", TWL4030_REG_PREDR_CTL, 0, 1, 0),
+       SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_PREDR_CTL, 1, 1, 0),
+       SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PREDR_CTL, 2, 1, 0),
+       SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PREDR_CTL, 3, 1, 0),
+};
 
 /* Headset Left */
-static const char *twl4030_hsol_texts[] =
-               {"Off", "DACL1", "DACL2"};
-
-static const struct soc_enum twl4030_hsol_enum =
-       SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 1,
-                       ARRAY_SIZE(twl4030_hsol_texts),
-                       twl4030_hsol_texts);
-
-static const struct snd_kcontrol_new twl4030_dapm_hsol_control =
-SOC_DAPM_ENUM("Route", twl4030_hsol_enum);
+static const struct snd_kcontrol_new twl4030_dapm_hsol_controls[] = {
+       SOC_DAPM_SINGLE("Voice", TWL4030_REG_HS_SEL, 0, 1, 0),
+       SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_HS_SEL, 1, 1, 0),
+       SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_HS_SEL, 2, 1, 0),
+};
 
 /* Headset Right */
-static const char *twl4030_hsor_texts[] =
-               {"Off", "DACR1", "DACR2"};
-
-static const struct soc_enum twl4030_hsor_enum =
-       SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 4,
-                       ARRAY_SIZE(twl4030_hsor_texts),
-                       twl4030_hsor_texts);
-
-static const struct snd_kcontrol_new twl4030_dapm_hsor_control =
-SOC_DAPM_ENUM("Route", twl4030_hsor_enum);
+static const struct snd_kcontrol_new twl4030_dapm_hsor_controls[] = {
+       SOC_DAPM_SINGLE("Voice", TWL4030_REG_HS_SEL, 3, 1, 0),
+       SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_HS_SEL, 4, 1, 0),
+       SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_HS_SEL, 5, 1, 0),
+};
 
 /* Carkit Left */
-static const char *twl4030_carkitl_texts[] =
-               {"Off", "DACL1", "DACL2"};
-
-static const struct soc_enum twl4030_carkitl_enum =
-       SOC_ENUM_SINGLE(TWL4030_REG_PRECKL_CTL, 1,
-                       ARRAY_SIZE(twl4030_carkitl_texts),
-                       twl4030_carkitl_texts);
-
-static const struct snd_kcontrol_new twl4030_dapm_carkitl_control =
-SOC_DAPM_ENUM("Route", twl4030_carkitl_enum);
+static const struct snd_kcontrol_new twl4030_dapm_carkitl_controls[] = {
+       SOC_DAPM_SINGLE("Voice", TWL4030_REG_PRECKL_CTL, 0, 1, 0),
+       SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_PRECKL_CTL, 1, 1, 0),
+       SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PRECKL_CTL, 2, 1, 0),
+};
 
 /* Carkit Right */
-static const char *twl4030_carkitr_texts[] =
-               {"Off", "DACR1", "DACR2"};
-
-static const struct soc_enum twl4030_carkitr_enum =
-       SOC_ENUM_SINGLE(TWL4030_REG_PRECKR_CTL, 1,
-                       ARRAY_SIZE(twl4030_carkitr_texts),
-                       twl4030_carkitr_texts);
-
-static const struct snd_kcontrol_new twl4030_dapm_carkitr_control =
-SOC_DAPM_ENUM("Route", twl4030_carkitr_enum);
+static const struct snd_kcontrol_new twl4030_dapm_carkitr_controls[] = {
+       SOC_DAPM_SINGLE("Voice", TWL4030_REG_PRECKR_CTL, 0, 1, 0),
+       SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_PRECKR_CTL, 1, 1, 0),
+       SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PRECKR_CTL, 2, 1, 0),
+};
 
 /* Handsfree Left */
 static const char *twl4030_handsfreel_texts[] =
-               {"Voice", "DACL1", "DACL2", "DACR2"};
+               {"Voice", "AudioL1", "AudioL2", "AudioR2"};
 
 static const struct soc_enum twl4030_handsfreel_enum =
        SOC_ENUM_SINGLE(TWL4030_REG_HFL_CTL, 0,
@@ -423,9 +396,13 @@ static const struct soc_enum twl4030_handsfreel_enum =
 static const struct snd_kcontrol_new twl4030_dapm_handsfreel_control =
 SOC_DAPM_ENUM("Route", twl4030_handsfreel_enum);
 
+/* Handsfree Left virtual mute */
+static const struct snd_kcontrol_new twl4030_dapm_handsfreelmute_control =
+       SOC_DAPM_SINGLE("Switch", TWL4030_REG_SW_SHADOW, 0, 1, 0);
+
 /* Handsfree Right */
 static const char *twl4030_handsfreer_texts[] =
-               {"Voice", "DACR1", "DACR2", "DACL2"};
+               {"Voice", "AudioR1", "AudioR2", "AudioL2"};
 
 static const struct soc_enum twl4030_handsfreer_enum =
        SOC_ENUM_SINGLE(TWL4030_REG_HFR_CTL, 0,
@@ -435,37 +412,48 @@ static const struct soc_enum twl4030_handsfreer_enum =
 static const struct snd_kcontrol_new twl4030_dapm_handsfreer_control =
 SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum);
 
-/* Left analog microphone selection */
-static const char *twl4030_analoglmic_texts[] =
-               {"Off", "Main mic", "Headset mic", "AUXL", "Carkit mic"};
+/* Handsfree Right virtual mute */
+static const struct snd_kcontrol_new twl4030_dapm_handsfreermute_control =
+       SOC_DAPM_SINGLE("Switch", TWL4030_REG_SW_SHADOW, 1, 1, 0);
 
-static const unsigned int twl4030_analoglmic_values[] =
-               {0x0, 0x1, 0x2, 0x4, 0x8};
+/* Vibra */
+/* Vibra audio path selection */
+static const char *twl4030_vibra_texts[] =
+               {"AudioL1", "AudioR1", "AudioL2", "AudioR2"};
 
-static const struct soc_enum twl4030_analoglmic_enum =
-       SOC_VALUE_ENUM_SINGLE(TWL4030_REG_ANAMICL, 0, 0xf,
-                       ARRAY_SIZE(twl4030_analoglmic_texts),
-                       twl4030_analoglmic_texts,
-                       twl4030_analoglmic_values);
+static const struct soc_enum twl4030_vibra_enum =
+       SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 2,
+                       ARRAY_SIZE(twl4030_vibra_texts),
+                       twl4030_vibra_texts);
 
-static const struct snd_kcontrol_new twl4030_dapm_analoglmic_control =
-SOC_DAPM_VALUE_ENUM("Route", twl4030_analoglmic_enum);
+static const struct snd_kcontrol_new twl4030_dapm_vibra_control =
+SOC_DAPM_ENUM("Route", twl4030_vibra_enum);
 
-/* Right analog microphone selection */
-static const char *twl4030_analogrmic_texts[] =
-               {"Off", "Sub mic", "AUXR"};
+/* Vibra path selection: local vibrator (PWM) or audio driven */
+static const char *twl4030_vibrapath_texts[] =
+               {"Local vibrator", "Audio"};
 
-static const unsigned int twl4030_analogrmic_values[] =
-               {0x0, 0x1, 0x4};
+static const struct soc_enum twl4030_vibrapath_enum =
+       SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 4,
+                       ARRAY_SIZE(twl4030_vibrapath_texts),
+                       twl4030_vibrapath_texts);
 
-static const struct soc_enum twl4030_analogrmic_enum =
-       SOC_VALUE_ENUM_SINGLE(TWL4030_REG_ANAMICR, 0, 0x5,
-                       ARRAY_SIZE(twl4030_analogrmic_texts),
-                       twl4030_analogrmic_texts,
-                       twl4030_analogrmic_values);
+static const struct snd_kcontrol_new twl4030_dapm_vibrapath_control =
+SOC_DAPM_ENUM("Route", twl4030_vibrapath_enum);
 
-static const struct snd_kcontrol_new twl4030_dapm_analogrmic_control =
-SOC_DAPM_VALUE_ENUM("Route", twl4030_analogrmic_enum);
+/* Left analog microphone selection */
+static const struct snd_kcontrol_new twl4030_dapm_analoglmic_controls[] = {
+       SOC_DAPM_SINGLE("Main mic", TWL4030_REG_ANAMICL, 0, 1, 0),
+       SOC_DAPM_SINGLE("Headset mic", TWL4030_REG_ANAMICL, 1, 1, 0),
+       SOC_DAPM_SINGLE("AUXL", TWL4030_REG_ANAMICL, 2, 1, 0),
+       SOC_DAPM_SINGLE("Carkit mic", TWL4030_REG_ANAMICL, 3, 1, 0),
+};
+
+/* Right analog microphone selection */
+static const struct snd_kcontrol_new twl4030_dapm_analogrmic_controls[] = {
+       SOC_DAPM_SINGLE("Sub mic", TWL4030_REG_ANAMICR, 0, 1, 0),
+       SOC_DAPM_SINGLE("AUXR", TWL4030_REG_ANAMICR, 2, 1, 0),
+};
 
 /* TX1 L/R Analog/Digital microphone selection */
 static const char *twl4030_micpathtx1_texts[] =
@@ -507,6 +495,10 @@ static const struct snd_kcontrol_new twl4030_dapm_abypassr2_control =
 static const struct snd_kcontrol_new twl4030_dapm_abypassl2_control =
        SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL2_APGA_CTL, 2, 1, 0);
 
+/* Analog bypass for Voice */
+static const struct snd_kcontrol_new twl4030_dapm_abypassv_control =
+       SOC_DAPM_SINGLE("Switch", TWL4030_REG_VDL_APGA_CTL, 2, 1, 0);
+
 /* Digital bypass gain, 0 mutes the bypass */
 static const unsigned int twl4030_dapm_dbypass_tlv[] = {
        TLV_DB_RANGE_HEAD(2),
@@ -526,6 +518,18 @@ static const struct snd_kcontrol_new twl4030_dapm_dbypassr_control =
                        TWL4030_REG_ATX2ARXPGA, 0, 7, 0,
                        twl4030_dapm_dbypass_tlv);
 
+/*
+ * Voice Sidetone GAIN volume control:
+ * from -51 to -10 dB in 1 dB steps (mute instead of -51 dB)
+ */
+static DECLARE_TLV_DB_SCALE(twl4030_dapm_dbypassv_tlv, -5100, 100, 1);
+
+/* Digital bypass voice: sidetone (VUL -> VDL)*/
+static const struct snd_kcontrol_new twl4030_dapm_dbypassv_control =
+       SOC_DAPM_SINGLE_TLV("Volume",
+                       TWL4030_REG_VSTPGA, 0, 0x29, 0,
+                       twl4030_dapm_dbypassv_tlv);
+
 static int micpath_event(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
@@ -556,63 +560,143 @@ static int micpath_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
-static int handsfree_event(struct snd_soc_dapm_widget *w,
-               struct snd_kcontrol *kcontrol, int event)
+static void handsfree_ramp(struct snd_soc_codec *codec, int reg, int ramp)
 {
-       struct soc_enum *e = (struct soc_enum *)w->kcontrols->private_value;
        unsigned char hs_ctl;
 
-       hs_ctl = twl4030_read_reg_cache(w->codec, e->reg);
+       hs_ctl = twl4030_read_reg_cache(codec, reg);
 
-       if (hs_ctl & TWL4030_HF_CTL_REF_EN) {
+       if (ramp) {
+               /* HF ramp-up */
+               hs_ctl |= TWL4030_HF_CTL_REF_EN;
+               twl4030_write(codec, reg, hs_ctl);
+               udelay(10);
                hs_ctl |= TWL4030_HF_CTL_RAMP_EN;
-               twl4030_write(w->codec, e->reg, hs_ctl);
+               twl4030_write(codec, reg, hs_ctl);
+               udelay(40);
                hs_ctl |= TWL4030_HF_CTL_LOOP_EN;
-               twl4030_write(w->codec, e->reg, hs_ctl);
                hs_ctl |= TWL4030_HF_CTL_HB_EN;
-               twl4030_write(w->codec, e->reg, hs_ctl);
+               twl4030_write(codec, reg, hs_ctl);
        } else {
-               hs_ctl &= ~(TWL4030_HF_CTL_RAMP_EN | TWL4030_HF_CTL_LOOP_EN
-                               | TWL4030_HF_CTL_HB_EN);
-               twl4030_write(w->codec, e->reg, hs_ctl);
+               /* HF ramp-down */
+               hs_ctl &= ~TWL4030_HF_CTL_LOOP_EN;
+               hs_ctl &= ~TWL4030_HF_CTL_HB_EN;
+               twl4030_write(codec, reg, hs_ctl);
+               hs_ctl &= ~TWL4030_HF_CTL_RAMP_EN;
+               twl4030_write(codec, reg, hs_ctl);
+               udelay(40);
+               hs_ctl &= ~TWL4030_HF_CTL_REF_EN;
+               twl4030_write(codec, reg, hs_ctl);
        }
+}
 
+static int handsfreelpga_event(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               handsfree_ramp(w->codec, TWL4030_REG_HFL_CTL, 1);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               handsfree_ramp(w->codec, TWL4030_REG_HFL_CTL, 0);
+               break;
+       }
        return 0;
 }
 
-static int headsetl_event(struct snd_soc_dapm_widget *w,
+static int handsfreerpga_event(struct snd_soc_dapm_widget *w,
                struct snd_kcontrol *kcontrol, int event)
+{
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               handsfree_ramp(w->codec, TWL4030_REG_HFR_CTL, 1);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               handsfree_ramp(w->codec, TWL4030_REG_HFR_CTL, 0);
+               break;
+       }
+       return 0;
+}
+
+static void headset_ramp(struct snd_soc_codec *codec, int ramp)
 {
        unsigned char hs_gain, hs_pop;
+       struct twl4030_priv *twl4030 = codec->private_data;
+       /* Base values for ramp delay calculation: 2^19 - 2^26 */
+       unsigned int ramp_base[] = {524288, 1048576, 2097152, 4194304,
+                                   8388608, 16777216, 33554432, 67108864};
 
-       /* Save the current volume */
-       hs_gain = twl4030_read_reg_cache(w->codec, TWL4030_REG_HS_GAIN_SET);
-       hs_pop = twl4030_read_reg_cache(w->codec, TWL4030_REG_HS_POPN_SET);
+       hs_gain = twl4030_read_reg_cache(codec, TWL4030_REG_HS_GAIN_SET);
+       hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
 
-       switch (event) {
-       case SND_SOC_DAPM_POST_PMU:
-               /* Do the anti-pop/bias ramp enable according to the TRM */
+       if (ramp) {
+               /* Headset ramp-up according to the TRM */
                hs_pop |= TWL4030_VMID_EN;
-               twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop);
-               /* Is this needed? Can we just use whatever gain here? */
-               twl4030_write(w->codec, TWL4030_REG_HS_GAIN_SET,
-                               (hs_gain & (~0x0f)) | 0x0a);
+               twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+               twl4030_write(codec, TWL4030_REG_HS_GAIN_SET, hs_gain);
                hs_pop |= TWL4030_RAMP_EN;
-               twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop);
-
-               /* Restore the original volume */
-               twl4030_write(w->codec, TWL4030_REG_HS_GAIN_SET, hs_gain);
-               break;
-       case SND_SOC_DAPM_POST_PMD:
-               /* Do the anti-pop/bias ramp disable according to the TRM */
+               twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+       } else {
+               /* Headset ramp-down _not_ according to
+                * the TRM, but in a way that it is working */
                hs_pop &= ~TWL4030_RAMP_EN;
-               twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+               twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+               /* Wait ramp delay time + 1, so the VMID can settle */
+               mdelay((ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] /
+                       twl4030->sysclk) + 1);
                /* Bypass the reg_cache to mute the headset */
                twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
                                        hs_gain & (~0x0f),
                                        TWL4030_REG_HS_GAIN_SET);
+
                hs_pop &= ~TWL4030_VMID_EN;
-               twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+               twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+       }
+}
+
+static int headsetlpga_event(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       struct twl4030_priv *twl4030 = w->codec->private_data;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               /* Do the ramp-up only once */
+               if (!twl4030->hsr_enabled)
+                       headset_ramp(w->codec, 1);
+
+               twl4030->hsl_enabled = 1;
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               /* Do the ramp-down only if both headsetL/R is disabled */
+               if (!twl4030->hsr_enabled)
+                       headset_ramp(w->codec, 0);
+
+               twl4030->hsl_enabled = 0;
+               break;
+       }
+       return 0;
+}
+
+static int headsetrpga_event(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       struct twl4030_priv *twl4030 = w->codec->private_data;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               /* Do the ramp-up only once */
+               if (!twl4030->hsl_enabled)
+                       headset_ramp(w->codec, 1);
+
+               twl4030->hsr_enabled = 1;
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               /* Do the ramp-down only if both headsetL/R is disabled */
+               if (!twl4030->hsl_enabled)
+                       headset_ramp(w->codec, 0);
+
+               twl4030->hsr_enabled = 0;
                break;
        }
        return 0;
@@ -624,7 +708,7 @@ static int bypass_event(struct snd_soc_dapm_widget *w,
        struct soc_mixer_control *m =
                (struct soc_mixer_control *)w->kcontrols->private_value;
        struct twl4030_priv *twl4030 = w->codec->private_data;
-       unsigned char reg;
+       unsigned char reg, misc;
 
        reg = twl4030_read_reg_cache(w->codec, m->reg);
 
@@ -636,14 +720,34 @@ static int bypass_event(struct snd_soc_dapm_widget *w,
                else
                        twl4030->bypass_state &=
                                ~(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL));
+       } else if (m->reg == TWL4030_REG_VDL_APGA_CTL) {
+               /* Analog voice bypass */
+               if (reg & (1 << m->shift))
+                       twl4030->bypass_state |= (1 << 4);
+               else
+                       twl4030->bypass_state &= ~(1 << 4);
+       } else if (m->reg == TWL4030_REG_VSTPGA) {
+               /* Voice digital bypass */
+               if (reg)
+                       twl4030->bypass_state |= (1 << 5);
+               else
+                       twl4030->bypass_state &= ~(1 << 5);
        } else {
                /* Digital bypass */
                if (reg & (0x7 << m->shift))
-                       twl4030->bypass_state |= (1 << (m->shift ? 5 : 4));
+                       twl4030->bypass_state |= (1 << (m->shift ? 7 : 6));
                else
-                       twl4030->bypass_state &= ~(1 << (m->shift ? 5 : 4));
+                       twl4030->bypass_state &= ~(1 << (m->shift ? 7 : 6));
        }
 
+       /* Enable master analog loopback mode if any analog switch is enabled*/
+       misc = twl4030_read_reg_cache(w->codec, TWL4030_REG_MISC_SET_1);
+       if (twl4030->bypass_state & 0x1F)
+               misc |= TWL4030_FMLOOP_EN;
+       else
+               misc &= ~TWL4030_FMLOOP_EN;
+       twl4030_write(w->codec, TWL4030_REG_MISC_SET_1, misc);
+
        if (w->codec->bias_level == SND_SOC_BIAS_STANDBY) {
                if (twl4030->bypass_state)
                        twl4030_codec_mute(w->codec, 0);
@@ -810,6 +914,48 @@ static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
        return err;
 }
 
+/* Codec operation modes */
+static const char *twl4030_op_modes_texts[] = {
+       "Option 2 (voice/audio)", "Option 1 (audio)"
+};
+
+static const struct soc_enum twl4030_op_modes_enum =
+       SOC_ENUM_SINGLE(TWL4030_REG_CODEC_MODE, 0,
+                       ARRAY_SIZE(twl4030_op_modes_texts),
+                       twl4030_op_modes_texts);
+
+int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct twl4030_priv *twl4030 = codec->private_data;
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       unsigned short val;
+       unsigned short mask, bitmask;
+
+       if (twl4030->configured) {
+               printk(KERN_ERR "twl4030 operation mode cannot be "
+                       "changed on-the-fly\n");
+               return -EBUSY;
+       }
+
+       for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
+               ;
+       if (ucontrol->value.enumerated.item[0] > e->max - 1)
+               return -EINVAL;
+
+       val = ucontrol->value.enumerated.item[0] << e->shift_l;
+       mask = (bitmask - 1) << e->shift_l;
+       if (e->shift_l != e->shift_r) {
+               if (ucontrol->value.enumerated.item[1] > e->max - 1)
+                       return -EINVAL;
+               val |= ucontrol->value.enumerated.item[1] << e->shift_r;
+               mask |= (bitmask - 1) << e->shift_r;
+       }
+
+       return snd_soc_update_bits(codec, e->reg, mask, val);
+}
+
 /*
  * FGAIN volume control:
  * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB)
@@ -823,6 +969,12 @@ static DECLARE_TLV_DB_SCALE(digital_fine_tlv, -6300, 100, 1);
  */
 static DECLARE_TLV_DB_SCALE(digital_coarse_tlv, 0, 600, 0);
 
+/*
+ * Voice Downlink GAIN volume control:
+ * from -37 to 12 dB in 1 dB steps (mute instead of -37 dB)
+ */
+static DECLARE_TLV_DB_SCALE(digital_voice_downlink_tlv, -3700, 100, 1);
+
 /*
  * Analog playback gain
  * -24 dB to 12 dB in 2 dB steps
@@ -864,7 +1016,32 @@ static const struct soc_enum twl4030_rampdelay_enum =
                        ARRAY_SIZE(twl4030_rampdelay_texts),
                        twl4030_rampdelay_texts);
 
+/* Vibra H-bridge direction mode */
+static const char *twl4030_vibradirmode_texts[] = {
+       "Vibra H-bridge direction", "Audio data MSB",
+};
+
+static const struct soc_enum twl4030_vibradirmode_enum =
+       SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 5,
+                       ARRAY_SIZE(twl4030_vibradirmode_texts),
+                       twl4030_vibradirmode_texts);
+
+/* Vibra H-bridge direction */
+static const char *twl4030_vibradir_texts[] = {
+       "Positive polarity", "Negative polarity",
+};
+
+static const struct soc_enum twl4030_vibradir_enum =
+       SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 1,
+                       ARRAY_SIZE(twl4030_vibradir_texts),
+                       twl4030_vibradir_texts);
+
 static const struct snd_kcontrol_new twl4030_snd_controls[] = {
+       /* Codec operation mode control */
+       SOC_ENUM_EXT("Codec Operation Mode", twl4030_op_modes_enum,
+               snd_soc_get_enum_double,
+               snd_soc_put_twl4030_opmode_enum_double),
+
        /* Common playback gain controls */
        SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume",
                TWL4030_REG_ARXL1PGA, TWL4030_REG_ARXR1PGA,
@@ -893,6 +1070,16 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = {
                TWL4030_REG_ARXL2_APGA_CTL, TWL4030_REG_ARXR2_APGA_CTL,
                1, 1, 0),
 
+       /* Common voice downlink gain controls */
+       SOC_SINGLE_TLV("DAC Voice Digital Downlink Volume",
+               TWL4030_REG_VRXPGA, 0, 0x31, 0, digital_voice_downlink_tlv),
+
+       SOC_SINGLE_TLV("DAC Voice Analog Downlink Volume",
+               TWL4030_REG_VDL_APGA_CTL, 3, 0x12, 1, analog_tlv),
+
+       SOC_SINGLE("DAC Voice Analog Downlink Switch",
+               TWL4030_REG_VDL_APGA_CTL, 1, 1, 0),
+
        /* Separate output gain controls */
        SOC_DOUBLE_R_TLV_TWL4030("PreDriv Playback Volume",
                TWL4030_REG_PREDL_CTL, TWL4030_REG_PREDR_CTL,
@@ -920,6 +1107,9 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = {
                0, 3, 5, 0, input_gain_tlv),
 
        SOC_ENUM("HS ramp delay", twl4030_rampdelay_enum),
+
+       SOC_ENUM("Vibra H-bridge mode", twl4030_vibradirmode_enum),
+       SOC_ENUM("Vibra H-bridge direction", twl4030_vibradir_enum),
 };
 
 static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
@@ -947,26 +1137,19 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
        SND_SOC_DAPM_OUTPUT("CARKITR"),
        SND_SOC_DAPM_OUTPUT("HFL"),
        SND_SOC_DAPM_OUTPUT("HFR"),
+       SND_SOC_DAPM_OUTPUT("VIBRA"),
 
        /* DACs */
-       SND_SOC_DAPM_DAC("DAC Right1", "Right Front Playback",
+       SND_SOC_DAPM_DAC("DAC Right1", "Right Front HiFi Playback",
                        SND_SOC_NOPM, 0, 0),
-       SND_SOC_DAPM_DAC("DAC Left1", "Left Front Playback",
+       SND_SOC_DAPM_DAC("DAC Left1", "Left Front HiFi Playback",
                        SND_SOC_NOPM, 0, 0),
-       SND_SOC_DAPM_DAC("DAC Right2", "Right Rear Playback",
+       SND_SOC_DAPM_DAC("DAC Right2", "Right Rear HiFi Playback",
                        SND_SOC_NOPM, 0, 0),
-       SND_SOC_DAPM_DAC("DAC Left2", "Left Rear Playback",
+       SND_SOC_DAPM_DAC("DAC Left2", "Left Rear HiFi Playback",
+                       SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_DAC("DAC Voice", "Voice Playback",
                        SND_SOC_NOPM, 0, 0),
-
-       /* Analog PGAs */
-       SND_SOC_DAPM_PGA("ARXR1_APGA", TWL4030_REG_ARXR1_APGA_CTL,
-                       0, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("ARXL1_APGA", TWL4030_REG_ARXL1_APGA_CTL,
-                       0, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("ARXR2_APGA", TWL4030_REG_ARXR2_APGA_CTL,
-                       0, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("ARXL2_APGA", TWL4030_REG_ARXL2_APGA_CTL,
-                       0, 0, NULL, 0),
 
        /* Analog bypasses */
        SND_SOC_DAPM_SWITCH_E("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0,
@@ -981,6 +1164,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
        SND_SOC_DAPM_SWITCH_E("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0,
                        &twl4030_dapm_abypassl2_control,
                        bypass_event, SND_SOC_DAPM_POST_REG),
+       SND_SOC_DAPM_SWITCH_E("Voice Analog Loopback", SND_SOC_NOPM, 0, 0,
+                       &twl4030_dapm_abypassv_control,
+                       bypass_event, SND_SOC_DAPM_POST_REG),
 
        /* Digital bypasses */
        SND_SOC_DAPM_SWITCH_E("Left Digital Loopback", SND_SOC_NOPM, 0, 0,
@@ -989,43 +1175,88 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
        SND_SOC_DAPM_SWITCH_E("Right Digital Loopback", SND_SOC_NOPM, 0, 0,
                        &twl4030_dapm_dbypassr_control, bypass_event,
                        SND_SOC_DAPM_POST_REG),
+       SND_SOC_DAPM_SWITCH_E("Voice Digital Loopback", SND_SOC_NOPM, 0, 0,
+                       &twl4030_dapm_dbypassv_control, bypass_event,
+                       SND_SOC_DAPM_POST_REG),
 
-       SND_SOC_DAPM_MIXER("Analog R1 Playback Mixer", TWL4030_REG_AVDAC_CTL,
-                       0, 0, NULL, 0),
-       SND_SOC_DAPM_MIXER("Analog L1 Playback Mixer", TWL4030_REG_AVDAC_CTL,
-                       1, 0, NULL, 0),
-       SND_SOC_DAPM_MIXER("Analog R2 Playback Mixer", TWL4030_REG_AVDAC_CTL,
-                       2, 0, NULL, 0),
-       SND_SOC_DAPM_MIXER("Analog L2 Playback Mixer", TWL4030_REG_AVDAC_CTL,
-                       3, 0, NULL, 0),
-
-       /* Output MUX controls */
+       /* Digital mixers, power control for the physical DACs */
+       SND_SOC_DAPM_MIXER("Digital R1 Playback Mixer",
+                       TWL4030_REG_AVDAC_CTL, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("Digital L1 Playback Mixer",
+                       TWL4030_REG_AVDAC_CTL, 1, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("Digital R2 Playback Mixer",
+                       TWL4030_REG_AVDAC_CTL, 2, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("Digital L2 Playback Mixer",
+                       TWL4030_REG_AVDAC_CTL, 3, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("Digital Voice Playback Mixer",
+                       TWL4030_REG_AVDAC_CTL, 4, 0, NULL, 0),
+
+       /* Analog mixers, power control for the physical PGAs */
+       SND_SOC_DAPM_MIXER("Analog R1 Playback Mixer",
+                       TWL4030_REG_ARXR1_APGA_CTL, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("Analog L1 Playback Mixer",
+                       TWL4030_REG_ARXL1_APGA_CTL, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("Analog R2 Playback Mixer",
+                       TWL4030_REG_ARXR2_APGA_CTL, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("Analog L2 Playback Mixer",
+                       TWL4030_REG_ARXL2_APGA_CTL, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("Analog Voice Playback Mixer",
+                       TWL4030_REG_VDL_APGA_CTL, 0, 0, NULL, 0),
+
+       /* Output MIXER controls */
        /* Earpiece */
-       SND_SOC_DAPM_VALUE_MUX("Earpiece Mux", SND_SOC_NOPM, 0, 0,
-               &twl4030_dapm_earpiece_control),
+       SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0,
+                       &twl4030_dapm_earpiece_controls[0],
+                       ARRAY_SIZE(twl4030_dapm_earpiece_controls)),
        /* PreDrivL/R */
-       SND_SOC_DAPM_VALUE_MUX("PredriveL Mux", SND_SOC_NOPM, 0, 0,
-               &twl4030_dapm_predrivel_control),
-       SND_SOC_DAPM_VALUE_MUX("PredriveR Mux", SND_SOC_NOPM, 0, 0,
-               &twl4030_dapm_predriver_control),
+       SND_SOC_DAPM_MIXER("PredriveL Mixer", SND_SOC_NOPM, 0, 0,
+                       &twl4030_dapm_predrivel_controls[0],
+                       ARRAY_SIZE(twl4030_dapm_predrivel_controls)),
+       SND_SOC_DAPM_MIXER("PredriveR Mixer", SND_SOC_NOPM, 0, 0,
+                       &twl4030_dapm_predriver_controls[0],
+                       ARRAY_SIZE(twl4030_dapm_predriver_controls)),
        /* HeadsetL/R */
-       SND_SOC_DAPM_MUX_E("HeadsetL Mux", SND_SOC_NOPM, 0, 0,
-               &twl4030_dapm_hsol_control, headsetl_event,
-               SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
-       SND_SOC_DAPM_MUX("HeadsetR Mux", SND_SOC_NOPM, 0, 0,
-               &twl4030_dapm_hsor_control),
+       SND_SOC_DAPM_MIXER("HeadsetL Mixer", SND_SOC_NOPM, 0, 0,
+                       &twl4030_dapm_hsol_controls[0],
+                       ARRAY_SIZE(twl4030_dapm_hsol_controls)),
+       SND_SOC_DAPM_PGA_E("HeadsetL PGA", SND_SOC_NOPM,
+                       0, 0, NULL, 0, headsetlpga_event,
+                       SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_MIXER("HeadsetR Mixer", SND_SOC_NOPM, 0, 0,
+                       &twl4030_dapm_hsor_controls[0],
+                       ARRAY_SIZE(twl4030_dapm_hsor_controls)),
+       SND_SOC_DAPM_PGA_E("HeadsetR PGA", SND_SOC_NOPM,
+                       0, 0, NULL, 0, headsetrpga_event,
+                       SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
        /* CarkitL/R */
-       SND_SOC_DAPM_MUX("CarkitL Mux", SND_SOC_NOPM, 0, 0,
-               &twl4030_dapm_carkitl_control),
-       SND_SOC_DAPM_MUX("CarkitR Mux", SND_SOC_NOPM, 0, 0,
-               &twl4030_dapm_carkitr_control),
+       SND_SOC_DAPM_MIXER("CarkitL Mixer", SND_SOC_NOPM, 0, 0,
+                       &twl4030_dapm_carkitl_controls[0],
+                       ARRAY_SIZE(twl4030_dapm_carkitl_controls)),
+       SND_SOC_DAPM_MIXER("CarkitR Mixer", SND_SOC_NOPM, 0, 0,
+                       &twl4030_dapm_carkitr_controls[0],
+                       ARRAY_SIZE(twl4030_dapm_carkitr_controls)),
+
+       /* Output MUX controls */
        /* HandsfreeL/R */
-       SND_SOC_DAPM_MUX_E("HandsfreeL Mux", TWL4030_REG_HFL_CTL, 5, 0,
-               &twl4030_dapm_handsfreel_control, handsfree_event,
-               SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
-       SND_SOC_DAPM_MUX_E("HandsfreeR Mux", TWL4030_REG_HFR_CTL, 5, 0,
-               &twl4030_dapm_handsfreer_control, handsfree_event,
-               SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_MUX("HandsfreeL Mux", SND_SOC_NOPM, 0, 0,
+               &twl4030_dapm_handsfreel_control),
+       SND_SOC_DAPM_SWITCH("HandsfreeL Switch", SND_SOC_NOPM, 0, 0,
+                       &twl4030_dapm_handsfreelmute_control),
+       SND_SOC_DAPM_PGA_E("HandsfreeL PGA", SND_SOC_NOPM,
+                       0, 0, NULL, 0, handsfreelpga_event,
+                       SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_MUX("HandsfreeR Mux", SND_SOC_NOPM, 5, 0,
+               &twl4030_dapm_handsfreer_control),
+       SND_SOC_DAPM_SWITCH("HandsfreeR Switch", SND_SOC_NOPM, 0, 0,
+                       &twl4030_dapm_handsfreermute_control),
+       SND_SOC_DAPM_PGA_E("HandsfreeR PGA", SND_SOC_NOPM,
+                       0, 0, NULL, 0, handsfreerpga_event,
+                       SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+       /* Vibra */
+       SND_SOC_DAPM_MUX("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0,
+               &twl4030_dapm_vibra_control),
+       SND_SOC_DAPM_MUX("Vibra Route", SND_SOC_NOPM, 0, 0,
+               &twl4030_dapm_vibrapath_control),
 
        /* Introducing four virtual ADC, since TWL4030 have four channel for
           capture */
@@ -1050,11 +1281,15 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
                SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD|
                SND_SOC_DAPM_POST_REG),
 
-       /* Analog input muxes with switch for the capture amplifiers */
-       SND_SOC_DAPM_VALUE_MUX("Analog Left Capture Route",
-               TWL4030_REG_ANAMICL, 4, 0, &twl4030_dapm_analoglmic_control),
-       SND_SOC_DAPM_VALUE_MUX("Analog Right Capture Route",
-               TWL4030_REG_ANAMICR, 4, 0, &twl4030_dapm_analogrmic_control),
+       /* Analog input mixers for the capture amplifiers */
+       SND_SOC_DAPM_MIXER("Analog Left Capture Route",
+               TWL4030_REG_ANAMICL, 4, 0,
+               &twl4030_dapm_analoglmic_controls[0],
+               ARRAY_SIZE(twl4030_dapm_analoglmic_controls)),
+       SND_SOC_DAPM_MIXER("Analog Right Capture Route",
+               TWL4030_REG_ANAMICR, 4, 0,
+               &twl4030_dapm_analogrmic_controls[0],
+               ARRAY_SIZE(twl4030_dapm_analogrmic_controls)),
 
        SND_SOC_DAPM_PGA("ADC Physical Left",
                TWL4030_REG_AVADC_CTL, 3, 0, NULL, 0),
@@ -1073,62 +1308,86 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
 };
 
 static const struct snd_soc_dapm_route intercon[] = {
-       {"Analog L1 Playback Mixer", NULL, "DAC Left1"},
-       {"Analog R1 Playback Mixer", NULL, "DAC Right1"},
-       {"Analog L2 Playback Mixer", NULL, "DAC Left2"},
-       {"Analog R2 Playback Mixer", NULL, "DAC Right2"},
-
-       {"ARXL1_APGA", NULL, "Analog L1 Playback Mixer"},
-       {"ARXR1_APGA", NULL, "Analog R1 Playback Mixer"},
-       {"ARXL2_APGA", NULL, "Analog L2 Playback Mixer"},
-       {"ARXR2_APGA", NULL, "Analog R2 Playback Mixer"},
+       {"Digital L1 Playback Mixer", NULL, "DAC Left1"},
+       {"Digital R1 Playback Mixer", NULL, "DAC Right1"},
+       {"Digital L2 Playback Mixer", NULL, "DAC Left2"},
+       {"Digital R2 Playback Mixer", NULL, "DAC Right2"},
+       {"Digital Voice Playback Mixer", NULL, "DAC Voice"},
+
+       {"Analog L1 Playback Mixer", NULL, "Digital L1 Playback Mixer"},
+       {"Analog R1 Playback Mixer", NULL, "Digital R1 Playback Mixer"},
+       {"Analog L2 Playback Mixer", NULL, "Digital L2 Playback Mixer"},
+       {"Analog R2 Playback Mixer", NULL, "Digital R2 Playback Mixer"},
+       {"Analog Voice Playback Mixer", NULL, "Digital Voice Playback Mixer"},
 
        /* Internal playback routings */
        /* Earpiece */
-       {"Earpiece Mux", "DACL1", "ARXL1_APGA"},
-       {"Earpiece Mux", "DACL2", "ARXL2_APGA"},
-       {"Earpiece Mux", "DACR1", "ARXR1_APGA"},
+       {"Earpiece Mixer", "Voice", "Analog Voice Playback Mixer"},
+       {"Earpiece Mixer", "AudioL1", "Analog L1 Playback Mixer"},
+       {"Earpiece Mixer", "AudioL2", "Analog L2 Playback Mixer"},
+       {"Earpiece Mixer", "AudioR1", "Analog R1 Playback Mixer"},
        /* PreDrivL */
-       {"PredriveL Mux", "DACL1", "ARXL1_APGA"},
-       {"PredriveL Mux", "DACL2", "ARXL2_APGA"},
-       {"PredriveL Mux", "DACR2", "ARXR2_APGA"},
+       {"PredriveL Mixer", "Voice", "Analog Voice Playback Mixer"},
+       {"PredriveL Mixer", "AudioL1", "Analog L1 Playback Mixer"},
+       {"PredriveL Mixer", "AudioL2", "Analog L2 Playback Mixer"},
+       {"PredriveL Mixer", "AudioR2", "Analog R2 Playback Mixer"},
        /* PreDrivR */
-       {"PredriveR Mux", "DACR1", "ARXR1_APGA"},
-       {"PredriveR Mux", "DACR2", "ARXR2_APGA"},
-       {"PredriveR Mux", "DACL2", "ARXL2_APGA"},
+       {"PredriveR Mixer", "Voice", "Analog Voice Playback Mixer"},
+       {"PredriveR Mixer", "AudioR1", "Analog R1 Playback Mixer"},
+       {"PredriveR Mixer", "AudioR2", "Analog R2 Playback Mixer"},
+       {"PredriveR Mixer", "AudioL2", "Analog L2 Playback Mixer"},
        /* HeadsetL */
-       {"HeadsetL Mux", "DACL1", "ARXL1_APGA"},
-       {"HeadsetL Mux", "DACL2", "ARXL2_APGA"},
+       {"HeadsetL Mixer", "Voice", "Analog Voice Playback Mixer"},
+       {"HeadsetL Mixer", "AudioL1", "Analog L1 Playback Mixer"},
+       {"HeadsetL Mixer", "AudioL2", "Analog L2 Playback Mixer"},
+       {"HeadsetL PGA", NULL, "HeadsetL Mixer"},
        /* HeadsetR */
-       {"HeadsetR Mux", "DACR1", "ARXR1_APGA"},
-       {"HeadsetR Mux", "DACR2", "ARXR2_APGA"},
+       {"HeadsetR Mixer", "Voice", "Analog Voice Playback Mixer"},
+       {"HeadsetR Mixer", "AudioR1", "Analog R1 Playback Mixer"},
+       {"HeadsetR Mixer", "AudioR2", "Analog R2 Playback Mixer"},
+       {"HeadsetR PGA", NULL, "HeadsetR Mixer"},
        /* CarkitL */
-       {"CarkitL Mux", "DACL1", "ARXL1_APGA"},
-       {"CarkitL Mux", "DACL2", "ARXL2_APGA"},
+       {"CarkitL Mixer", "Voice", "Analog Voice Playback Mixer"},
+       {"CarkitL Mixer", "AudioL1", "Analog L1 Playback Mixer"},
+       {"CarkitL Mixer", "AudioL2", "Analog L2 Playback Mixer"},
        /* CarkitR */
-       {"CarkitR Mux", "DACR1", "ARXR1_APGA"},
-       {"CarkitR Mux", "DACR2", "ARXR2_APGA"},
+       {"CarkitR Mixer", "Voice", "Analog Voice Playback Mixer"},
+       {"CarkitR Mixer", "AudioR1", "Analog R1 Playback Mixer"},
+       {"CarkitR Mixer", "AudioR2", "Analog R2 Playback Mixer"},
        /* HandsfreeL */
-       {"HandsfreeL Mux", "DACL1", "ARXL1_APGA"},
-       {"HandsfreeL Mux", "DACL2", "ARXL2_APGA"},
-       {"HandsfreeL Mux", "DACR2", "ARXR2_APGA"},
+       {"HandsfreeL Mux", "Voice", "Analog Voice Playback Mixer"},
+       {"HandsfreeL Mux", "AudioL1", "Analog L1 Playback Mixer"},
+       {"HandsfreeL Mux", "AudioL2", "Analog L2 Playback Mixer"},
+       {"HandsfreeL Mux", "AudioR2", "Analog R2 Playback Mixer"},
+       {"HandsfreeL Switch", "Switch", "HandsfreeL Mux"},
+       {"HandsfreeL PGA", NULL, "HandsfreeL Switch"},
        /* HandsfreeR */
-       {"HandsfreeR Mux", "DACR1", "ARXR1_APGA"},
-       {"HandsfreeR Mux", "DACR2", "ARXR2_APGA"},
-       {"HandsfreeR Mux", "DACL2", "ARXL2_APGA"},
+       {"HandsfreeR Mux", "Voice", "Analog Voice Playback Mixer"},
+       {"HandsfreeR Mux", "AudioR1", "Analog R1 Playback Mixer"},
+       {"HandsfreeR Mux", "AudioR2", "Analog R2 Playback Mixer"},
+       {"HandsfreeR Mux", "AudioL2", "Analog L2 Playback Mixer"},
+       {"HandsfreeR Switch", "Switch", "HandsfreeR Mux"},
+       {"HandsfreeR PGA", NULL, "HandsfreeR Switch"},
+       /* Vibra */
+       {"Vibra Mux", "AudioL1", "DAC Left1"},
+       {"Vibra Mux", "AudioR1", "DAC Right1"},
+       {"Vibra Mux", "AudioL2", "DAC Left2"},
+       {"Vibra Mux", "AudioR2", "DAC Right2"},
 
        /* outputs */
-       {"OUTL", NULL, "ARXL2_APGA"},
-       {"OUTR", NULL, "ARXR2_APGA"},
-       {"EARPIECE", NULL, "Earpiece Mux"},
-       {"PREDRIVEL", NULL, "PredriveL Mux"},
-       {"PREDRIVER", NULL, "PredriveR Mux"},
-       {"HSOL", NULL, "HeadsetL Mux"},
-       {"HSOR", NULL, "HeadsetR Mux"},
-       {"CARKITL", NULL, "CarkitL Mux"},
-       {"CARKITR", NULL, "CarkitR Mux"},
-       {"HFL", NULL, "HandsfreeL Mux"},
-       {"HFR", NULL, "HandsfreeR Mux"},
+       {"OUTL", NULL, "Analog L2 Playback Mixer"},
+       {"OUTR", NULL, "Analog R2 Playback Mixer"},
+       {"EARPIECE", NULL, "Earpiece Mixer"},
+       {"PREDRIVEL", NULL, "PredriveL Mixer"},
+       {"PREDRIVER", NULL, "PredriveR Mixer"},
+       {"HSOL", NULL, "HeadsetL PGA"},
+       {"HSOR", NULL, "HeadsetR PGA"},
+       {"CARKITL", NULL, "CarkitL Mixer"},
+       {"CARKITR", NULL, "CarkitR Mixer"},
+       {"HFL", NULL, "HandsfreeL PGA"},
+       {"HFR", NULL, "HandsfreeR PGA"},
+       {"Vibra Route", "Audio", "Vibra Mux"},
+       {"VIBRA", NULL, "Vibra Route"},
 
        /* Capture path */
        {"Analog Left Capture Route", "Main mic", "MAINMIC"},
@@ -1168,18 +1427,22 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"Left1 Analog Loopback", "Switch", "Analog Left Capture Route"},
        {"Right2 Analog Loopback", "Switch", "Analog Right Capture Route"},
        {"Left2 Analog Loopback", "Switch", "Analog Left Capture Route"},
+       {"Voice Analog Loopback", "Switch", "Analog Left Capture Route"},
 
        {"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"},
        {"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"},
        {"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"},
        {"Analog L2 Playback Mixer", NULL, "Left2 Analog Loopback"},
+       {"Analog Voice Playback Mixer", NULL, "Voice Analog Loopback"},
 
        /* Digital bypass routes */
        {"Right Digital Loopback", "Volume", "TX1 Capture Route"},
        {"Left Digital Loopback", "Volume", "TX1 Capture Route"},
+       {"Voice Digital Loopback", "Volume", "TX2 Capture Route"},
 
-       {"Analog R2 Playback Mixer", NULL, "Right Digital Loopback"},
-       {"Analog L2 Playback Mixer", NULL, "Left Digital Loopback"},
+       {"Digital R2 Playback Mixer", NULL, "Right Digital Loopback"},
+       {"Digital L2 Playback Mixer", NULL, "Left Digital Loopback"},
+       {"Digital Voice Playback Mixer", NULL, "Voice Digital Loopback"},
 
 };
 
@@ -1226,6 +1489,58 @@ static int twl4030_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
+static void twl4030_constraints(struct twl4030_priv *twl4030,
+                               struct snd_pcm_substream *mst_substream)
+{
+       struct snd_pcm_substream *slv_substream;
+
+       /* Pick the stream, which need to be constrained */
+       if (mst_substream == twl4030->master_substream)
+               slv_substream = twl4030->slave_substream;
+       else if (mst_substream == twl4030->slave_substream)
+               slv_substream = twl4030->master_substream;
+       else /* This should not happen.. */
+               return;
+
+       /* Set the constraints according to the already configured stream */
+       snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+                               SNDRV_PCM_HW_PARAM_RATE,
+                               twl4030->rate,
+                               twl4030->rate);
+
+       snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+                               SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+                               twl4030->sample_bits,
+                               twl4030->sample_bits);
+
+       snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+                               SNDRV_PCM_HW_PARAM_CHANNELS,
+                               twl4030->channels,
+                               twl4030->channels);
+}
+
+/* In case of 4 channel mode, the RX1 L/R for playback and the TX2 L/R for
+ * capture has to be enabled/disabled. */
+static void twl4030_tdm_enable(struct snd_soc_codec *codec, int direction,
+                               int enable)
+{
+       u8 reg, mask;
+
+       reg = twl4030_read_reg_cache(codec, TWL4030_REG_OPTION);
+
+       if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+               mask = TWL4030_ARXL1_VRX_EN | TWL4030_ARXR1_EN;
+       else
+               mask = TWL4030_ATXL2_VTXL_EN | TWL4030_ATXR2_VTXR_EN;
+
+       if (enable)
+               reg |= mask;
+       else
+               reg &= ~mask;
+
+       twl4030_write(codec, TWL4030_REG_OPTION, reg);
+}
+
 static int twl4030_startup(struct snd_pcm_substream *substream,
                           struct snd_soc_dai *dai)
 {
@@ -1234,26 +1549,25 @@ static int twl4030_startup(struct snd_pcm_substream *substream,
        struct snd_soc_codec *codec = socdev->card->codec;
        struct twl4030_priv *twl4030 = codec->private_data;
 
-       /* If we already have a playback or capture going then constrain
-        * this substream to match it.
-        */
        if (twl4030->master_substream) {
-               struct snd_pcm_runtime *master_runtime;
-               master_runtime = twl4030->master_substream->runtime;
-
-               snd_pcm_hw_constraint_minmax(substream->runtime,
-                                            SNDRV_PCM_HW_PARAM_RATE,
-                                            master_runtime->rate,
-                                            master_runtime->rate);
-
-               snd_pcm_hw_constraint_minmax(substream->runtime,
-                                            SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
-                                            master_runtime->sample_bits,
-                                            master_runtime->sample_bits);
-
                twl4030->slave_substream = substream;
-       } else
+               /* The DAI has one configuration for playback and capture, so
+                * if the DAI has been already configured then constrain this
+                * substream to match it. */
+               if (twl4030->configured)
+                       twl4030_constraints(twl4030, twl4030->master_substream);
+       } else {
+               if (!(twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) &
+                       TWL4030_OPTION_1)) {
+                       /* In option2 4 channel is not supported, set the
+                        * constraint for the first stream for channels, the
+                        * second stream will 'inherit' this cosntraint */
+                       snd_pcm_hw_constraint_minmax(substream->runtime,
+                                               SNDRV_PCM_HW_PARAM_CHANNELS,
+                                               2, 2);
+               }
                twl4030->master_substream = substream;
+       }
 
        return 0;
 }
@@ -1270,6 +1584,17 @@ static void twl4030_shutdown(struct snd_pcm_substream *substream,
                twl4030->master_substream = twl4030->slave_substream;
 
        twl4030->slave_substream = NULL;
+
+       /* If all streams are closed, or the remaining stream has not yet
+        * been configured than set the DAI as not configured. */
+       if (!twl4030->master_substream)
+               twl4030->configured = 0;
+        else if (!twl4030->master_substream->runtime->channels)
+               twl4030->configured = 0;
+
+        /* If the closing substream had 4 channel, do the necessary cleanup */
+       if (substream->runtime->channels == 4)
+               twl4030_tdm_enable(codec, substream->stream, 0);
 }
 
 static int twl4030_hw_params(struct snd_pcm_substream *substream,
@@ -1282,8 +1607,24 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
        struct twl4030_priv *twl4030 = codec->private_data;
        u8 mode, old_mode, format, old_format;
 
-       if (substream == twl4030->slave_substream)
-               /* Ignoring hw_params for slave substream */
+        /* If the substream has 4 channel, do the necessary setup */
+       if (params_channels(params) == 4) {
+               u8 format, mode;
+
+               format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
+               mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
+
+               /* Safety check: are we in the correct operating mode and
+                * the interface is in TDM mode? */
+               if ((mode & TWL4030_OPTION_1) &&
+                   ((format & TWL4030_AIF_FORMAT) == TWL4030_AIF_FORMAT_TDM))
+                       twl4030_tdm_enable(codec, substream->stream, 1);
+               else
+                       return -EINVAL;
+       }
+
+       if (twl4030->configured)
+               /* Ignoring hw_params for already configured DAI */
                return 0;
 
        /* bit rate */
@@ -1363,6 +1704,21 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
                /* set CODECPDZ afterwards */
                twl4030_codec_enable(codec, 1);
        }
+
+       /* Store the important parameters for the DAI configuration and set
+        * the DAI as configured */
+       twl4030->configured = 1;
+       twl4030->rate = params_rate(params);
+       twl4030->sample_bits = hw_param_interval(params,
+                                       SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min;
+       twl4030->channels = params_channels(params);
+
+       /* If both playback and capture streams are open, and one of them
+        * is setting the hw parameters right now (since we are here), set
+        * constraints to the other stream to match the current one. */
+       if (twl4030->slave_substream)
+               twl4030_constraints(twl4030, substream);
+
        return 0;
 }
 
@@ -1370,17 +1726,21 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai,
                int clk_id, unsigned int freq, int dir)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
+       struct twl4030_priv *twl4030 = codec->private_data;
        u8 infreq;
 
        switch (freq) {
        case 19200000:
                infreq = TWL4030_APLL_INFREQ_19200KHZ;
+               twl4030->sysclk = 19200;
                break;
        case 26000000:
                infreq = TWL4030_APLL_INFREQ_26000KHZ;
+               twl4030->sysclk = 26000;
                break;
        case 38400000:
                infreq = TWL4030_APLL_INFREQ_38400KHZ;
+               twl4030->sysclk = 38400;
                break;
        default:
                printk(KERN_ERR "TWL4030 set sysclk: unknown rate %d\n",
@@ -1424,6 +1784,9 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
        case SND_SOC_DAIFMT_I2S:
                format |= TWL4030_AIF_FORMAT_CODEC;
                break;
+       case SND_SOC_DAIFMT_DSP_A:
+               format |= TWL4030_AIF_FORMAT_TDM;
+               break;
        default:
                return -EINVAL;
        }
@@ -1443,6 +1806,180 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
        return 0;
 }
 
+/* In case of voice mode, the RX1 L(VRX) for downlink and the TX2 L/R
+ * (VTXL, VTXR) for uplink has to be enabled/disabled. */
+static void twl4030_voice_enable(struct snd_soc_codec *codec, int direction,
+                               int enable)
+{
+       u8 reg, mask;
+
+       reg = twl4030_read_reg_cache(codec, TWL4030_REG_OPTION);
+
+       if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+               mask = TWL4030_ARXL1_VRX_EN;
+       else
+               mask = TWL4030_ATXL2_VTXL_EN | TWL4030_ATXR2_VTXR_EN;
+
+       if (enable)
+               reg |= mask;
+       else
+               reg &= ~mask;
+
+       twl4030_write(codec, TWL4030_REG_OPTION, reg);
+}
+
+static int twl4030_voice_startup(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       u8 infreq;
+       u8 mode;
+
+       /* If the system master clock is not 26MHz, the voice PCM interface is
+        * not avilable.
+        */
+       infreq = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL)
+               & TWL4030_APLL_INFREQ;
+
+       if (infreq != TWL4030_APLL_INFREQ_26000KHZ) {
+               printk(KERN_ERR "TWL4030 voice startup: "
+                       "MCLK is not 26MHz, call set_sysclk() on init\n");
+               return -EINVAL;
+       }
+
+       /* If the codec mode is not option2, the voice PCM interface is not
+        * avilable.
+        */
+       mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE)
+               & TWL4030_OPT_MODE;
+
+       if (mode != TWL4030_OPTION_2) {
+               printk(KERN_ERR "TWL4030 voice startup: "
+                       "the codec mode is not option2\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void twl4030_voice_shutdown(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       /* Enable voice digital filters */
+       twl4030_voice_enable(codec, substream->stream, 0);
+}
+
+static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       u8 old_mode, mode;
+
+       /* Enable voice digital filters */
+       twl4030_voice_enable(codec, substream->stream, 1);
+
+       /* bit rate */
+       old_mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE)
+               & ~(TWL4030_CODECPDZ);
+       mode = old_mode;
+
+       switch (params_rate(params)) {
+       case 8000:
+               mode &= ~(TWL4030_SEL_16K);
+               break;
+       case 16000:
+               mode |= TWL4030_SEL_16K;
+               break;
+       default:
+               printk(KERN_ERR "TWL4030 voice hw params: unknown rate %d\n",
+                       params_rate(params));
+               return -EINVAL;
+       }
+
+       if (mode != old_mode) {
+               /* change rate and set CODECPDZ */
+               twl4030_codec_enable(codec, 0);
+               twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
+               twl4030_codec_enable(codec, 1);
+       }
+
+       return 0;
+}
+
+static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+               int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u8 infreq;
+
+       switch (freq) {
+       case 26000000:
+               infreq = TWL4030_APLL_INFREQ_26000KHZ;
+               break;
+       default:
+               printk(KERN_ERR "TWL4030 voice set sysclk: unknown rate %d\n",
+                       freq);
+               return -EINVAL;
+       }
+
+       infreq |= TWL4030_APLL_EN;
+       twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq);
+
+       return 0;
+}
+
+static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
+               unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u8 old_format, format;
+
+       /* get format */
+       old_format = twl4030_read_reg_cache(codec, TWL4030_REG_VOICE_IF);
+       format = old_format;
+
+       /* set master/slave audio interface */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFM:
+               format &= ~(TWL4030_VIF_SLAVE_EN);
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               format |= TWL4030_VIF_SLAVE_EN;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* clock inversion */
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_IB_NF:
+               format &= ~(TWL4030_VIF_FORMAT);
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               format |= TWL4030_VIF_FORMAT;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (format != old_format) {
+               /* change format and set CODECPDZ */
+               twl4030_codec_enable(codec, 0);
+               twl4030_write(codec, TWL4030_REG_VOICE_IF, format);
+               twl4030_codec_enable(codec, 1);
+       }
+
+       return 0;
+}
+
 #define TWL4030_RATES   (SNDRV_PCM_RATE_8000_48000)
 #define TWL4030_FORMATS         (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE)
 
@@ -1454,21 +1991,47 @@ static struct snd_soc_dai_ops twl4030_dai_ops = {
        .set_fmt        = twl4030_set_dai_fmt,
 };
 
-struct snd_soc_dai twl4030_dai = {
+static struct snd_soc_dai_ops twl4030_dai_voice_ops = {
+       .startup        = twl4030_voice_startup,
+       .shutdown       = twl4030_voice_shutdown,
+       .hw_params      = twl4030_voice_hw_params,
+       .set_sysclk     = twl4030_voice_set_dai_sysclk,
+       .set_fmt        = twl4030_voice_set_dai_fmt,
+};
+
+struct snd_soc_dai twl4030_dai[] = {
+{
        .name = "twl4030",
        .playback = {
-               .stream_name = "Playback",
+               .stream_name = "HiFi Playback",
                .channels_min = 2,
-               .channels_max = 2,
+               .channels_max = 4,
                .rates = TWL4030_RATES | SNDRV_PCM_RATE_96000,
                .formats = TWL4030_FORMATS,},
        .capture = {
                .stream_name = "Capture",
                .channels_min = 2,
-               .channels_max = 2,
+               .channels_max = 4,
                .rates = TWL4030_RATES,
                .formats = TWL4030_FORMATS,},
        .ops = &twl4030_dai_ops,
+},
+{
+       .name = "twl4030 Voice",
+       .playback = {
+               .stream_name = "Voice Playback",
+               .channels_min = 1,
+               .channels_max = 1,
+               .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+       .ops = &twl4030_dai_voice_ops,
+},
 };
 EXPORT_SYMBOL_GPL(twl4030_dai);
 
@@ -1500,6 +2063,8 @@ static int twl4030_resume(struct platform_device *pdev)
 static int twl4030_init(struct snd_soc_device *socdev)
 {
        struct snd_soc_codec *codec = socdev->card->codec;
+       struct twl4030_setup_data *setup = socdev->codec_data;
+       struct twl4030_priv *twl4030 = codec->private_data;
        int ret = 0;
 
        printk(KERN_INFO "TWL4030 Audio Codec init \n");
@@ -1509,14 +2074,31 @@ static int twl4030_init(struct snd_soc_device *socdev)
        codec->read = twl4030_read_reg_cache;
        codec->write = twl4030_write;
        codec->set_bias_level = twl4030_set_bias_level;
-       codec->dai = &twl4030_dai;
-       codec->num_dai = 1;
+       codec->dai = twl4030_dai;
+       codec->num_dai = ARRAY_SIZE(twl4030_dai),
        codec->reg_cache_size = sizeof(twl4030_reg);
        codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
                                        GFP_KERNEL);
        if (codec->reg_cache == NULL)
                return -ENOMEM;
 
+       /* Configuration for headset ramp delay from setup data */
+       if (setup) {
+               unsigned char hs_pop;
+
+               if (setup->sysclk)
+                       twl4030->sysclk = setup->sysclk;
+               else
+                       twl4030->sysclk = 26000;
+
+               hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
+               hs_pop &= ~TWL4030_RAMP_DELAY;
+               hs_pop |= (setup->ramp_delay_value << 2);
+               twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+       } else {
+               twl4030->sysclk = 26000;
+       }
+
        /* register pcms */
        ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
        if (ret < 0) {
@@ -1604,13 +2186,13 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
 
 static int __init twl4030_modinit(void)
 {
-       return snd_soc_register_dai(&twl4030_dai);
+       return snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
 }
 module_init(twl4030_modinit);
 
 static void __exit twl4030_exit(void)
 {
-       snd_soc_unregister_dai(&twl4030_dai);
+       snd_soc_unregister_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
 }
 module_exit(twl4030_exit);
 
index cb63765db1df13a898b6c4380d7afdbf3c50af79..fe5f395d9e4fe8bdcce9b4ca29c82aceecb57a7c 100644 (file)
@@ -92,8 +92,9 @@
 #define TWL4030_REG_VIBRA_PWM_SET      0x47
 #define TWL4030_REG_ANAMIC_GAIN                0x48
 #define TWL4030_REG_MISC_SET_2         0x49
+#define TWL4030_REG_SW_SHADOW          0x4A
 
-#define TWL4030_CACHEREGNUM    (TWL4030_REG_MISC_SET_2 + 1)
+#define TWL4030_CACHEREGNUM    (TWL4030_REG_SW_SHADOW + 1)
 
 /* Bitfield Definitions */
 
 #define TWL4030_APLL_RATE_44100                0x90
 #define TWL4030_APLL_RATE_48000                0xA0
 #define TWL4030_APLL_RATE_96000                0xE0
-#define TWL4030_SEL_16K                        0x04
+#define TWL4030_SEL_16K                        0x08
 #define TWL4030_CODECPDZ               0x02
 #define TWL4030_OPT_MODE               0x01
+#define TWL4030_OPTION_1               (1 << 0)
+#define TWL4030_OPTION_2               (0 << 0)
+
+/* TWL4030_OPTION (0x02) Fields */
+
+#define TWL4030_ATXL1_EN               (1 << 0)
+#define TWL4030_ATXR1_EN               (1 << 1)
+#define TWL4030_ATXL2_VTXL_EN          (1 << 2)
+#define TWL4030_ATXR2_VTXR_EN          (1 << 3)
+#define TWL4030_ARXL1_VRX_EN           (1 << 4)
+#define TWL4030_ARXR1_EN               (1 << 5)
+#define TWL4030_ARXL2_EN               (1 << 6)
+#define TWL4030_ARXR2_EN               (1 << 7)
 
 /* TWL4030_REG_MICBIAS_CTL (0x04) Fields */
 
 #define TWL4030_CLK256FS_EN            0x02
 #define TWL4030_AIF_EN                 0x01
 
+/* VOICE_IF (0x0F) Fields */
+
+#define TWL4030_VIF_SLAVE_EN           0x80
+#define TWL4030_VIF_DIN_EN             0x40
+#define TWL4030_VIF_DOUT_EN            0x20
+#define TWL4030_VIF_SWAP               0x10
+#define TWL4030_VIF_FORMAT             0x08
+#define TWL4030_VIF_TRI_EN             0x04
+#define TWL4030_VIF_SUB_EN             0x02
+#define TWL4030_VIF_EN                 0x01
+
 /* EAR_CTL (0x21) */
 #define TWL4030_EAR_GAIN               0x30
 
 #define TWL4030_SMOOTH_ANAVOL_EN       0x02
 #define TWL4030_DIGMIC_LR_SWAP_EN      0x01
 
-extern struct snd_soc_dai twl4030_dai;
+/* TWL4030_REG_SW_SHADOW (0x4A) Fields */
+#define TWL4030_HFL_EN                 0x01
+#define TWL4030_HFR_EN                 0x02
+
+#define TWL4030_DAI_HIFI               0
+#define TWL4030_DAI_VOICE              1
+
+extern struct snd_soc_dai twl4030_dai[2];
 extern struct snd_soc_codec_device soc_codec_dev_twl4030;
 
+struct twl4030_setup_data {
+       unsigned int ramp_delay_value;
+       unsigned int sysclk;
+};
+
 #endif /* End of __TWL4030_AUDIO_H__ */
index ddefb8f80145bb1830253cde7277f3bf6ae50818..269b108e1de612f6aaff359b09b59be3f904abaa 100644 (file)
@@ -101,7 +101,7 @@ static int uda134x_write(struct snd_soc_codec *codec, unsigned int reg,
        pr_debug("%s reg: %02X, value:%02X\n", __func__, reg, value);
 
        if (reg >= UDA134X_REGS_NUM) {
-               printk(KERN_ERR "%s unkown register: reg: %d",
+               printk(KERN_ERR "%s unkown register: reg: %u",
                       __func__, reg);
                return -EINVAL;
        }
@@ -296,7 +296,7 @@ static int uda134x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
        struct snd_soc_codec *codec = codec_dai->codec;
        struct uda134x_priv *uda134x = codec->private_data;
 
-       pr_debug("%s clk_id: %d, freq: %d, dir: %d\n", __func__,
+       pr_debug("%s clk_id: %d, freq: %u, dir: %d\n", __func__,
                 clk_id, freq, dir);
 
        /* Anything between 256fs*8Khz and 512fs*48Khz should be acceptable
index 0275321ff8ab29840c994bb107c3b54d8529f1aa..e7348d341b761d5870889a97f814f8f5a927b0cd 100644 (file)
@@ -1108,7 +1108,7 @@ static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
        if (ret < 0)
                return ret;
        dev_dbg(wm8350->dev,
-               "FLL in %d FLL out %d N 0x%x K 0x%x div %d ratio %d",
+               "FLL in %u FLL out %u N 0x%x K 0x%x div %d ratio %d",
                freq_in, freq_out, fll_div.n, fll_div.k, fll_div.div,
                fll_div.ratio);
 
index d11bd9288cf9170761e735e2ff4094c52353a437..d088eb4b88bb85ca2ee2d260dc46003ea9e5241e 100644 (file)
@@ -13,6 +13,7 @@
 #define _WM8350_H
 
 #include <sound/soc.h>
+#include <linux/mfd/wm8350/audio.h>
 
 extern struct snd_soc_dai wm8350_dai;
 extern struct snd_soc_codec_device soc_codec_dev_wm8350;
index 510efa6040088a03cc30f39ac8bfd015f8ba74f9..502eefac1ecd797dd02ff8382e31e19f8d8b972b 100644 (file)
@@ -954,7 +954,7 @@ static int fll_factors(struct wm8400_priv *wm8400, struct fll_factors *factors,
                factors->outdiv *= 2;
                if (factors->outdiv > 32) {
                        dev_err(wm8400->wm8400->dev,
-                               "Unsupported FLL output frequency %dHz\n",
+                               "Unsupported FLL output frequency %uHz\n",
                                Fout);
                        return -EINVAL;
                }
@@ -1003,7 +1003,7 @@ static int fll_factors(struct wm8400_priv *wm8400, struct fll_factors *factors,
        factors->k = K / 10;
 
        dev_dbg(wm8400->wm8400->dev,
-               "FLL: Fref=%d Fout=%d N=%x K=%x, FRATIO=%x OUTDIV=%x\n",
+               "FLL: Fref=%u Fout=%u N=%x K=%x, FRATIO=%x OUTDIV=%x\n",
                Fref, Fout,
                factors->n, factors->k, factors->fratio, factors->outdiv);
 
@@ -1473,8 +1473,8 @@ static int wm8400_codec_probe(struct platform_device *dev)
 
        codec = &priv->codec;
        codec->private_data = priv;
-       codec->control_data = dev->dev.driver_data;
-       priv->wm8400 = dev->dev.driver_data;
+       codec->control_data = dev_get_drvdata(&dev->dev);
+       priv->wm8400 = dev_get_drvdata(&dev->dev);
 
        ret = regulator_bulk_get(priv->wm8400->dev,
                                 ARRAY_SIZE(power), &power[0]);
index 6a4cea09c45dbe624ba2941442c68e3fdb5971c2..c8b8dba858907d0ec7d9bc3f64b2a19cc6b876fc 100644 (file)
@@ -298,7 +298,7 @@ static void pll_factors(unsigned int target, unsigned int source)
 
        if ((Ndiv < 6) || (Ndiv > 12))
                printk(KERN_WARNING
-                       "WM8510 N value %d outwith recommended range!d\n",
+                       "WM8510 N value %u outwith recommended range!d\n",
                        Ndiv);
 
        pll_div.n = Ndiv;
index 9f6be3d31ac0a8268473463c39ac6c3066b01b75..86c4b24db8172aa610d283120476440587035c3d 100644 (file)
@@ -415,7 +415,7 @@ static int pll_factors(struct _pll_div *pll_div, unsigned int target,
        unsigned int K, Ndiv, Nmod;
        int i;
 
-       pr_debug("wm8580: PLL %dHz->%dHz\n", source, target);
+       pr_debug("wm8580: PLL %uHz->%uHz\n", source, target);
 
        /* Scale the output frequency up; the PLL should run in the
         * region of 90-100MHz.
@@ -447,7 +447,7 @@ static int pll_factors(struct _pll_div *pll_div, unsigned int target,
 
        if ((Ndiv < 5) || (Ndiv > 13)) {
                printk(KERN_ERR
-                       "WM8580 N=%d outside supported range\n", Ndiv);
+                       "WM8580 N=%u outside supported range\n", Ndiv);
                return -EINVAL;
        }
 
index e043e3f60008b46579e4ae97e411a0df1790e15b..7a205876ef4f0dc4e0982855694c62f2ea5e4def 100644 (file)
@@ -666,14 +666,14 @@ static int __devinit wm8731_spi_probe(struct spi_device *spi)
        codec->hw_write = (hw_write_t)wm8731_spi_write;
        codec->dev = &spi->dev;
 
-       spi->dev.driver_data = wm8731;
+       dev_set_drvdata(&spi->dev, wm8731);
 
        return wm8731_register(wm8731);
 }
 
 static int __devexit wm8731_spi_remove(struct spi_device *spi)
 {
-       struct wm8731_priv *wm8731 = spi->dev.driver_data;
+       struct wm8731_priv *wm8731 = dev_get_drvdata(&spi->dev);
 
        wm8731_unregister(wm8731);
 
index a6e8f3f7f052ee4dc45b8ef4540f4cf31c9f411e..d28eeaceb8573a504f3c7c5471f9f2f296c5618c 100644 (file)
@@ -703,7 +703,7 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int target,
 
        if ((Ndiv < 6) || (Ndiv > 12))
                printk(KERN_WARNING
-                       "wm8753: unsupported N = %d\n", Ndiv);
+                       "wm8753: unsupported N = %u\n", Ndiv);
 
        pll_div->n = Ndiv;
        Nmod = target % source;
@@ -1822,14 +1822,14 @@ static int __devinit wm8753_spi_probe(struct spi_device *spi)
        codec->hw_write = (hw_write_t)wm8753_spi_write;
        codec->dev = &spi->dev;
 
-       spi->dev.driver_data = wm8753;
+       dev_set_drvdata(&spi->dev, wm8753);
 
        return wm8753_register(wm8753);
 }
 
 static int __devexit wm8753_spi_remove(struct spi_device *spi)
 {
-       struct wm8753_priv *wm8753 = spi->dev.driver_data;
+       struct wm8753_priv *wm8753 = dev_get_drvdata(&spi->dev);
        wm8753_unregister(wm8753);
        return 0;
 }
index 46c5ea1ff921aea8c3f6be8fd13ed7bd8de1f897..3c78945244b8df516f12f0181f0194a4b1a1b949 100644 (file)
@@ -778,11 +778,11 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
        }
 
        if (target > 100000000)
-               printk(KERN_WARNING "wm8900: FLL rate %d out of range, Fref=%d"
-                      " Fout=%d\n", target, Fref, Fout);
+               printk(KERN_WARNING "wm8900: FLL rate %u out of range, Fref=%u"
+                      " Fout=%u\n", target, Fref, Fout);
        if (div > 32) {
                printk(KERN_ERR "wm8900: Invalid FLL division rate %u, "
-                      "Fref=%d, Fout=%d, target=%d\n",
+                      "Fref=%u, Fout=%u, target=%u\n",
                       div, Fref, Fout, target);
                return -EINVAL;
        }
index 8cf571f1a803d3ae7b1b1e561aedb01470cc19dd..d8a9222fbf745c53077bd3a5f12e89c3f70bded0 100644 (file)
@@ -217,7 +217,6 @@ struct wm8903_priv {
        int sysclk;
 
        /* Reference counts */
-       int charge_pump_users;
        int class_w_users;
        int playback_active;
        int capture_active;
@@ -373,6 +372,15 @@ static void wm8903_reset(struct snd_soc_codec *codec)
 #define WM8903_OUTPUT_INT   0x2
 #define WM8903_OUTPUT_IN    0x1
 
+static int wm8903_cp_event(struct snd_soc_dapm_widget *w,
+                          struct snd_kcontrol *kcontrol, int event)
+{
+       WARN_ON(event != SND_SOC_DAPM_POST_PMU);
+       mdelay(4);
+
+       return 0;
+}
+
 /*
  * Event for headphone and line out amplifier power changes.  Special
  * power up/down sequences are required in order to maximise pop/click
@@ -382,19 +390,20 @@ static int wm8903_output_event(struct snd_soc_dapm_widget *w,
                               struct snd_kcontrol *kcontrol, int event)
 {
        struct snd_soc_codec *codec = w->codec;
-       struct wm8903_priv *wm8903 = codec->private_data;
-       struct i2c_client *i2c = codec->control_data;
        u16 val;
        u16 reg;
+       u16 dcs_reg;
+       u16 dcs_bit;
        int shift;
-       u16 cp_reg = wm8903_read(codec, WM8903_CHARGE_PUMP_0);
 
        switch (w->reg) {
        case WM8903_POWER_MANAGEMENT_2:
                reg = WM8903_ANALOGUE_HP_0;
+               dcs_bit = 0 + w->shift;
                break;
        case WM8903_POWER_MANAGEMENT_3:
                reg = WM8903_ANALOGUE_LINEOUT_0;
+               dcs_bit = 2 + w->shift;
                break;
        default:
                BUG();
@@ -419,18 +428,6 @@ static int wm8903_output_event(struct snd_soc_dapm_widget *w,
                /* Short the output */
                val &= ~(WM8903_OUTPUT_SHORT << shift);
                wm8903_write(codec, reg, val);
-
-               wm8903->charge_pump_users++;
-
-               dev_dbg(&i2c->dev, "Charge pump use count now %d\n",
-                       wm8903->charge_pump_users);
-
-               if (wm8903->charge_pump_users == 1) {
-                       dev_dbg(&i2c->dev, "Enabling charge pump\n");
-                       wm8903_write(codec, WM8903_CHARGE_PUMP_0,
-                                    cp_reg | WM8903_CP_ENA);
-                       mdelay(4);
-               }
        }
 
        if (event & SND_SOC_DAPM_POST_PMU) {
@@ -446,6 +443,11 @@ static int wm8903_output_event(struct snd_soc_dapm_widget *w,
                val |= (WM8903_OUTPUT_OUT << shift);
                wm8903_write(codec, reg, val);
 
+               /* Enable the DC servo */
+               dcs_reg = wm8903_read(codec, WM8903_DC_SERVO_0);
+               dcs_reg |= dcs_bit;
+               wm8903_write(codec, WM8903_DC_SERVO_0, dcs_reg);
+
                /* Remove the short */
                val |= (WM8903_OUTPUT_SHORT << shift);
                wm8903_write(codec, reg, val);
@@ -458,25 +460,17 @@ static int wm8903_output_event(struct snd_soc_dapm_widget *w,
                val &= ~(WM8903_OUTPUT_SHORT << shift);
                wm8903_write(codec, reg, val);
 
+               /* Disable the DC servo */
+               dcs_reg = wm8903_read(codec, WM8903_DC_SERVO_0);
+               dcs_reg &= ~dcs_bit;
+               wm8903_write(codec, WM8903_DC_SERVO_0, dcs_reg);
+
                /* Then disable the intermediate and output stages */
                val &= ~((WM8903_OUTPUT_OUT | WM8903_OUTPUT_INT |
                          WM8903_OUTPUT_IN) << shift);
                wm8903_write(codec, reg, val);
        }
 
-       if (event & SND_SOC_DAPM_POST_PMD) {
-               wm8903->charge_pump_users--;
-
-               dev_dbg(&i2c->dev, "Charge pump use count now %d\n",
-                       wm8903->charge_pump_users);
-
-               if (wm8903->charge_pump_users == 0) {
-                       dev_dbg(&i2c->dev, "Disabling charge pump\n");
-                       wm8903_write(codec, WM8903_CHARGE_PUMP_0,
-                                    cp_reg & ~WM8903_CP_ENA);
-               }
-       }
-
        return 0;
 }
 
@@ -539,6 +533,7 @@ static int wm8903_class_w_put(struct snd_kcontrol *kcontrol,
 /* ALSA can only do steps of .01dB */
 static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
 
+static const DECLARE_TLV_DB_SCALE(digital_sidetone_tlv, -3600, 300, 0);
 static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0);
 
 static const DECLARE_TLV_DB_SCALE(drc_tlv_thresh, 0, 75, 0);
@@ -657,6 +652,16 @@ static const struct soc_enum rinput_inv_enum =
        SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 4, 3, rinput_mux_text);
 
 
+static const char *sidetone_text[] = {
+       "None", "Left", "Right"
+};
+
+static const struct soc_enum lsidetone_enum =
+       SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 2, 3, sidetone_text);
+
+static const struct soc_enum rsidetone_enum =
+       SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 0, 3, sidetone_text);
+
 static const struct snd_kcontrol_new wm8903_snd_controls[] = {
 
 /* Input PGAs - No TLV since the scale depends on PGA mode */
@@ -700,6 +705,9 @@ SOC_DOUBLE_R_TLV("Digital Capture Volume", WM8903_ADC_DIGITAL_VOLUME_LEFT,
 SOC_ENUM("ADC Companding Mode", adc_companding),
 SOC_SINGLE("ADC Companding Switch", WM8903_AUDIO_INTERFACE_0, 3, 1, 0),
 
+SOC_DOUBLE_TLV("Digital Sidetone Volume", WM8903_DAC_DIGITAL_0, 4, 8,
+              12, 0, digital_sidetone_tlv),
+
 /* DAC */
 SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8903_DAC_DIGITAL_VOLUME_LEFT,
                 WM8903_DAC_DIGITAL_VOLUME_RIGHT, 1, 120, 0, digital_tlv),
@@ -762,6 +770,12 @@ static const struct snd_kcontrol_new rinput_mux =
 static const struct snd_kcontrol_new rinput_inv_mux =
        SOC_DAPM_ENUM("Right Inverting Input Mux", rinput_inv_enum);
 
+static const struct snd_kcontrol_new lsidetone_mux =
+       SOC_DAPM_ENUM("DACL Sidetone Mux", lsidetone_enum);
+
+static const struct snd_kcontrol_new rsidetone_mux =
+       SOC_DAPM_ENUM("DACR Sidetone Mux", rsidetone_enum);
+
 static const struct snd_kcontrol_new left_output_mixer[] = {
 SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_LEFT_MIX_0, 3, 1, 0),
 SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_LEFT_MIX_0, 2, 1, 0),
@@ -828,6 +842,9 @@ SND_SOC_DAPM_PGA("Right Input PGA", WM8903_POWER_MANAGEMENT_0, 0, 0, NULL, 0),
 SND_SOC_DAPM_ADC("ADCL", "Left HiFi Capture", WM8903_POWER_MANAGEMENT_6, 1, 0),
 SND_SOC_DAPM_ADC("ADCR", "Right HiFi Capture", WM8903_POWER_MANAGEMENT_6, 0, 0),
 
+SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &lsidetone_mux),
+SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &rsidetone_mux),
+
 SND_SOC_DAPM_DAC("DACL", "Left Playback", WM8903_POWER_MANAGEMENT_6, 3, 0),
 SND_SOC_DAPM_DAC("DACR", "Right Playback", WM8903_POWER_MANAGEMENT_6, 2, 0),
 
@@ -844,26 +861,29 @@ SND_SOC_DAPM_MIXER("Right Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 0, 0,
 SND_SOC_DAPM_PGA_E("Left Headphone Output PGA", WM8903_POWER_MANAGEMENT_2,
                   1, 0, NULL, 0, wm8903_output_event,
                   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-                  SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+                  SND_SOC_DAPM_PRE_PMD),
 SND_SOC_DAPM_PGA_E("Right Headphone Output PGA", WM8903_POWER_MANAGEMENT_2,
                   0, 0, NULL, 0, wm8903_output_event,
                   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-                  SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+                  SND_SOC_DAPM_PRE_PMD),
 
 SND_SOC_DAPM_PGA_E("Left Line Output PGA", WM8903_POWER_MANAGEMENT_3, 1, 0,
                   NULL, 0, wm8903_output_event,
                   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-                  SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+                  SND_SOC_DAPM_PRE_PMD),
 SND_SOC_DAPM_PGA_E("Right Line Output PGA", WM8903_POWER_MANAGEMENT_3, 0, 0,
                   NULL, 0, wm8903_output_event,
                   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-                  SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+                  SND_SOC_DAPM_PRE_PMD),
 
 SND_SOC_DAPM_PGA("Left Speaker PGA", WM8903_POWER_MANAGEMENT_5, 1, 0,
                 NULL, 0),
 SND_SOC_DAPM_PGA("Right Speaker PGA", WM8903_POWER_MANAGEMENT_5, 0, 0,
                 NULL, 0),
 
+SND_SOC_DAPM_SUPPLY("Charge Pump", WM8903_CHARGE_PUMP_0, 0, 0,
+                   wm8903_cp_event, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8903_CLOCK_RATES_2, 1, 0, NULL, 0),
 };
 
 static const struct snd_soc_dapm_route intercon[] = {
@@ -909,7 +929,19 @@ static const struct snd_soc_dapm_route intercon[] = {
        { "Right Input PGA", NULL, "Right Input Mode Mux" },
 
        { "ADCL", NULL, "Left Input PGA" },
+       { "ADCL", NULL, "CLK_DSP" },
        { "ADCR", NULL, "Right Input PGA" },
+       { "ADCR", NULL, "CLK_DSP" },
+
+       { "DACL Sidetone", "Left", "ADCL" },
+       { "DACL Sidetone", "Right", "ADCR" },
+       { "DACR Sidetone", "Left", "ADCL" },
+       { "DACR Sidetone", "Right", "ADCR" },
+
+       { "DACL", NULL, "DACL Sidetone" },
+       { "DACL", NULL, "CLK_DSP" },
+       { "DACR", NULL, "DACR Sidetone" },
+       { "DACR", NULL, "CLK_DSP" },
 
        { "Left Output Mixer", "Left Bypass Switch", "Left Input PGA" },
        { "Left Output Mixer", "Right Bypass Switch", "Right Input PGA" },
@@ -951,6 +983,11 @@ static const struct snd_soc_dapm_route intercon[] = {
 
        { "ROP", NULL, "Right Speaker PGA" },
        { "RON", NULL, "Right Speaker PGA" },
+
+       { "Left Headphone Output PGA", NULL, "Charge Pump" },
+       { "Right Headphone Output PGA", NULL, "Charge Pump" },
+       { "Left Line Output PGA", NULL, "Charge Pump" },
+       { "Right Line Output PGA", NULL, "Charge Pump" },
 };
 
 static int wm8903_add_widgets(struct snd_soc_codec *codec)
@@ -985,6 +1022,11 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec,
                        wm8903_write(codec, WM8903_CLOCK_RATES_2,
                                     WM8903_CLK_SYS_ENA);
 
+                       /* Change DC servo dither level in startup sequence */
+                       wm8903_write(codec, WM8903_WRITE_SEQUENCER_0, 0x11);
+                       wm8903_write(codec, WM8903_WRITE_SEQUENCER_1, 0x1257);
+                       wm8903_write(codec, WM8903_WRITE_SEQUENCER_2, 0x2);
+
                        wm8903_run_sequence(codec, 0);
                        wm8903_sync_reg_cache(codec, codec->reg_cache);
 
@@ -1277,14 +1319,8 @@ static int wm8903_startup(struct snd_pcm_substream *substream,
        if (wm8903->master_substream) {
                master_runtime = wm8903->master_substream->runtime;
 
-               dev_dbg(&i2c->dev, "Constraining to %d bits at %dHz\n",
-                       master_runtime->sample_bits,
-                       master_runtime->rate);
-
-               snd_pcm_hw_constraint_minmax(substream->runtime,
-                                            SNDRV_PCM_HW_PARAM_RATE,
-                                            master_runtime->rate,
-                                            master_runtime->rate);
+               dev_dbg(&i2c->dev, "Constraining to %d bits\n",
+                       master_runtime->sample_bits);
 
                snd_pcm_hw_constraint_minmax(substream->runtime,
                                             SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
@@ -1523,6 +1559,7 @@ struct snd_soc_dai wm8903_dai = {
                 .formats = WM8903_FORMATS,
         },
        .ops = &wm8903_dai_ops,
+       .symmetric_rates = 1,
 };
 EXPORT_SYMBOL_GPL(wm8903_dai);
 
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
new file mode 100644 (file)
index 0000000..b8e17d6
--- /dev/null
@@ -0,0 +1,955 @@
+/*
+ * wm8940.c  --  WM8940 ALSA Soc Audio driver
+ *
+ * Author: Jonathan Cameron <jic23@cam.ac.uk>
+ *
+ * Based on wm8510.c
+ *    Copyright  2006 Wolfson Microelectronics PLC.
+ *    Author:  Liam Girdwood <lrg@slimlogic.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Not currently handled:
+ * Notch filter control
+ * AUXMode (inverting vs mixer)
+ * No means to obtain current gain if alc enabled.
+ * No use made of gpio
+ * Fast VMID discharge for power down
+ * Soft Start
+ * DLR and ALR Swaps not enabled
+ * Digital Sidetone not supported
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8940.h"
+
+struct wm8940_priv {
+       unsigned int sysclk;
+       u16 reg_cache[WM8940_CACHEREGNUM];
+       struct snd_soc_codec codec;
+};
+
+static u16 wm8940_reg_defaults[] = {
+       0x8940, /* Soft Reset */
+       0x0000, /* Power 1 */
+       0x0000, /* Power 2 */
+       0x0000, /* Power 3 */
+       0x0010, /* Interface Control */
+       0x0000, /* Companding Control */
+       0x0140, /* Clock Control */
+       0x0000, /* Additional Controls */
+       0x0000, /* GPIO Control */
+       0x0002, /* Auto Increment Control */
+       0x0000, /* DAC Control */
+       0x00FF, /* DAC Volume */
+       0,
+       0,
+       0x0100, /* ADC Control */
+       0x00FF, /* ADC Volume */
+       0x0000, /* Notch Filter 1 Control 1 */
+       0x0000, /* Notch Filter 1 Control 2 */
+       0x0000, /* Notch Filter 2 Control 1 */
+       0x0000, /* Notch Filter 2 Control 2 */
+       0x0000, /* Notch Filter 3 Control 1 */
+       0x0000, /* Notch Filter 3 Control 2 */
+       0x0000, /* Notch Filter 4 Control 1 */
+       0x0000, /* Notch Filter 4 Control 2 */
+       0x0032, /* DAC Limit Control 1 */
+       0x0000, /* DAC Limit Control 2 */
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0x0038, /* ALC Control 1 */
+       0x000B, /* ALC Control 2 */
+       0x0032, /* ALC Control 3 */
+       0x0000, /* Noise Gate */
+       0x0041, /* PLLN */
+       0x000C, /* PLLK1 */
+       0x0093, /* PLLK2 */
+       0x00E9, /* PLLK3 */
+       0,
+       0,
+       0x0030, /* ALC Control 4 */
+       0,
+       0x0002, /* Input Control */
+       0x0050, /* PGA Gain */
+       0,
+       0x0002, /* ADC Boost Control */
+       0,
+       0x0002, /* Output Control */
+       0x0000, /* Speaker Mixer Control */
+       0,
+       0,
+       0,
+       0x0079, /* Speaker Volume */
+       0,
+       0x0000, /* Mono Mixer Control */
+};
+
+static inline unsigned int wm8940_read_reg_cache(struct snd_soc_codec *codec,
+                                                unsigned int reg)
+{
+       u16 *cache = codec->reg_cache;
+
+       if (reg >= ARRAY_SIZE(wm8940_reg_defaults))
+               return -1;
+
+       return cache[reg];
+}
+
+static inline int wm8940_write_reg_cache(struct snd_soc_codec *codec,
+                                         u16 reg, unsigned int value)
+{
+       u16 *cache = codec->reg_cache;
+
+       if (reg >= ARRAY_SIZE(wm8940_reg_defaults))
+               return -1;
+
+       cache[reg] = value;
+
+       return 0;
+}
+
+static int wm8940_write(struct snd_soc_codec *codec, unsigned int reg,
+                       unsigned int value)
+{
+       int ret;
+       u8 data[3] = { reg,
+                      (value & 0xff00) >> 8,
+                      (value & 0x00ff)
+       };
+
+       wm8940_write_reg_cache(codec, reg, value);
+
+       ret = codec->hw_write(codec->control_data, data, 3);
+
+       if (ret < 0)
+               return ret;
+       else if (ret != 3)
+               return -EIO;
+       return 0;
+}
+
+static const char *wm8940_companding[] = { "Off", "NC", "u-law", "A-law" };
+static const struct soc_enum wm8940_adc_companding_enum
+= SOC_ENUM_SINGLE(WM8940_COMPANDINGCTL, 1, 4, wm8940_companding);
+static const struct soc_enum wm8940_dac_companding_enum
+= SOC_ENUM_SINGLE(WM8940_COMPANDINGCTL, 3, 4, wm8940_companding);
+
+static const char *wm8940_alc_mode_text[] = {"ALC", "Limiter"};
+static const struct soc_enum wm8940_alc_mode_enum
+= SOC_ENUM_SINGLE(WM8940_ALC3, 8, 2, wm8940_alc_mode_text);
+
+static const char *wm8940_mic_bias_level_text[] = {"0.9", "0.65"};
+static const struct soc_enum wm8940_mic_bias_level_enum
+= SOC_ENUM_SINGLE(WM8940_INPUTCTL, 8, 2, wm8940_mic_bias_level_text);
+
+static const char *wm8940_filter_mode_text[] = {"Audio", "Application"};
+static const struct soc_enum wm8940_filter_mode_enum
+= SOC_ENUM_SINGLE(WM8940_ADC, 7, 2, wm8940_filter_mode_text);
+
+static DECLARE_TLV_DB_SCALE(wm8940_spk_vol_tlv, -5700, 100, 1);
+static DECLARE_TLV_DB_SCALE(wm8940_att_tlv, -1000, 1000, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_pga_vol_tlv, -1200, 75, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_alc_min_tlv, -1200, 600, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_alc_max_tlv, 675, 600, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_alc_tar_tlv, -2250, 50, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_lim_boost_tlv, 0, 100, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_lim_thresh_tlv, -600, 100, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_adc_tlv, -12750, 50, 1);
+static DECLARE_TLV_DB_SCALE(wm8940_capture_boost_vol_tlv, 0, 2000, 0);
+
+static const struct snd_kcontrol_new wm8940_snd_controls[] = {
+       SOC_SINGLE("Digital Loopback Switch", WM8940_COMPANDINGCTL,
+                  6, 1, 0),
+       SOC_ENUM("DAC Companding", wm8940_dac_companding_enum),
+       SOC_ENUM("ADC Companding", wm8940_adc_companding_enum),
+
+       SOC_ENUM("ALC Mode", wm8940_alc_mode_enum),
+       SOC_SINGLE("ALC Switch", WM8940_ALC1, 8, 1, 0),
+       SOC_SINGLE_TLV("ALC Capture Max Gain", WM8940_ALC1,
+                      3, 7, 1, wm8940_alc_max_tlv),
+       SOC_SINGLE_TLV("ALC Capture Min Gain", WM8940_ALC1,
+                      0, 7, 0, wm8940_alc_min_tlv),
+       SOC_SINGLE_TLV("ALC Capture Target", WM8940_ALC2,
+                      0, 14, 0, wm8940_alc_tar_tlv),
+       SOC_SINGLE("ALC Capture Hold", WM8940_ALC2, 4, 10, 0),
+       SOC_SINGLE("ALC Capture Decay", WM8940_ALC3, 4, 10, 0),
+       SOC_SINGLE("ALC Capture Attach", WM8940_ALC3, 0, 10, 0),
+       SOC_SINGLE("ALC ZC Switch", WM8940_ALC4, 1, 1, 0),
+       SOC_SINGLE("ALC Capture Noise Gate Switch", WM8940_NOISEGATE,
+                  3, 1, 0),
+       SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8940_NOISEGATE,
+                  0, 7, 0),
+
+       SOC_SINGLE("DAC Playback Limiter Switch", WM8940_DACLIM1, 8, 1, 0),
+       SOC_SINGLE("DAC Playback Limiter Attack", WM8940_DACLIM1, 0, 9, 0),
+       SOC_SINGLE("DAC Playback Limiter Decay", WM8940_DACLIM1, 4, 11, 0),
+       SOC_SINGLE_TLV("DAC Playback Limiter Threshold", WM8940_DACLIM2,
+                      4, 9, 1, wm8940_lim_thresh_tlv),
+       SOC_SINGLE_TLV("DAC Playback Limiter Boost", WM8940_DACLIM2,
+                      0, 12, 0, wm8940_lim_boost_tlv),
+
+       SOC_SINGLE("Capture PGA ZC Switch", WM8940_PGAGAIN, 7, 1, 0),
+       SOC_SINGLE_TLV("Capture PGA Volume", WM8940_PGAGAIN,
+                      0, 63, 0, wm8940_pga_vol_tlv),
+       SOC_SINGLE_TLV("Digital Playback Volume", WM8940_DACVOL,
+                      0, 255, 0, wm8940_adc_tlv),
+       SOC_SINGLE_TLV("Digital Capture Volume", WM8940_ADCVOL,
+                      0, 255, 0, wm8940_adc_tlv),
+       SOC_ENUM("Mic Bias Level", wm8940_mic_bias_level_enum),
+       SOC_SINGLE_TLV("Capture Boost Volue", WM8940_ADCBOOST,
+                      8, 1, 0, wm8940_capture_boost_vol_tlv),
+       SOC_SINGLE_TLV("Speaker Playback Volume", WM8940_SPKVOL,
+                      0, 63, 0, wm8940_spk_vol_tlv),
+       SOC_SINGLE("Speaker Playback Switch", WM8940_SPKVOL,  6, 1, 1),
+
+       SOC_SINGLE_TLV("Speaker Mixer Line Bypass Volume", WM8940_SPKVOL,
+                      8, 1, 1, wm8940_att_tlv),
+       SOC_SINGLE("Speaker Playback ZC Switch", WM8940_SPKVOL, 7, 1, 0),
+
+       SOC_SINGLE("Mono Out Switch", WM8940_MONOMIX, 6, 1, 1),
+       SOC_SINGLE_TLV("Mono Mixer Line Bypass Volume", WM8940_MONOMIX,
+                      7, 1, 1, wm8940_att_tlv),
+
+       SOC_SINGLE("High Pass Filter Switch", WM8940_ADC, 8, 1, 0),
+       SOC_ENUM("High Pass Filter Mode", wm8940_filter_mode_enum),
+       SOC_SINGLE("High Pass Filter Cut Off", WM8940_ADC, 4, 7, 0),
+       SOC_SINGLE("ADC Inversion Switch", WM8940_ADC, 0, 1, 0),
+       SOC_SINGLE("DAC Inversion Switch", WM8940_DAC, 0, 1, 0),
+       SOC_SINGLE("DAC Auto Mute Switch", WM8940_DAC, 2, 1, 0),
+       SOC_SINGLE("ZC Timeout Clock Switch", WM8940_ADDCNTRL, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8940_speaker_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_SPKMIX, 1, 1, 0),
+       SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_SPKMIX, 5, 1, 0),
+       SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_SPKMIX, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8940_mono_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_MONOMIX, 1, 1, 0),
+       SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_MONOMIX, 2, 1, 0),
+       SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_MONOMIX, 0, 1, 0),
+};
+
+static DECLARE_TLV_DB_SCALE(wm8940_boost_vol_tlv, -1500, 300, 1);
+static const struct snd_kcontrol_new wm8940_input_boost_controls[] = {
+       SOC_DAPM_SINGLE("Mic PGA Switch", WM8940_PGAGAIN, 6, 1, 1),
+       SOC_DAPM_SINGLE_TLV("Aux Volume", WM8940_ADCBOOST,
+                           0, 7, 0, wm8940_boost_vol_tlv),
+       SOC_DAPM_SINGLE_TLV("Mic Volume", WM8940_ADCBOOST,
+                           4, 7, 0, wm8940_boost_vol_tlv),
+};
+
+static const struct snd_kcontrol_new wm8940_micpga_controls[] = {
+       SOC_DAPM_SINGLE("AUX Switch", WM8940_INPUTCTL, 2, 1, 0),
+       SOC_DAPM_SINGLE("MICP Switch", WM8940_INPUTCTL, 0, 1, 0),
+       SOC_DAPM_SINGLE("MICN Switch", WM8940_INPUTCTL, 1, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8940_dapm_widgets[] = {
+       SND_SOC_DAPM_MIXER("Speaker Mixer", WM8940_POWER3, 2, 0,
+                          &wm8940_speaker_mixer_controls[0],
+                          ARRAY_SIZE(wm8940_speaker_mixer_controls)),
+       SND_SOC_DAPM_MIXER("Mono Mixer", WM8940_POWER3, 3, 0,
+                          &wm8940_mono_mixer_controls[0],
+                          ARRAY_SIZE(wm8940_mono_mixer_controls)),
+       SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8940_POWER3, 0, 0),
+
+       SND_SOC_DAPM_PGA("SpkN Out", WM8940_POWER3, 5, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SpkP Out", WM8940_POWER3, 6, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Mono Out", WM8940_POWER3, 7, 0, NULL, 0),
+       SND_SOC_DAPM_OUTPUT("MONOOUT"),
+       SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+       SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+
+       SND_SOC_DAPM_PGA("Aux Input", WM8940_POWER1, 6, 0, NULL, 0),
+       SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8940_POWER2, 0, 0),
+       SND_SOC_DAPM_MIXER("Mic PGA", WM8940_POWER2, 2, 0,
+                          &wm8940_micpga_controls[0],
+                          ARRAY_SIZE(wm8940_micpga_controls)),
+       SND_SOC_DAPM_MIXER("Boost Mixer", WM8940_POWER2, 4, 0,
+                          &wm8940_input_boost_controls[0],
+                          ARRAY_SIZE(wm8940_input_boost_controls)),
+       SND_SOC_DAPM_MICBIAS("Mic Bias", WM8940_POWER1, 4, 0),
+
+       SND_SOC_DAPM_INPUT("MICN"),
+       SND_SOC_DAPM_INPUT("MICP"),
+       SND_SOC_DAPM_INPUT("AUX"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+       /* Mono output mixer */
+       {"Mono Mixer", "PCM Playback Switch", "DAC"},
+       {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
+       {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
+
+       /* Speaker output mixer */
+       {"Speaker Mixer", "PCM Playback Switch", "DAC"},
+       {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
+       {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
+
+       /* Outputs */
+       {"Mono Out", NULL, "Mono Mixer"},
+       {"MONOOUT", NULL, "Mono Out"},
+       {"SpkN Out", NULL, "Speaker Mixer"},
+       {"SpkP Out", NULL, "Speaker Mixer"},
+       {"SPKOUTN", NULL, "SpkN Out"},
+       {"SPKOUTP", NULL, "SpkP Out"},
+
+       /*  Microphone PGA */
+       {"Mic PGA", "MICN Switch", "MICN"},
+       {"Mic PGA", "MICP Switch", "MICP"},
+       {"Mic PGA", "AUX Switch", "AUX"},
+
+       /* Boost Mixer */
+       {"Boost Mixer", "Mic PGA Switch", "Mic PGA"},
+       {"Boost Mixer", "Mic Volume",  "MICP"},
+       {"Boost Mixer", "Aux Volume", "Aux Input"},
+
+       {"ADC", NULL, "Boost Mixer"},
+};
+
+static int wm8940_add_widgets(struct snd_soc_codec *codec)
+{
+       int ret;
+
+       ret = snd_soc_dapm_new_controls(codec, wm8940_dapm_widgets,
+                                       ARRAY_SIZE(wm8940_dapm_widgets));
+       if (ret)
+               goto error_ret;
+       ret = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+       if (ret)
+               goto error_ret;
+       ret = snd_soc_dapm_new_widgets(codec);
+
+error_ret:
+       return ret;
+}
+
+#define wm8940_reset(c) wm8940_write(c, WM8940_SOFTRESET, 0);
+
+static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai,
+                             unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u16 iface = wm8940_read_reg_cache(codec, WM8940_IFACE) & 0xFE67;
+       u16 clk = wm8940_read_reg_cache(codec, WM8940_CLOCK) & 0x1fe;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               clk |= 1;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+       wm8940_write(codec, WM8940_CLOCK, clk);
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               iface |= (2 << 3);
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               iface |= (1 << 3);
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               iface |= (3 << 3);
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               iface |= (3 << 3) | (1 << 7);
+               break;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               iface |= (1 << 7);
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               iface |= (1 << 8);
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               iface |= (1 << 8) | (1 << 7);
+               break;
+       }
+
+       wm8940_write(codec, WM8940_IFACE, iface);
+
+       return 0;
+}
+
+static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       u16 iface = wm8940_read_reg_cache(codec, WM8940_IFACE) & 0xFD9F;
+       u16 addcntrl = wm8940_read_reg_cache(codec, WM8940_ADDCNTRL) & 0xFFF1;
+       u16 companding =  wm8940_read_reg_cache(codec,
+                                               WM8940_COMPANDINGCTL) & 0xFFDF;
+       int ret;
+
+       /* LoutR control */
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE
+           && params_channels(params) == 2)
+               iface |= (1 << 9);
+
+       switch (params_rate(params)) {
+       case SNDRV_PCM_RATE_8000:
+               addcntrl |= (0x5 << 1);
+               break;
+       case SNDRV_PCM_RATE_11025:
+               addcntrl |= (0x4 << 1);
+               break;
+       case SNDRV_PCM_RATE_16000:
+               addcntrl |= (0x3 << 1);
+               break;
+       case SNDRV_PCM_RATE_22050:
+               addcntrl |= (0x2 << 1);
+               break;
+       case SNDRV_PCM_RATE_32000:
+               addcntrl |= (0x1 << 1);
+               break;
+       case SNDRV_PCM_RATE_44100:
+       case SNDRV_PCM_RATE_48000:
+               break;
+       }
+       ret = wm8940_write(codec, WM8940_ADDCNTRL, addcntrl);
+       if (ret)
+               goto error_ret;
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S8:
+               companding = companding | (1 << 5);
+               break;
+       case SNDRV_PCM_FORMAT_S16_LE:
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               iface |= (1 << 5);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               iface |= (2 << 5);
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               iface |= (3 << 5);
+               break;
+       }
+       ret = wm8940_write(codec, WM8940_COMPANDINGCTL, companding);
+       if (ret)
+               goto error_ret;
+       ret = wm8940_write(codec, WM8940_IFACE, iface);
+
+error_ret:
+       return ret;
+}
+
+static int wm8940_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u16 mute_reg = wm8940_read_reg_cache(codec, WM8940_DAC) & 0xffbf;
+
+       if (mute)
+               mute_reg |= 0x40;
+
+       return wm8940_write(codec, WM8940_DAC, mute_reg);
+}
+
+static int wm8940_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       u16 val;
+       u16 pwr_reg = wm8940_read_reg_cache(codec, WM8940_POWER1) & 0x1F0;
+       int ret = 0;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               /* ensure bufioen and biasen */
+               pwr_reg |= (1 << 2) | (1 << 3);
+               /* Enable thermal shutdown */
+               val = wm8940_read_reg_cache(codec, WM8940_OUTPUTCTL);
+               ret = wm8940_write(codec, WM8940_OUTPUTCTL, val | 0x2);
+               if (ret)
+                       break;
+               /* set vmid to 75k */
+               ret = wm8940_write(codec, WM8940_POWER1, pwr_reg | 0x1);
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               /* ensure bufioen and biasen */
+               pwr_reg |= (1 << 2) | (1 << 3);
+               ret = wm8940_write(codec, WM8940_POWER1, pwr_reg | 0x1);
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               /* ensure bufioen and biasen */
+               pwr_reg |= (1 << 2) | (1 << 3);
+               /* set vmid to 300k for standby */
+               ret = wm8940_write(codec, WM8940_POWER1, pwr_reg | 0x2);
+               break;
+       case SND_SOC_BIAS_OFF:
+               ret = wm8940_write(codec, WM8940_POWER1, pwr_reg);
+               break;
+       }
+
+       return ret;
+}
+
+struct pll_ {
+       unsigned int pre_scale:2;
+       unsigned int n:4;
+       unsigned int k;
+};
+
+static struct pll_ pll_div;
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 24) * 10)
+static void pll_factors(unsigned int target, unsigned int source)
+{
+       unsigned long long Kpart;
+       unsigned int K, Ndiv, Nmod;
+       /* The left shift ist to avoid accuracy loss when right shifting */
+       Ndiv = target / source;
+
+       if (Ndiv > 12) {
+               source <<= 1;
+               /* Multiply by 2 */
+               pll_div.pre_scale = 0;
+               Ndiv = target / source;
+       } else if (Ndiv < 3) {
+               source >>= 2;
+               /* Divide by 4 */
+               pll_div.pre_scale = 3;
+               Ndiv = target / source;
+       } else if (Ndiv < 6) {
+               source >>= 1;
+               /* divide by 2 */
+               pll_div.pre_scale = 2;
+               Ndiv = target / source;
+       } else
+               pll_div.pre_scale = 1;
+
+       if ((Ndiv < 6) || (Ndiv > 12))
+               printk(KERN_WARNING
+                       "WM8940 N value %d outwith recommended range!d\n",
+                       Ndiv);
+
+       pll_div.n = Ndiv;
+       Nmod = target % source;
+       Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+       do_div(Kpart, source);
+
+       K = Kpart & 0xFFFFFFFF;
+
+       /* Check if we need to round */
+       if ((K % 10) >= 5)
+               K += 5;
+
+       /* Move down to proper range now rounding is done */
+       K /= 10;
+
+       pll_div.k = K;
+}
+
+/* Untested at the moment */
+static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai,
+               int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u16 reg;
+
+       /* Turn off PLL */
+       reg = wm8940_read_reg_cache(codec, WM8940_POWER1);
+       wm8940_write(codec, WM8940_POWER1, reg & 0x1df);
+
+       if (freq_in == 0 || freq_out == 0) {
+               /* Clock CODEC directly from MCLK */
+               reg = wm8940_read_reg_cache(codec, WM8940_CLOCK);
+               wm8940_write(codec, WM8940_CLOCK, reg & 0x0ff);
+               /* Pll power down */
+               wm8940_write(codec, WM8940_PLLN, (1 << 7));
+               return 0;
+       }
+
+       /* Pll is followed by a frequency divide by 4 */
+       pll_factors(freq_out*4, freq_in);
+       if (pll_div.k)
+               wm8940_write(codec, WM8940_PLLN,
+                            (pll_div.pre_scale << 4) | pll_div.n | (1 << 6));
+       else /* No factional component */
+               wm8940_write(codec, WM8940_PLLN,
+                            (pll_div.pre_scale << 4) | pll_div.n);
+       wm8940_write(codec, WM8940_PLLK1, pll_div.k >> 18);
+       wm8940_write(codec, WM8940_PLLK2, (pll_div.k >> 9) & 0x1ff);
+       wm8940_write(codec, WM8940_PLLK3, pll_div.k & 0x1ff);
+       /* Enable the PLL */
+       reg = wm8940_read_reg_cache(codec, WM8940_POWER1);
+       wm8940_write(codec, WM8940_POWER1, reg | 0x020);
+
+       /* Run CODEC from PLL instead of MCLK */
+       reg = wm8940_read_reg_cache(codec, WM8940_CLOCK);
+       wm8940_write(codec, WM8940_CLOCK, reg | 0x100);
+
+       return 0;
+}
+
+static int wm8940_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+                                int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct wm8940_priv *wm8940 = codec->private_data;
+
+       switch (freq) {
+       case 11289600:
+       case 12000000:
+       case 12288000:
+       case 16934400:
+       case 18432000:
+               wm8940->sysclk = freq;
+               return 0;
+       }
+       return -EINVAL;
+}
+
+static int wm8940_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+                                int div_id, int div)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u16 reg;
+       int ret = 0;
+
+       switch (div_id) {
+       case WM8940_BCLKDIV:
+               reg = wm8940_read_reg_cache(codec, WM8940_CLOCK) & 0xFFEF3;
+               ret = wm8940_write(codec, WM8940_CLOCK, reg | (div << 2));
+               break;
+       case WM8940_MCLKDIV:
+               reg = wm8940_read_reg_cache(codec, WM8940_CLOCK) & 0xFF1F;
+               ret = wm8940_write(codec, WM8940_CLOCK, reg | (div << 5));
+               break;
+       case WM8940_OPCLKDIV:
+               reg = wm8940_read_reg_cache(codec, WM8940_ADDCNTRL) & 0xFFCF;
+               ret = wm8940_write(codec, WM8940_ADDCNTRL, reg | (div << 4));
+               break;
+       }
+       return ret;
+}
+
+#define WM8940_RATES SNDRV_PCM_RATE_8000_48000
+
+#define WM8940_FORMATS (SNDRV_PCM_FMTBIT_S8 |                          \
+                       SNDRV_PCM_FMTBIT_S16_LE |                       \
+                       SNDRV_PCM_FMTBIT_S20_3LE |                      \
+                       SNDRV_PCM_FMTBIT_S24_LE |                       \
+                       SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops wm8940_dai_ops = {
+       .hw_params = wm8940_i2s_hw_params,
+       .set_sysclk = wm8940_set_dai_sysclk,
+       .digital_mute = wm8940_mute,
+       .set_fmt = wm8940_set_dai_fmt,
+       .set_clkdiv = wm8940_set_dai_clkdiv,
+       .set_pll = wm8940_set_dai_pll,
+};
+
+struct snd_soc_dai wm8940_dai = {
+       .name = "WM8940",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = WM8940_RATES,
+               .formats = WM8940_FORMATS,
+       },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = WM8940_RATES,
+               .formats = WM8940_FORMATS,
+       },
+       .ops = &wm8940_dai_ops,
+       .symmetric_rates = 1,
+};
+EXPORT_SYMBOL_GPL(wm8940_dai);
+
+static int wm8940_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       return wm8940_set_bias_level(codec, SND_SOC_BIAS_OFF);
+}
+
+static int wm8940_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+       int i;
+       int ret;
+       u8 data[3];
+       u16 *cache = codec->reg_cache;
+
+       /* Sync reg_cache with the hardware
+        * Could use auto incremented writes to speed this up
+        */
+       for (i = 0; i < ARRAY_SIZE(wm8940_reg_defaults); i++) {
+               data[0] = i;
+               data[1] = (cache[i] & 0xFF00) >> 8;
+               data[2] = cache[i] & 0x00FF;
+               ret = codec->hw_write(codec->control_data, data, 3);
+               if (ret < 0)
+                       goto error_ret;
+               else if (ret != 3) {
+                       ret = -EIO;
+                       goto error_ret;
+               }
+       }
+       ret = wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       if (ret)
+               goto error_ret;
+       ret = wm8940_set_bias_level(codec, codec->suspend_bias_level);
+
+error_ret:
+       return ret;
+}
+
+static struct snd_soc_codec *wm8940_codec;
+
+static int wm8940_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+
+       int ret = 0;
+
+       if (wm8940_codec == NULL) {
+               dev_err(&pdev->dev, "Codec device not registered\n");
+               return -ENODEV;
+       }
+
+       socdev->card->codec = wm8940_codec;
+       codec = wm8940_codec;
+
+       mutex_init(&codec->mutex);
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+               goto pcm_err;
+       }
+
+       ret = snd_soc_add_controls(codec, wm8940_snd_controls,
+                            ARRAY_SIZE(wm8940_snd_controls));
+       if (ret)
+               goto error_free_pcms;
+       ret = wm8940_add_widgets(codec);
+       if (ret)
+               goto error_free_pcms;
+
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to register card: %d\n", ret);
+               goto error_free_pcms;
+       }
+
+       return ret;
+
+error_free_pcms:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+pcm_err:
+       return ret;
+}
+
+static int wm8940_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+       return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8940 = {
+       .probe = wm8940_probe,
+       .remove = wm8940_remove,
+       .suspend = wm8940_suspend,
+       .resume = wm8940_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8940);
+
+static int wm8940_register(struct wm8940_priv *wm8940)
+{
+       struct wm8940_setup_data *pdata = wm8940->codec.dev->platform_data;
+       struct snd_soc_codec *codec = &wm8940->codec;
+       int ret;
+       u16 reg;
+       if (wm8940_codec) {
+               dev_err(codec->dev, "Another WM8940 is registered\n");
+               return -EINVAL;
+       }
+
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->private_data = wm8940;
+       codec->name = "WM8940";
+       codec->owner = THIS_MODULE;
+       codec->read = wm8940_read_reg_cache;
+       codec->write = wm8940_write;
+       codec->bias_level = SND_SOC_BIAS_OFF;
+       codec->set_bias_level = wm8940_set_bias_level;
+       codec->dai = &wm8940_dai;
+       codec->num_dai = 1;
+       codec->reg_cache_size = ARRAY_SIZE(wm8940_reg_defaults);
+       codec->reg_cache = &wm8940->reg_cache;
+
+       memcpy(codec->reg_cache, wm8940_reg_defaults,
+              sizeof(wm8940_reg_defaults));
+
+       ret = wm8940_reset(codec);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to issue reset\n");
+               return ret;
+       }
+
+       wm8940_dai.dev = codec->dev;
+
+       wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       ret = wm8940_write(codec, WM8940_POWER1, 0x180);
+       if (ret < 0)
+               return ret;
+
+       if (!pdata)
+               dev_warn(codec->dev, "No platform data supplied\n");
+       else {
+               reg = wm8940_read_reg_cache(codec, WM8940_OUTPUTCTL);
+               ret = wm8940_write(codec, WM8940_OUTPUTCTL, reg | pdata->vroi);
+               if (ret < 0)
+                       return ret;
+       }
+
+
+       wm8940_codec = codec;
+
+       ret = snd_soc_register_codec(codec);
+       if (ret) {
+               dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+               return ret;
+       }
+
+       ret = snd_soc_register_dai(&wm8940_dai);
+       if (ret) {
+               dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+               snd_soc_unregister_codec(codec);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void wm8940_unregister(struct wm8940_priv *wm8940)
+{
+       wm8940_set_bias_level(&wm8940->codec, SND_SOC_BIAS_OFF);
+       snd_soc_unregister_dai(&wm8940_dai);
+       snd_soc_unregister_codec(&wm8940->codec);
+       kfree(wm8940);
+       wm8940_codec = NULL;
+}
+
+static int wm8940_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       struct wm8940_priv *wm8940;
+       struct snd_soc_codec *codec;
+
+       wm8940 = kzalloc(sizeof *wm8940, GFP_KERNEL);
+       if (wm8940 == NULL)
+               return -ENOMEM;
+
+       codec = &wm8940->codec;
+       codec->hw_write = (hw_write_t)i2c_master_send;
+       i2c_set_clientdata(i2c, wm8940);
+       codec->control_data = i2c;
+       codec->dev = &i2c->dev;
+
+       return wm8940_register(wm8940);
+}
+
+static int __devexit wm8940_i2c_remove(struct i2c_client *client)
+{
+       struct wm8940_priv *wm8940 = i2c_get_clientdata(client);
+
+       wm8940_unregister(wm8940);
+
+       return 0;
+}
+
+static const struct i2c_device_id wm8940_i2c_id[] = {
+       { "wm8940", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8940_i2c_id);
+
+static struct i2c_driver wm8940_i2c_driver = {
+       .driver = {
+               .name = "WM8940 I2C Codec",
+               .owner = THIS_MODULE,
+       },
+       .probe = wm8940_i2c_probe,
+       .remove = __devexit_p(wm8940_i2c_remove),
+       .id_table = wm8940_i2c_id,
+};
+
+static int __init wm8940_modinit(void)
+{
+       int ret;
+
+       ret = i2c_add_driver(&wm8940_i2c_driver);
+       if (ret)
+               printk(KERN_ERR "Failed to register WM8940 I2C driver: %d\n",
+                      ret);
+       return ret;
+}
+module_init(wm8940_modinit);
+
+static void __exit wm8940_exit(void)
+{
+       i2c_del_driver(&wm8940_i2c_driver);
+}
+module_exit(wm8940_exit);
+
+MODULE_DESCRIPTION("ASoC WM8940 driver");
+MODULE_AUTHOR("Jonathan Cameron");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8940.h b/sound/soc/codecs/wm8940.h
new file mode 100644 (file)
index 0000000..8410eed
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * wm8940.h -- WM8940 Soc Audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8940_H
+#define _WM8940_H
+
+struct wm8940_setup_data {
+       /* Vref to analogue output resistance */
+#define WM8940_VROI_1K 0
+#define WM8940_VROI_30K 1
+       unsigned int vroi:1;
+};
+extern struct snd_soc_dai wm8940_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8940;
+
+/* WM8940 register space */
+#define WM8940_SOFTRESET       0x00
+#define WM8940_POWER1          0x01
+#define WM8940_POWER2          0x02
+#define WM8940_POWER3          0x03
+#define WM8940_IFACE           0x04
+#define WM8940_COMPANDINGCTL   0x05
+#define WM8940_CLOCK           0x06
+#define WM8940_ADDCNTRL                0x07
+#define WM8940_GPIO            0x08
+#define WM8940_CTLINT          0x09
+#define WM8940_DAC             0x0A
+#define WM8940_DACVOL          0x0B
+
+#define WM8940_ADC             0x0E
+#define WM8940_ADCVOL          0x0F
+#define WM8940_NOTCH1          0x10
+#define WM8940_NOTCH2          0x11
+#define WM8940_NOTCH3          0x12
+#define WM8940_NOTCH4          0x13
+#define WM8940_NOTCH5          0x14
+#define WM8940_NOTCH6          0x15
+#define WM8940_NOTCH7          0x16
+#define WM8940_NOTCH8          0x17
+#define WM8940_DACLIM1         0x18
+#define WM8940_DACLIM2         0x19
+
+#define WM8940_ALC1            0x20
+#define WM8940_ALC2            0x21
+#define WM8940_ALC3            0x22
+#define WM8940_NOISEGATE       0x23
+#define WM8940_PLLN            0x24
+#define WM8940_PLLK1           0x25
+#define WM8940_PLLK2           0x26
+#define WM8940_PLLK3           0x27
+
+#define WM8940_ALC4            0x2A
+
+#define WM8940_INPUTCTL                0x2C
+#define WM8940_PGAGAIN         0x2D
+
+#define WM8940_ADCBOOST                0x2F
+
+#define WM8940_OUTPUTCTL       0x31
+#define WM8940_SPKMIX          0x32
+
+#define WM8940_SPKVOL          0x36
+
+#define WM8940_MONOMIX         0x38
+
+#define WM8940_CACHEREGNUM  0x57
+
+
+/* Clock divider Id's */
+#define WM8940_BCLKDIV 0
+#define WM8940_MCLKDIV 1
+#define WM8940_OPCLKDIV 2
+
+/* MCLK clock dividers */
+#define WM8940_MCLKDIV_1       0
+#define WM8940_MCLKDIV_1_5     1
+#define WM8940_MCLKDIV_2       2
+#define WM8940_MCLKDIV_3       3
+#define WM8940_MCLKDIV_4       4
+#define WM8940_MCLKDIV_6       5
+#define WM8940_MCLKDIV_8       6
+#define WM8940_MCLKDIV_12      7
+
+/* BCLK clock dividers */
+#define WM8940_BCLKDIV_1 0
+#define WM8940_BCLKDIV_2 1
+#define WM8940_BCLKDIV_4 2
+#define WM8940_BCLKDIV_8 3
+#define WM8940_BCLKDIV_16 4
+#define WM8940_BCLKDIV_32 5
+
+/* PLL Out Dividers */
+#define WM8940_OPCLKDIV_1 0
+#define WM8940_OPCLKDIV_2 1
+#define WM8940_OPCLKDIV_3 2
+#define WM8940_OPCLKDIV_4 3
+
+#endif /* _WM8940_H */
+
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
new file mode 100644 (file)
index 0000000..e224d8a
--- /dev/null
@@ -0,0 +1,969 @@
+/*
+ * wm8960.c  --  WM8960 ALSA SoC Audio driver
+ *
+ * Author: Liam Girdwood
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8960.h"
+
+#define AUDIO_NAME "wm8960"
+
+struct snd_soc_codec_device soc_codec_dev_wm8960;
+
+/* R25 - Power 1 */
+#define WM8960_VREF      0x40
+
+/* R28 - Anti-pop 1 */
+#define WM8960_POBCTRL   0x80
+#define WM8960_BUFDCOPEN 0x10
+#define WM8960_BUFIOEN   0x08
+#define WM8960_SOFT_ST   0x04
+#define WM8960_HPSTBY    0x01
+
+/* R29 - Anti-pop 2 */
+#define WM8960_DISOP     0x40
+
+/*
+ * wm8960 register cache
+ * We can't read the WM8960 register space when we are
+ * using 2 wire for device control, so we cache them instead.
+ */
+static const u16 wm8960_reg[WM8960_CACHEREGNUM] = {
+       0x0097, 0x0097, 0x0000, 0x0000,
+       0x0000, 0x0008, 0x0000, 0x000a,
+       0x01c0, 0x0000, 0x00ff, 0x00ff,
+       0x0000, 0x0000, 0x0000, 0x0000,
+       0x0000, 0x007b, 0x0100, 0x0032,
+       0x0000, 0x00c3, 0x00c3, 0x01c0,
+       0x0000, 0x0000, 0x0000, 0x0000,
+       0x0000, 0x0000, 0x0000, 0x0000,
+       0x0100, 0x0100, 0x0050, 0x0050,
+       0x0050, 0x0050, 0x0000, 0x0000,
+       0x0000, 0x0000, 0x0040, 0x0000,
+       0x0000, 0x0050, 0x0050, 0x0000,
+       0x0002, 0x0037, 0x004d, 0x0080,
+       0x0008, 0x0031, 0x0026, 0x00e9,
+};
+
+struct wm8960_priv {
+       u16 reg_cache[WM8960_CACHEREGNUM];
+       struct snd_soc_codec codec;
+};
+
+/*
+ * read wm8960 register cache
+ */
+static inline unsigned int wm8960_read_reg_cache(struct snd_soc_codec *codec,
+       unsigned int reg)
+{
+       u16 *cache = codec->reg_cache;
+       if (reg == WM8960_RESET)
+               return 0;
+       if (reg >= WM8960_CACHEREGNUM)
+               return -1;
+       return cache[reg];
+}
+
+/*
+ * write wm8960 register cache
+ */
+static inline void wm8960_write_reg_cache(struct snd_soc_codec *codec,
+       u16 reg, unsigned int value)
+{
+       u16 *cache = codec->reg_cache;
+       if (reg >= WM8960_CACHEREGNUM)
+               return;
+       cache[reg] = value;
+}
+
+static inline unsigned int wm8960_read(struct snd_soc_codec *codec,
+       unsigned int reg)
+{
+       return wm8960_read_reg_cache(codec, reg);
+}
+
+/*
+ * write to the WM8960 register space
+ */
+static int wm8960_write(struct snd_soc_codec *codec, unsigned int reg,
+       unsigned int value)
+{
+       u8 data[2];
+
+       /* data is
+        *   D15..D9 WM8960 register offset
+        *   D8...D0 register data
+        */
+       data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+       data[1] = value & 0x00ff;
+
+       wm8960_write_reg_cache(codec, reg, value);
+       if (codec->hw_write(codec->control_data, data, 2) == 2)
+               return 0;
+       else
+               return -EIO;
+}
+
+#define wm8960_reset(c)        wm8960_write(c, WM8960_RESET, 0)
+
+/* enumerated controls */
+static const char *wm8960_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted",
+       "Right Inverted", "Stereo Inversion"};
+static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"};
+static const char *wm8960_3d_lower_cutoff[] = {"Low", "High"};
+static const char *wm8960_alcfunc[] = {"Off", "Right", "Left", "Stereo"};
+static const char *wm8960_alcmode[] = {"ALC", "Limiter"};
+
+static const struct soc_enum wm8960_enum[] = {
+       SOC_ENUM_SINGLE(WM8960_DACCTL1, 1, 4, wm8960_deemph),
+       SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity),
+       SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity),
+       SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff),
+       SOC_ENUM_SINGLE(WM8960_3D, 5, 2, wm8960_3d_lower_cutoff),
+       SOC_ENUM_SINGLE(WM8960_ALC1, 7, 4, wm8960_alcfunc),
+       SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode),
+};
+
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1);
+static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+
+static const struct snd_kcontrol_new wm8960_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
+                0, 63, 0, adc_tlv),
+SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL,
+       6, 1, 0),
+SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL,
+       7, 1, 0),
+
+SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC,
+                0, 255, 0, dac_tlv),
+
+SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8960_LOUT1, WM8960_ROUT1,
+                0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8960_LOUT1, WM8960_ROUT1,
+       7, 1, 0),
+
+SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8960_LOUT2, WM8960_ROUT2,
+                0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8960_LOUT2, WM8960_ROUT2,
+       7, 1, 0),
+SOC_SINGLE("Speaker DC Volume", WM8960_CLASSD3, 3, 5, 0),
+SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0),
+
+SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0),
+SOC_ENUM("ADC Polarity", wm8960_enum[1]),
+SOC_ENUM("Playback De-emphasis", wm8960_enum[0]),
+SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0),
+
+SOC_ENUM("DAC Polarity", wm8960_enum[2]),
+
+SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[3]),
+SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[4]),
+SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0),
+SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0),
+
+SOC_ENUM("ALC Function", wm8960_enum[5]),
+SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0),
+SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1),
+SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0),
+SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0),
+SOC_ENUM("ALC Mode", wm8960_enum[6]),
+SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0),
+SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0),
+
+SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0),
+SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0),
+
+SOC_DOUBLE_R("ADC PCM Capture Volume", WM8960_LINPATH, WM8960_RINPATH,
+       0, 127, 0),
+
+SOC_SINGLE_TLV("Left Output Mixer Boost Bypass Volume",
+              WM8960_BYPASS1, 4, 7, 1, bypass_tlv),
+SOC_SINGLE_TLV("Left Output Mixer LINPUT3 Volume",
+              WM8960_LOUTMIX, 4, 7, 1, bypass_tlv),
+SOC_SINGLE_TLV("Right Output Mixer Boost Bypass Volume",
+              WM8960_BYPASS2, 4, 7, 1, bypass_tlv),
+SOC_SINGLE_TLV("Right Output Mixer RINPUT3 Volume",
+              WM8960_ROUTMIX, 4, 7, 1, bypass_tlv),
+};
+
+static const struct snd_kcontrol_new wm8960_lin_boost[] = {
+SOC_DAPM_SINGLE("LINPUT2 Switch", WM8960_LINPATH, 6, 1, 0),
+SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LINPATH, 7, 1, 0),
+SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8960_lin[] = {
+SOC_DAPM_SINGLE("Boost Switch", WM8960_LINPATH, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8960_rin_boost[] = {
+SOC_DAPM_SINGLE("RINPUT2 Switch", WM8960_RINPATH, 6, 1, 0),
+SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_RINPATH, 7, 1, 0),
+SOC_DAPM_SINGLE("RINPUT1 Switch", WM8960_RINPATH, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8960_rin[] = {
+SOC_DAPM_SINGLE("Boost Switch", WM8960_RINPATH, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8960_loutput_mixer[] = {
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_LOUTMIX, 8, 1, 0),
+SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LOUTMIX, 7, 1, 0),
+SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS1, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8960_routput_mixer[] = {
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_ROUTMIX, 8, 1, 0),
+SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_ROUTMIX, 7, 1, 0),
+SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS2, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8960_mono_out[] = {
+SOC_DAPM_SINGLE("Left Switch", WM8960_MONOMIX1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Switch", WM8960_MONOMIX2, 7, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("LINPUT1"),
+SND_SOC_DAPM_INPUT("RINPUT1"),
+SND_SOC_DAPM_INPUT("LINPUT2"),
+SND_SOC_DAPM_INPUT("RINPUT2"),
+SND_SOC_DAPM_INPUT("LINPUT3"),
+SND_SOC_DAPM_INPUT("RINPUT3"),
+
+SND_SOC_DAPM_MICBIAS("MICB", WM8960_POWER1, 1, 0),
+
+SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8960_POWER1, 5, 0,
+                  wm8960_lin_boost, ARRAY_SIZE(wm8960_lin_boost)),
+SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8960_POWER1, 4, 0,
+                  wm8960_rin_boost, ARRAY_SIZE(wm8960_rin_boost)),
+
+SND_SOC_DAPM_MIXER("Left Input Mixer", WM8960_POWER3, 5, 0,
+                  wm8960_lin, ARRAY_SIZE(wm8960_lin)),
+SND_SOC_DAPM_MIXER("Right Input Mixer", WM8960_POWER3, 4, 0,
+                  wm8960_rin, ARRAY_SIZE(wm8960_rin)),
+
+SND_SOC_DAPM_ADC("Left ADC", "Capture", WM8960_POWER2, 3, 0),
+SND_SOC_DAPM_ADC("Right ADC", "Capture", WM8960_POWER2, 2, 0),
+
+SND_SOC_DAPM_DAC("Left DAC", "Playback", WM8960_POWER2, 8, 0),
+SND_SOC_DAPM_DAC("Right DAC", "Playback", WM8960_POWER2, 7, 0),
+
+SND_SOC_DAPM_MIXER("Left Output Mixer", WM8960_POWER3, 3, 0,
+       &wm8960_loutput_mixer[0],
+       ARRAY_SIZE(wm8960_loutput_mixer)),
+SND_SOC_DAPM_MIXER("Right Output Mixer", WM8960_POWER3, 2, 0,
+       &wm8960_routput_mixer[0],
+       ARRAY_SIZE(wm8960_routput_mixer)),
+
+SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0,
+       &wm8960_mono_out[0],
+       ARRAY_SIZE(wm8960_mono_out)),
+
+SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Left Speaker PGA", WM8960_POWER2, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Speaker PGA", WM8960_POWER2, 3, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Right Speaker Output", WM8960_CLASSD1, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Left Speaker Output", WM8960_CLASSD1, 6, 0, NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("SPK_LP"),
+SND_SOC_DAPM_OUTPUT("SPK_LN"),
+SND_SOC_DAPM_OUTPUT("HP_L"),
+SND_SOC_DAPM_OUTPUT("HP_R"),
+SND_SOC_DAPM_OUTPUT("SPK_RP"),
+SND_SOC_DAPM_OUTPUT("SPK_RN"),
+SND_SOC_DAPM_OUTPUT("OUT3"),
+};
+
+static const struct snd_soc_dapm_route audio_paths[] = {
+       { "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" },
+       { "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" },
+       { "Left Boost Mixer", "LINPUT3 Switch", "LINPUT3" },
+
+       { "Left Input Mixer", "Boost Switch", "Left Boost Mixer", },
+       { "Left Input Mixer", NULL, "LINPUT1", },  /* Really Boost Switch */
+       { "Left Input Mixer", NULL, "LINPUT2" },
+       { "Left Input Mixer", NULL, "LINPUT3" },
+
+       { "Right Boost Mixer", "RINPUT1 Switch", "RINPUT1" },
+       { "Right Boost Mixer", "RINPUT2 Switch", "RINPUT2" },
+       { "Right Boost Mixer", "RINPUT3 Switch", "RINPUT3" },
+
+       { "Right Input Mixer", "Boost Switch", "Right Boost Mixer", },
+       { "Right Input Mixer", NULL, "RINPUT1", },  /* Really Boost Switch */
+       { "Right Input Mixer", NULL, "RINPUT2" },
+       { "Right Input Mixer", NULL, "LINPUT3" },
+
+       { "Left ADC", NULL, "Left Input Mixer" },
+       { "Right ADC", NULL, "Right Input Mixer" },
+
+       { "Left Output Mixer", "LINPUT3 Switch", "LINPUT3" },
+       { "Left Output Mixer", "Boost Bypass Switch", "Left Boost Mixer"} ,
+       { "Left Output Mixer", "PCM Playback Switch", "Left DAC" },
+
+       { "Right Output Mixer", "RINPUT3 Switch", "RINPUT3" },
+       { "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" } ,
+       { "Right Output Mixer", "PCM Playback Switch", "Right DAC" },
+
+       { "Mono Output Mixer", "Left Switch", "Left Output Mixer" },
+       { "Mono Output Mixer", "Right Switch", "Right Output Mixer" },
+
+       { "LOUT1 PGA", NULL, "Left Output Mixer" },
+       { "ROUT1 PGA", NULL, "Right Output Mixer" },
+
+       { "HP_L", NULL, "LOUT1 PGA" },
+       { "HP_R", NULL, "ROUT1 PGA" },
+
+       { "Left Speaker PGA", NULL, "Left Output Mixer" },
+       { "Right Speaker PGA", NULL, "Right Output Mixer" },
+
+       { "Left Speaker Output", NULL, "Left Speaker PGA" },
+       { "Right Speaker Output", NULL, "Right Speaker PGA" },
+
+       { "SPK_LN", NULL, "Left Speaker Output" },
+       { "SPK_LP", NULL, "Left Speaker Output" },
+       { "SPK_RN", NULL, "Right Speaker Output" },
+       { "SPK_RP", NULL, "Right Speaker Output" },
+
+       { "OUT3", NULL, "Mono Output Mixer", }
+};
+
+static int wm8960_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets,
+                                 ARRAY_SIZE(wm8960_dapm_widgets));
+
+       snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
+
+       snd_soc_dapm_new_widgets(codec);
+       return 0;
+}
+
+static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai,
+               unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u16 iface = 0;
+
+       /* set master/slave audio interface */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               iface |= 0x0040;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* interface format */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               iface |= 0x0002;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               iface |= 0x0001;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               iface |= 0x0003;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               iface |= 0x0013;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* clock inversion */
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               iface |= 0x0090;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               iface |= 0x0080;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               iface |= 0x0010;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* set iface */
+       wm8960_write(codec, WM8960_IFACE1, iface);
+       return 0;
+}
+
+static int wm8960_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       u16 iface = wm8960_read(codec, WM8960_IFACE1) & 0xfff3;
+
+       /* bit size */
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               iface |= 0x0004;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               iface |= 0x0008;
+               break;
+       }
+
+       /* set iface */
+       wm8960_write(codec, WM8960_IFACE1, iface);
+       return 0;
+}
+
+static int wm8960_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u16 mute_reg = wm8960_read(codec, WM8960_DACCTL1) & 0xfff7;
+
+       if (mute)
+               wm8960_write(codec, WM8960_DACCTL1, mute_reg | 0x8);
+       else
+               wm8960_write(codec, WM8960_DACCTL1, mute_reg);
+       return 0;
+}
+
+static int wm8960_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       struct wm8960_data *pdata = codec->dev->platform_data;
+       u16 reg;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               /* Set VMID to 2x50k */
+               reg = wm8960_read(codec, WM8960_POWER1);
+               reg &= ~0x180;
+               reg |= 0x80;
+               wm8960_write(codec, WM8960_POWER1, reg);
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->bias_level == SND_SOC_BIAS_OFF) {
+                       /* Enable anti-pop features */
+                       wm8960_write(codec, WM8960_APOP1,
+                                    WM8960_POBCTRL | WM8960_SOFT_ST |
+                                    WM8960_BUFDCOPEN | WM8960_BUFIOEN);
+
+                       /* Discharge HP output */
+                       reg = WM8960_DISOP;
+                       if (pdata)
+                               reg |= pdata->dres << 4;
+                       wm8960_write(codec, WM8960_APOP2, reg);
+
+                       msleep(400);
+
+                       wm8960_write(codec, WM8960_APOP2, 0);
+
+                       /* Enable & ramp VMID at 2x50k */
+                       reg = wm8960_read(codec, WM8960_POWER1);
+                       reg |= 0x80;
+                       wm8960_write(codec, WM8960_POWER1, reg);
+                       msleep(100);
+
+                       /* Enable VREF */
+                       wm8960_write(codec, WM8960_POWER1, reg | WM8960_VREF);
+
+                       /* Disable anti-pop features */
+                       wm8960_write(codec, WM8960_APOP1, WM8960_BUFIOEN);
+               }
+
+               /* Set VMID to 2x250k */
+               reg = wm8960_read(codec, WM8960_POWER1);
+               reg &= ~0x180;
+               reg |= 0x100;
+               wm8960_write(codec, WM8960_POWER1, reg);
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               /* Enable anti-pop features */
+               wm8960_write(codec, WM8960_APOP1,
+                            WM8960_POBCTRL | WM8960_SOFT_ST |
+                            WM8960_BUFDCOPEN | WM8960_BUFIOEN);
+
+               /* Disable VMID and VREF, let them discharge */
+               wm8960_write(codec, WM8960_POWER1, 0);
+               msleep(600);
+
+               wm8960_write(codec, WM8960_APOP1, 0);
+               break;
+       }
+
+       codec->bias_level = level;
+
+       return 0;
+}
+
+/* PLL divisors */
+struct _pll_div {
+       u32 pre_div:1;
+       u32 n:4;
+       u32 k:24;
+};
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 24) * 10)
+
+static int pll_factors(unsigned int source, unsigned int target,
+                      struct _pll_div *pll_div)
+{
+       unsigned long long Kpart;
+       unsigned int K, Ndiv, Nmod;
+
+       pr_debug("WM8960 PLL: setting %dHz->%dHz\n", source, target);
+
+       /* Scale up target to PLL operating frequency */
+       target *= 4;
+
+       Ndiv = target / source;
+       if (Ndiv < 6) {
+               source >>= 1;
+               pll_div->pre_div = 1;
+               Ndiv = target / source;
+       } else
+               pll_div->pre_div = 0;
+
+       if ((Ndiv < 6) || (Ndiv > 12)) {
+               pr_err("WM8960 PLL: Unsupported N=%d\n", Ndiv);
+               return -EINVAL;
+       }
+
+       pll_div->n = Ndiv;
+       Nmod = target % source;
+       Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+       do_div(Kpart, source);
+
+       K = Kpart & 0xFFFFFFFF;
+
+       /* Check if we need to round */
+       if ((K % 10) >= 5)
+               K += 5;
+
+       /* Move down to proper range now rounding is done */
+       K /= 10;
+
+       pll_div->k = K;
+
+       pr_debug("WM8960 PLL: N=%x K=%x pre_div=%d\n",
+                pll_div->n, pll_div->k, pll_div->pre_div);
+
+       return 0;
+}
+
+static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai,
+               int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u16 reg;
+       static struct _pll_div pll_div;
+       int ret;
+
+       if (freq_in && freq_out) {
+               ret = pll_factors(freq_in, freq_out, &pll_div);
+               if (ret != 0)
+                       return ret;
+       }
+
+       /* Disable the PLL: even if we are changing the frequency the
+        * PLL needs to be disabled while we do so. */
+       wm8960_write(codec, WM8960_CLOCK1,
+                    wm8960_read(codec, WM8960_CLOCK1) & ~1);
+       wm8960_write(codec, WM8960_POWER2,
+                    wm8960_read(codec, WM8960_POWER2) & ~1);
+
+       if (!freq_in || !freq_out)
+               return 0;
+
+       reg = wm8960_read(codec, WM8960_PLL1) & ~0x3f;
+       reg |= pll_div.pre_div << 4;
+       reg |= pll_div.n;
+
+       if (pll_div.k) {
+               reg |= 0x20;
+
+               wm8960_write(codec, WM8960_PLL2, (pll_div.k >> 18) & 0x3f);
+               wm8960_write(codec, WM8960_PLL3, (pll_div.k >> 9) & 0x1ff);
+               wm8960_write(codec, WM8960_PLL4, pll_div.k & 0x1ff);
+       }
+       wm8960_write(codec, WM8960_PLL1, reg);
+
+       /* Turn it on */
+       wm8960_write(codec, WM8960_POWER2,
+                    wm8960_read(codec, WM8960_POWER2) | 1);
+       msleep(250);
+       wm8960_write(codec, WM8960_CLOCK1,
+                    wm8960_read(codec, WM8960_CLOCK1) | 1);
+
+       return 0;
+}
+
+static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+               int div_id, int div)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u16 reg;
+
+       switch (div_id) {
+       case WM8960_SYSCLKSEL:
+               reg = wm8960_read(codec, WM8960_CLOCK1) & 0x1fe;
+               wm8960_write(codec, WM8960_CLOCK1, reg | div);
+               break;
+       case WM8960_SYSCLKDIV:
+               reg = wm8960_read(codec, WM8960_CLOCK1) & 0x1f9;
+               wm8960_write(codec, WM8960_CLOCK1, reg | div);
+               break;
+       case WM8960_DACDIV:
+               reg = wm8960_read(codec, WM8960_CLOCK1) & 0x1c7;
+               wm8960_write(codec, WM8960_CLOCK1, reg | div);
+               break;
+       case WM8960_OPCLKDIV:
+               reg = wm8960_read(codec, WM8960_PLL1) & 0x03f;
+               wm8960_write(codec, WM8960_PLL1, reg | div);
+               break;
+       case WM8960_DCLKDIV:
+               reg = wm8960_read(codec, WM8960_CLOCK2) & 0x03f;
+               wm8960_write(codec, WM8960_CLOCK2, reg | div);
+               break;
+       case WM8960_TOCLKSEL:
+               reg = wm8960_read(codec, WM8960_ADDCTL1) & 0x1fd;
+               wm8960_write(codec, WM8960_ADDCTL1, reg | div);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+#define WM8960_RATES SNDRV_PCM_RATE_8000_48000
+
+#define WM8960_FORMATS \
+       (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+       SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8960_dai_ops = {
+       .hw_params = wm8960_hw_params,
+       .digital_mute = wm8960_mute,
+       .set_fmt = wm8960_set_dai_fmt,
+       .set_clkdiv = wm8960_set_dai_clkdiv,
+       .set_pll = wm8960_set_dai_pll,
+};
+
+struct snd_soc_dai wm8960_dai = {
+       .name = "WM8960",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = WM8960_RATES,
+               .formats = WM8960_FORMATS,},
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = WM8960_RATES,
+               .formats = WM8960_FORMATS,},
+       .ops = &wm8960_dai_ops,
+       .symmetric_rates = 1,
+};
+EXPORT_SYMBOL_GPL(wm8960_dai);
+
+static int wm8960_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       wm8960_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int wm8960_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+       int i;
+       u8 data[2];
+       u16 *cache = codec->reg_cache;
+
+       /* Sync reg_cache with the hardware */
+       for (i = 0; i < ARRAY_SIZE(wm8960_reg); i++) {
+               data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+               data[1] = cache[i] & 0x00ff;
+               codec->hw_write(codec->control_data, data, 2);
+       }
+
+       wm8960_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       wm8960_set_bias_level(codec, codec->suspend_bias_level);
+       return 0;
+}
+
+static struct snd_soc_codec *wm8960_codec;
+
+static int wm8960_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       int ret = 0;
+
+       if (wm8960_codec == NULL) {
+               dev_err(&pdev->dev, "Codec device not registered\n");
+               return -ENODEV;
+       }
+
+       socdev->card->codec = wm8960_codec;
+       codec = wm8960_codec;
+
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+               goto pcm_err;
+       }
+
+       snd_soc_add_controls(codec, wm8960_snd_controls,
+                            ARRAY_SIZE(wm8960_snd_controls));
+       wm8960_add_widgets(codec);
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to register card: %d\n", ret);
+               goto card_err;
+       }
+
+       return ret;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+pcm_err:
+       return ret;
+}
+
+/* power down chip */
+static int wm8960_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+       return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8960 = {
+       .probe =        wm8960_probe,
+       .remove =       wm8960_remove,
+       .suspend =      wm8960_suspend,
+       .resume =       wm8960_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8960);
+
+static int wm8960_register(struct wm8960_priv *wm8960)
+{
+       struct wm8960_data *pdata = wm8960->codec.dev->platform_data;
+       struct snd_soc_codec *codec = &wm8960->codec;
+       int ret;
+       u16 reg;
+
+       if (wm8960_codec) {
+               dev_err(codec->dev, "Another WM8960 is registered\n");
+               return -EINVAL;
+       }
+
+       if (!pdata) {
+               dev_warn(codec->dev, "No platform data supplied\n");
+       } else {
+               if (pdata->dres > WM8960_DRES_MAX) {
+                       dev_err(codec->dev, "Invalid DRES: %d\n", pdata->dres);
+                       pdata->dres = 0;
+               }
+       }
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->private_data = wm8960;
+       codec->name = "WM8960";
+       codec->owner = THIS_MODULE;
+       codec->read = wm8960_read_reg_cache;
+       codec->write = wm8960_write;
+       codec->bias_level = SND_SOC_BIAS_OFF;
+       codec->set_bias_level = wm8960_set_bias_level;
+       codec->dai = &wm8960_dai;
+       codec->num_dai = 1;
+       codec->reg_cache_size = WM8960_CACHEREGNUM;
+       codec->reg_cache = &wm8960->reg_cache;
+
+       memcpy(codec->reg_cache, wm8960_reg, sizeof(wm8960_reg));
+
+       ret = wm8960_reset(codec);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to issue reset\n");
+               return ret;
+       }
+
+       wm8960_dai.dev = codec->dev;
+
+       wm8960_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       /* Latch the update bits */
+       reg = wm8960_read(codec, WM8960_LINVOL);
+       wm8960_write(codec, WM8960_LINVOL, reg | 0x100);
+       reg = wm8960_read(codec, WM8960_RINVOL);
+       wm8960_write(codec, WM8960_RINVOL, reg | 0x100);
+       reg = wm8960_read(codec, WM8960_LADC);
+       wm8960_write(codec, WM8960_LADC, reg | 0x100);
+       reg = wm8960_read(codec, WM8960_RADC);
+       wm8960_write(codec, WM8960_RADC, reg | 0x100);
+       reg = wm8960_read(codec, WM8960_LDAC);
+       wm8960_write(codec, WM8960_LDAC, reg | 0x100);
+       reg = wm8960_read(codec, WM8960_RDAC);
+       wm8960_write(codec, WM8960_RDAC, reg | 0x100);
+       reg = wm8960_read(codec, WM8960_LOUT1);
+       wm8960_write(codec, WM8960_LOUT1, reg | 0x100);
+       reg = wm8960_read(codec, WM8960_ROUT1);
+       wm8960_write(codec, WM8960_ROUT1, reg | 0x100);
+       reg = wm8960_read(codec, WM8960_LOUT2);
+       wm8960_write(codec, WM8960_LOUT2, reg | 0x100);
+       reg = wm8960_read(codec, WM8960_ROUT2);
+       wm8960_write(codec, WM8960_ROUT2, reg | 0x100);
+
+       wm8960_codec = codec;
+
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+               return ret;
+       }
+
+       ret = snd_soc_register_dai(&wm8960_dai);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+               snd_soc_unregister_codec(codec);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void wm8960_unregister(struct wm8960_priv *wm8960)
+{
+       wm8960_set_bias_level(&wm8960->codec, SND_SOC_BIAS_OFF);
+       snd_soc_unregister_dai(&wm8960_dai);
+       snd_soc_unregister_codec(&wm8960->codec);
+       kfree(wm8960);
+       wm8960_codec = NULL;
+}
+
+static __devinit int wm8960_i2c_probe(struct i2c_client *i2c,
+                                     const struct i2c_device_id *id)
+{
+       struct wm8960_priv *wm8960;
+       struct snd_soc_codec *codec;
+
+       wm8960 = kzalloc(sizeof(struct wm8960_priv), GFP_KERNEL);
+       if (wm8960 == NULL)
+               return -ENOMEM;
+
+       codec = &wm8960->codec;
+       codec->hw_write = (hw_write_t)i2c_master_send;
+
+       i2c_set_clientdata(i2c, wm8960);
+       codec->control_data = i2c;
+
+       codec->dev = &i2c->dev;
+
+       return wm8960_register(wm8960);
+}
+
+static __devexit int wm8960_i2c_remove(struct i2c_client *client)
+{
+       struct wm8960_priv *wm8960 = i2c_get_clientdata(client);
+       wm8960_unregister(wm8960);
+       return 0;
+}
+
+static const struct i2c_device_id wm8960_i2c_id[] = {
+       { "wm8960", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id);
+
+static struct i2c_driver wm8960_i2c_driver = {
+       .driver = {
+               .name = "WM8960 I2C Codec",
+               .owner = THIS_MODULE,
+       },
+       .probe =    wm8960_i2c_probe,
+       .remove =   __devexit_p(wm8960_i2c_remove),
+       .id_table = wm8960_i2c_id,
+};
+
+static int __init wm8960_modinit(void)
+{
+       int ret;
+
+       ret = i2c_add_driver(&wm8960_i2c_driver);
+       if (ret != 0) {
+               printk(KERN_ERR "Failed to register WM8960 I2C driver: %d\n",
+                      ret);
+       }
+
+       return ret;
+}
+module_init(wm8960_modinit);
+
+static void __exit wm8960_exit(void)
+{
+       i2c_del_driver(&wm8960_i2c_driver);
+}
+module_exit(wm8960_exit);
+
+
+MODULE_DESCRIPTION("ASoC WM8960 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8960.h b/sound/soc/codecs/wm8960.h
new file mode 100644 (file)
index 0000000..c9af56c
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * wm8960.h  --  WM8960 Soc Audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8960_H
+#define _WM8960_H
+
+/* WM8960 register space */
+
+
+#define WM8960_CACHEREGNUM     56
+
+#define WM8960_LINVOL          0x0
+#define WM8960_RINVOL          0x1
+#define WM8960_LOUT1           0x2
+#define WM8960_ROUT1           0x3
+#define WM8960_CLOCK1          0x4
+#define WM8960_DACCTL1         0x5
+#define WM8960_DACCTL2         0x6
+#define WM8960_IFACE1          0x7
+#define WM8960_CLOCK2          0x8
+#define WM8960_IFACE2          0x9
+#define WM8960_LDAC            0xa
+#define WM8960_RDAC            0xb
+
+#define WM8960_RESET           0xf
+#define WM8960_3D              0x10
+#define WM8960_ALC1            0x11
+#define WM8960_ALC2            0x12
+#define WM8960_ALC3            0x13
+#define WM8960_NOISEG          0x14
+#define WM8960_LADC            0x15
+#define WM8960_RADC            0x16
+#define WM8960_ADDCTL1         0x17
+#define WM8960_ADDCTL2         0x18
+#define WM8960_POWER1          0x19
+#define WM8960_POWER2          0x1a
+#define WM8960_ADDCTL3         0x1b
+#define WM8960_APOP1           0x1c
+#define WM8960_APOP2           0x1d
+
+#define WM8960_LINPATH         0x20
+#define WM8960_RINPATH         0x21
+#define WM8960_LOUTMIX         0x22
+
+#define WM8960_ROUTMIX         0x25
+#define WM8960_MONOMIX1                0x26
+#define WM8960_MONOMIX2                0x27
+#define WM8960_LOUT2           0x28
+#define WM8960_ROUT2           0x29
+#define WM8960_MONO            0x2a
+#define WM8960_INBMIX1         0x2b
+#define WM8960_INBMIX2         0x2c
+#define WM8960_BYPASS1         0x2d
+#define WM8960_BYPASS2         0x2e
+#define WM8960_POWER3          0x2f
+#define WM8960_ADDCTL4         0x30
+#define WM8960_CLASSD1         0x31
+
+#define WM8960_CLASSD3         0x33
+#define WM8960_PLL1            0x34
+#define WM8960_PLL2            0x35
+#define WM8960_PLL3            0x36
+#define WM8960_PLL4            0x37
+
+
+/*
+ * WM8960 Clock dividers
+ */
+#define WM8960_SYSCLKDIV               0
+#define WM8960_DACDIV                  1
+#define WM8960_OPCLKDIV                        2
+#define WM8960_DCLKDIV                 3
+#define WM8960_TOCLKSEL                        4
+#define WM8960_SYSCLKSEL               5
+
+#define WM8960_SYSCLK_DIV_1            (0 << 1)
+#define WM8960_SYSCLK_DIV_2            (2 << 1)
+
+#define WM8960_SYSCLK_MCLK             (0 << 0)
+#define WM8960_SYSCLK_PLL              (1 << 0)
+
+#define WM8960_DAC_DIV_1               (0 << 3)
+#define WM8960_DAC_DIV_1_5             (1 << 3)
+#define WM8960_DAC_DIV_2               (2 << 3)
+#define WM8960_DAC_DIV_3               (3 << 3)
+#define WM8960_DAC_DIV_4               (4 << 3)
+#define WM8960_DAC_DIV_5_5             (5 << 3)
+#define WM8960_DAC_DIV_6               (6 << 3)
+
+#define WM8960_DCLK_DIV_1_5            (0 << 6)
+#define WM8960_DCLK_DIV_2              (1 << 6)
+#define WM8960_DCLK_DIV_3              (2 << 6)
+#define WM8960_DCLK_DIV_4              (3 << 6)
+#define WM8960_DCLK_DIV_6              (4 << 6)
+#define WM8960_DCLK_DIV_8              (5 << 6)
+#define WM8960_DCLK_DIV_12             (6 << 6)
+#define WM8960_DCLK_DIV_16             (7 << 6)
+
+#define WM8960_TOCLK_F19               (0 << 1)
+#define WM8960_TOCLK_F21               (1 << 1)
+
+#define WM8960_OPCLK_DIV_1             (0 << 0)
+#define WM8960_OPCLK_DIV_2             (1 << 0)
+#define WM8960_OPCLK_DIV_3             (2 << 0)
+#define WM8960_OPCLK_DIV_4             (3 << 0)
+#define WM8960_OPCLK_DIV_5_5           (4 << 0)
+#define WM8960_OPCLK_DIV_6             (5 << 0)
+
+extern struct snd_soc_dai wm8960_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8960;
+
+#define WM8960_DRES_400R 0
+#define WM8960_DRES_200R 1
+#define WM8960_DRES_600R 2
+#define WM8960_DRES_150R 3
+#define WM8960_DRES_MAX  3
+
+struct wm8960_data {
+       int dres;
+};
+
+#endif
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
new file mode 100644 (file)
index 0000000..c05f718
--- /dev/null
@@ -0,0 +1,1097 @@
+/*
+ * wm8988.c -- WM8988 ALSA SoC audio driver
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "wm8988.h"
+
+/*
+ * wm8988 register cache
+ * We can't read the WM8988 register space when we
+ * are using 2 wire for device control, so we cache them instead.
+ */
+static const u16 wm8988_reg[] = {
+       0x0097, 0x0097, 0x0079, 0x0079,  /*  0 */
+       0x0000, 0x0008, 0x0000, 0x000a,  /*  4 */
+       0x0000, 0x0000, 0x00ff, 0x00ff,  /*  8 */
+       0x000f, 0x000f, 0x0000, 0x0000,  /* 12 */
+       0x0000, 0x007b, 0x0000, 0x0032,  /* 16 */
+       0x0000, 0x00c3, 0x00c3, 0x00c0,  /* 20 */
+       0x0000, 0x0000, 0x0000, 0x0000,  /* 24 */
+       0x0000, 0x0000, 0x0000, 0x0000,  /* 28 */
+       0x0000, 0x0000, 0x0050, 0x0050,  /* 32 */
+       0x0050, 0x0050, 0x0050, 0x0050,  /* 36 */
+       0x0079, 0x0079, 0x0079,          /* 40 */
+};
+
+/* codec private data */
+struct wm8988_priv {
+       unsigned int sysclk;
+       struct snd_soc_codec codec;
+       struct snd_pcm_hw_constraint_list *sysclk_constraints;
+       u16 reg_cache[WM8988_NUM_REG];
+};
+
+
+/*
+ * read wm8988 register cache
+ */
+static inline unsigned int wm8988_read_reg_cache(struct snd_soc_codec *codec,
+       unsigned int reg)
+{
+       u16 *cache = codec->reg_cache;
+       if (reg > WM8988_NUM_REG)
+               return -1;
+       return cache[reg];
+}
+
+/*
+ * write wm8988 register cache
+ */
+static inline void wm8988_write_reg_cache(struct snd_soc_codec *codec,
+       unsigned int reg, unsigned int value)
+{
+       u16 *cache = codec->reg_cache;
+       if (reg > WM8988_NUM_REG)
+               return;
+       cache[reg] = value;
+}
+
+static int wm8988_write(struct snd_soc_codec *codec, unsigned int reg,
+       unsigned int value)
+{
+       u8 data[2];
+
+       /* data is
+        *   D15..D9 WM8753 register offset
+        *   D8...D0 register data
+        */
+       data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+       data[1] = value & 0x00ff;
+
+       wm8988_write_reg_cache(codec, reg, value);
+       if (codec->hw_write(codec->control_data, data, 2) == 2)
+               return 0;
+       else
+               return -EIO;
+}
+
+#define wm8988_reset(c)        wm8988_write(c, WM8988_RESET, 0)
+
+/*
+ * WM8988 Controls
+ */
+
+static const char *bass_boost_txt[] = {"Linear Control", "Adaptive Boost"};
+static const struct soc_enum bass_boost =
+       SOC_ENUM_SINGLE(WM8988_BASS, 7, 2, bass_boost_txt);
+
+static const char *bass_filter_txt[] = { "130Hz @ 48kHz", "200Hz @ 48kHz" };
+static const struct soc_enum bass_filter =
+       SOC_ENUM_SINGLE(WM8988_BASS, 6, 2, bass_filter_txt);
+
+static const char *treble_txt[] = {"8kHz", "4kHz"};
+static const struct soc_enum treble =
+       SOC_ENUM_SINGLE(WM8988_TREBLE, 6, 2, treble_txt);
+
+static const char *stereo_3d_lc_txt[] = {"200Hz", "500Hz"};
+static const struct soc_enum stereo_3d_lc =
+       SOC_ENUM_SINGLE(WM8988_3D, 5, 2, stereo_3d_lc_txt);
+
+static const char *stereo_3d_uc_txt[] = {"2.2kHz", "1.5kHz"};
+static const struct soc_enum stereo_3d_uc =
+       SOC_ENUM_SINGLE(WM8988_3D, 6, 2, stereo_3d_uc_txt);
+
+static const char *stereo_3d_func_txt[] = {"Capture", "Playback"};
+static const struct soc_enum stereo_3d_func =
+       SOC_ENUM_SINGLE(WM8988_3D, 7, 2, stereo_3d_func_txt);
+
+static const char *alc_func_txt[] = {"Off", "Right", "Left", "Stereo"};
+static const struct soc_enum alc_func =
+       SOC_ENUM_SINGLE(WM8988_ALC1, 7, 4, alc_func_txt);
+
+static const char *ng_type_txt[] = {"Constant PGA Gain",
+                                   "Mute ADC Output"};
+static const struct soc_enum ng_type =
+       SOC_ENUM_SINGLE(WM8988_NGATE, 1, 2, ng_type_txt);
+
+static const char *deemph_txt[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+static const struct soc_enum deemph =
+       SOC_ENUM_SINGLE(WM8988_ADCDAC, 1, 4, deemph_txt);
+
+static const char *adcpol_txt[] = {"Normal", "L Invert", "R Invert",
+                                  "L + R Invert"};
+static const struct soc_enum adcpol =
+       SOC_ENUM_SINGLE(WM8988_ADCDAC, 5, 4, adcpol_txt);
+
+static const DECLARE_TLV_DB_SCALE(pga_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
+
+static const struct snd_kcontrol_new wm8988_snd_controls[] = {
+
+SOC_ENUM("Bass Boost", bass_boost),
+SOC_ENUM("Bass Filter", bass_filter),
+SOC_SINGLE("Bass Volume", WM8988_BASS, 0, 15, 1),
+
+SOC_SINGLE("Treble Volume", WM8988_TREBLE, 0, 15, 0),
+SOC_ENUM("Treble Cut-off", treble),
+
+SOC_SINGLE("3D Switch", WM8988_3D, 0, 1, 0),
+SOC_SINGLE("3D Volume", WM8988_3D, 1, 15, 0),
+SOC_ENUM("3D Lower Cut-off", stereo_3d_lc),
+SOC_ENUM("3D Upper Cut-off", stereo_3d_uc),
+SOC_ENUM("3D Mode", stereo_3d_func),
+
+SOC_SINGLE("ALC Capture Target Volume", WM8988_ALC1, 0, 7, 0),
+SOC_SINGLE("ALC Capture Max Volume", WM8988_ALC1, 4, 7, 0),
+SOC_ENUM("ALC Capture Function", alc_func),
+SOC_SINGLE("ALC Capture ZC Switch", WM8988_ALC2, 7, 1, 0),
+SOC_SINGLE("ALC Capture Hold Time", WM8988_ALC2, 0, 15, 0),
+SOC_SINGLE("ALC Capture Decay Time", WM8988_ALC3, 4, 15, 0),
+SOC_SINGLE("ALC Capture Attack Time", WM8988_ALC3, 0, 15, 0),
+SOC_SINGLE("ALC Capture NG Threshold", WM8988_NGATE, 3, 31, 0),
+SOC_ENUM("ALC Capture NG Type", ng_type),
+SOC_SINGLE("ALC Capture NG Switch", WM8988_NGATE, 0, 1, 0),
+
+SOC_SINGLE("ZC Timeout Switch", WM8988_ADCTL1, 0, 1, 0),
+
+SOC_DOUBLE_R_TLV("Capture Digital Volume", WM8988_LADC, WM8988_RADC,
+                0, 255, 0, adc_tlv),
+SOC_DOUBLE_R_TLV("Capture Volume", WM8988_LINVOL, WM8988_RINVOL,
+                0, 63, 0, pga_tlv),
+SOC_DOUBLE_R("Capture ZC Switch", WM8988_LINVOL, WM8988_RINVOL, 6, 1, 0),
+SOC_DOUBLE_R("Capture Switch", WM8988_LINVOL, WM8988_RINVOL, 7, 1, 1),
+
+SOC_ENUM("Playback De-emphasis", deemph),
+
+SOC_ENUM("Capture Polarity", adcpol),
+SOC_SINGLE("Playback 6dB Attenuate", WM8988_ADCDAC, 7, 1, 0),
+SOC_SINGLE("Capture 6dB Attenuate", WM8988_ADCDAC, 8, 1, 0),
+
+SOC_DOUBLE_R_TLV("PCM Volume", WM8988_LDAC, WM8988_RDAC, 0, 255, 0, dac_tlv),
+
+SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", WM8988_LOUTM1, 4, 7, 1,
+              bypass_tlv),
+SOC_SINGLE_TLV("Left Mixer Right Bypass Volume", WM8988_LOUTM2, 4, 7, 1,
+              bypass_tlv),
+SOC_SINGLE_TLV("Right Mixer Left Bypass Volume", WM8988_ROUTM1, 4, 7, 1,
+              bypass_tlv),
+SOC_SINGLE_TLV("Right Mixer Right Bypass Volume", WM8988_ROUTM2, 4, 7, 1,
+              bypass_tlv),
+
+SOC_DOUBLE_R("Output 1 Playback ZC Switch", WM8988_LOUT1V,
+            WM8988_ROUT1V, 7, 1, 0),
+SOC_DOUBLE_R_TLV("Output 1 Playback Volume", WM8988_LOUT1V, WM8988_ROUT1V,
+                0, 127, 0, out_tlv),
+
+SOC_DOUBLE_R("Output 2 Playback ZC Switch", WM8988_LOUT2V,
+            WM8988_ROUT2V, 7, 1, 0),
+SOC_DOUBLE_R_TLV("Output 2 Playback Volume", WM8988_LOUT2V, WM8988_ROUT2V,
+                0, 127, 0, out_tlv),
+
+};
+
+/*
+ * DAPM Controls
+ */
+
+static int wm8988_lrc_control(struct snd_soc_dapm_widget *w,
+                             struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       u16 adctl2 = wm8988_read_reg_cache(codec, WM8988_ADCTL2);
+
+       /* Use the DAC to gate LRC if active, otherwise use ADC */
+       if (wm8988_read_reg_cache(codec, WM8988_PWR2) & 0x180)
+               adctl2 &= ~0x4;
+       else
+               adctl2 |= 0x4;
+
+       return wm8988_write(codec, WM8988_ADCTL2, adctl2);
+}
+
+static const char *wm8988_line_texts[] = {
+       "Line 1", "Line 2", "PGA", "Differential"};
+
+static const unsigned int wm8988_line_values[] = {
+       0, 1, 3, 4};
+
+static const struct soc_enum wm8988_lline_enum =
+       SOC_VALUE_ENUM_SINGLE(WM8988_LOUTM1, 0, 7,
+                             ARRAY_SIZE(wm8988_line_texts),
+                             wm8988_line_texts,
+                             wm8988_line_values);
+static const struct snd_kcontrol_new wm8988_left_line_controls =
+       SOC_DAPM_VALUE_ENUM("Route", wm8988_lline_enum);
+
+static const struct soc_enum wm8988_rline_enum =
+       SOC_VALUE_ENUM_SINGLE(WM8988_ROUTM1, 0, 7,
+                             ARRAY_SIZE(wm8988_line_texts),
+                             wm8988_line_texts,
+                             wm8988_line_values);
+static const struct snd_kcontrol_new wm8988_right_line_controls =
+       SOC_DAPM_VALUE_ENUM("Route", wm8988_lline_enum);
+
+/* Left Mixer */
+static const struct snd_kcontrol_new wm8988_left_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Playback Switch", WM8988_LOUTM1, 8, 1, 0),
+       SOC_DAPM_SINGLE("Left Bypass Switch", WM8988_LOUTM1, 7, 1, 0),
+       SOC_DAPM_SINGLE("Right Playback Switch", WM8988_LOUTM2, 8, 1, 0),
+       SOC_DAPM_SINGLE("Right Bypass Switch", WM8988_LOUTM2, 7, 1, 0),
+};
+
+/* Right Mixer */
+static const struct snd_kcontrol_new wm8988_right_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left Playback Switch", WM8988_ROUTM1, 8, 1, 0),
+       SOC_DAPM_SINGLE("Left Bypass Switch", WM8988_ROUTM1, 7, 1, 0),
+       SOC_DAPM_SINGLE("Playback Switch", WM8988_ROUTM2, 8, 1, 0),
+       SOC_DAPM_SINGLE("Right Bypass Switch", WM8988_ROUTM2, 7, 1, 0),
+};
+
+static const char *wm8988_pga_sel[] = {"Line 1", "Line 2", "Differential"};
+static const unsigned int wm8988_pga_val[] = { 0, 1, 3 };
+
+/* Left PGA Mux */
+static const struct soc_enum wm8988_lpga_enum =
+       SOC_VALUE_ENUM_SINGLE(WM8988_LADCIN, 6, 3,
+                             ARRAY_SIZE(wm8988_pga_sel),
+                             wm8988_pga_sel,
+                             wm8988_pga_val);
+static const struct snd_kcontrol_new wm8988_left_pga_controls =
+       SOC_DAPM_VALUE_ENUM("Route", wm8988_lpga_enum);
+
+/* Right PGA Mux */
+static const struct soc_enum wm8988_rpga_enum =
+       SOC_VALUE_ENUM_SINGLE(WM8988_RADCIN, 6, 3,
+                             ARRAY_SIZE(wm8988_pga_sel),
+                             wm8988_pga_sel,
+                             wm8988_pga_val);
+static const struct snd_kcontrol_new wm8988_right_pga_controls =
+       SOC_DAPM_VALUE_ENUM("Route", wm8988_rpga_enum);
+
+/* Differential Mux */
+static const char *wm8988_diff_sel[] = {"Line 1", "Line 2"};
+static const struct soc_enum diffmux =
+       SOC_ENUM_SINGLE(WM8988_ADCIN, 8, 2, wm8988_diff_sel);
+static const struct snd_kcontrol_new wm8988_diffmux_controls =
+       SOC_DAPM_ENUM("Route", diffmux);
+
+/* Mono ADC Mux */
+static const char *wm8988_mono_mux[] = {"Stereo", "Mono (Left)",
+       "Mono (Right)", "Digital Mono"};
+static const struct soc_enum monomux =
+       SOC_ENUM_SINGLE(WM8988_ADCIN, 6, 4, wm8988_mono_mux);
+static const struct snd_kcontrol_new wm8988_monomux_controls =
+       SOC_DAPM_ENUM("Route", monomux);
+
+static const struct snd_soc_dapm_widget wm8988_dapm_widgets[] = {
+       SND_SOC_DAPM_MICBIAS("Mic Bias", WM8988_PWR1, 1, 0),
+
+       SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
+               &wm8988_diffmux_controls),
+       SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
+               &wm8988_monomux_controls),
+       SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
+               &wm8988_monomux_controls),
+
+       SND_SOC_DAPM_MUX("Left PGA Mux", WM8988_PWR1, 5, 0,
+               &wm8988_left_pga_controls),
+       SND_SOC_DAPM_MUX("Right PGA Mux", WM8988_PWR1, 4, 0,
+               &wm8988_right_pga_controls),
+
+       SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
+               &wm8988_left_line_controls),
+       SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
+               &wm8988_right_line_controls),
+
+       SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8988_PWR1, 2, 0),
+       SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8988_PWR1, 3, 0),
+
+       SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8988_PWR2, 7, 0),
+       SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8988_PWR2, 8, 0),
+
+       SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+               &wm8988_left_mixer_controls[0],
+               ARRAY_SIZE(wm8988_left_mixer_controls)),
+       SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+               &wm8988_right_mixer_controls[0],
+               ARRAY_SIZE(wm8988_right_mixer_controls)),
+
+       SND_SOC_DAPM_PGA("Right Out 2", WM8988_PWR2, 3, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Left Out 2", WM8988_PWR2, 4, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Right Out 1", WM8988_PWR2, 5, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Left Out 1", WM8988_PWR2, 6, 0, NULL, 0),
+
+       SND_SOC_DAPM_POST("LRC control", wm8988_lrc_control),
+
+       SND_SOC_DAPM_OUTPUT("LOUT1"),
+       SND_SOC_DAPM_OUTPUT("ROUT1"),
+       SND_SOC_DAPM_OUTPUT("LOUT2"),
+       SND_SOC_DAPM_OUTPUT("ROUT2"),
+       SND_SOC_DAPM_OUTPUT("VREF"),
+
+       SND_SOC_DAPM_INPUT("LINPUT1"),
+       SND_SOC_DAPM_INPUT("LINPUT2"),
+       SND_SOC_DAPM_INPUT("RINPUT1"),
+       SND_SOC_DAPM_INPUT("RINPUT2"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+
+       { "Left Line Mux", "Line 1", "LINPUT1" },
+       { "Left Line Mux", "Line 2", "LINPUT2" },
+       { "Left Line Mux", "PGA", "Left PGA Mux" },
+       { "Left Line Mux", "Differential", "Differential Mux" },
+
+       { "Right Line Mux", "Line 1", "RINPUT1" },
+       { "Right Line Mux", "Line 2", "RINPUT2" },
+       { "Right Line Mux", "PGA", "Right PGA Mux" },
+       { "Right Line Mux", "Differential", "Differential Mux" },
+
+       { "Left PGA Mux", "Line 1", "LINPUT1" },
+       { "Left PGA Mux", "Line 2", "LINPUT2" },
+       { "Left PGA Mux", "Differential", "Differential Mux" },
+
+       { "Right PGA Mux", "Line 1", "RINPUT1" },
+       { "Right PGA Mux", "Line 2", "RINPUT2" },
+       { "Right PGA Mux", "Differential", "Differential Mux" },
+
+       { "Differential Mux", "Line 1", "LINPUT1" },
+       { "Differential Mux", "Line 1", "RINPUT1" },
+       { "Differential Mux", "Line 2", "LINPUT2" },
+       { "Differential Mux", "Line 2", "RINPUT2" },
+
+       { "Left ADC Mux", "Stereo", "Left PGA Mux" },
+       { "Left ADC Mux", "Mono (Left)", "Left PGA Mux" },
+       { "Left ADC Mux", "Digital Mono", "Left PGA Mux" },
+
+       { "Right ADC Mux", "Stereo", "Right PGA Mux" },
+       { "Right ADC Mux", "Mono (Right)", "Right PGA Mux" },
+       { "Right ADC Mux", "Digital Mono", "Right PGA Mux" },
+
+       { "Left ADC", NULL, "Left ADC Mux" },
+       { "Right ADC", NULL, "Right ADC Mux" },
+
+       { "Left Line Mux", "Line 1", "LINPUT1" },
+       { "Left Line Mux", "Line 2", "LINPUT2" },
+       { "Left Line Mux", "PGA", "Left PGA Mux" },
+       { "Left Line Mux", "Differential", "Differential Mux" },
+
+       { "Right Line Mux", "Line 1", "RINPUT1" },
+       { "Right Line Mux", "Line 2", "RINPUT2" },
+       { "Right Line Mux", "PGA", "Right PGA Mux" },
+       { "Right Line Mux", "Differential", "Differential Mux" },
+
+       { "Left Mixer", "Playback Switch", "Left DAC" },
+       { "Left Mixer", "Left Bypass Switch", "Left Line Mux" },
+       { "Left Mixer", "Right Playback Switch", "Right DAC" },
+       { "Left Mixer", "Right Bypass Switch", "Right Line Mux" },
+
+       { "Right Mixer", "Left Playback Switch", "Left DAC" },
+       { "Right Mixer", "Left Bypass Switch", "Left Line Mux" },
+       { "Right Mixer", "Playback Switch", "Right DAC" },
+       { "Right Mixer", "Right Bypass Switch", "Right Line Mux" },
+
+       { "Left Out 1", NULL, "Left Mixer" },
+       { "LOUT1", NULL, "Left Out 1" },
+       { "Right Out 1", NULL, "Right Mixer" },
+       { "ROUT1", NULL, "Right Out 1" },
+
+       { "Left Out 2", NULL, "Left Mixer" },
+       { "LOUT2", NULL, "Left Out 2" },
+       { "Right Out 2", NULL, "Right Mixer" },
+       { "ROUT2", NULL, "Right Out 2" },
+};
+
+struct _coeff_div {
+       u32 mclk;
+       u32 rate;
+       u16 fs;
+       u8 sr:5;
+       u8 usb:1;
+};
+
+/* codec hifi mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+       /* 8k */
+       {12288000, 8000, 1536, 0x6, 0x0},
+       {11289600, 8000, 1408, 0x16, 0x0},
+       {18432000, 8000, 2304, 0x7, 0x0},
+       {16934400, 8000, 2112, 0x17, 0x0},
+       {12000000, 8000, 1500, 0x6, 0x1},
+
+       /* 11.025k */
+       {11289600, 11025, 1024, 0x18, 0x0},
+       {16934400, 11025, 1536, 0x19, 0x0},
+       {12000000, 11025, 1088, 0x19, 0x1},
+
+       /* 16k */
+       {12288000, 16000, 768, 0xa, 0x0},
+       {18432000, 16000, 1152, 0xb, 0x0},
+       {12000000, 16000, 750, 0xa, 0x1},
+
+       /* 22.05k */
+       {11289600, 22050, 512, 0x1a, 0x0},
+       {16934400, 22050, 768, 0x1b, 0x0},
+       {12000000, 22050, 544, 0x1b, 0x1},
+
+       /* 32k */
+       {12288000, 32000, 384, 0xc, 0x0},
+       {18432000, 32000, 576, 0xd, 0x0},
+       {12000000, 32000, 375, 0xa, 0x1},
+
+       /* 44.1k */
+       {11289600, 44100, 256, 0x10, 0x0},
+       {16934400, 44100, 384, 0x11, 0x0},
+       {12000000, 44100, 272, 0x11, 0x1},
+
+       /* 48k */
+       {12288000, 48000, 256, 0x0, 0x0},
+       {18432000, 48000, 384, 0x1, 0x0},
+       {12000000, 48000, 250, 0x0, 0x1},
+
+       /* 88.2k */
+       {11289600, 88200, 128, 0x1e, 0x0},
+       {16934400, 88200, 192, 0x1f, 0x0},
+       {12000000, 88200, 136, 0x1f, 0x1},
+
+       /* 96k */
+       {12288000, 96000, 128, 0xe, 0x0},
+       {18432000, 96000, 192, 0xf, 0x0},
+       {12000000, 96000, 125, 0xe, 0x1},
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+               if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+                       return i;
+       }
+
+       return -EINVAL;
+}
+
+/* The set of rates we can generate from the above for each SYSCLK */
+
+static unsigned int rates_12288[] = {
+       8000, 12000, 16000, 24000, 24000, 32000, 48000, 96000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_12288 = {
+       .count  = ARRAY_SIZE(rates_12288),
+       .list   = rates_12288,
+};
+
+static unsigned int rates_112896[] = {
+       8000, 11025, 22050, 44100,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_112896 = {
+       .count  = ARRAY_SIZE(rates_112896),
+       .list   = rates_112896,
+};
+
+static unsigned int rates_12[] = {
+       8000, 11025, 12000, 16000, 22050, 2400, 32000, 41100, 48000,
+       48000, 88235, 96000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_12 = {
+       .count  = ARRAY_SIZE(rates_12),
+       .list   = rates_12,
+};
+
+/*
+ * Note that this should be called from init rather than from hw_params.
+ */
+static int wm8988_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+               int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct wm8988_priv *wm8988 = codec->private_data;
+
+       switch (freq) {
+       case 11289600:
+       case 18432000:
+       case 22579200:
+       case 36864000:
+               wm8988->sysclk_constraints = &constraints_112896;
+               wm8988->sysclk = freq;
+               return 0;
+
+       case 12288000:
+       case 16934400:
+       case 24576000:
+       case 33868800:
+               wm8988->sysclk_constraints = &constraints_12288;
+               wm8988->sysclk = freq;
+               return 0;
+
+       case 12000000:
+       case 24000000:
+               wm8988->sysclk_constraints = &constraints_12;
+               wm8988->sysclk = freq;
+               return 0;
+       }
+       return -EINVAL;
+}
+
+static int wm8988_set_dai_fmt(struct snd_soc_dai *codec_dai,
+               unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u16 iface = 0;
+
+       /* set master/slave audio interface */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               iface = 0x0040;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* interface format */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               iface |= 0x0002;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               iface |= 0x0001;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               iface |= 0x0003;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               iface |= 0x0013;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* clock inversion */
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               iface |= 0x0090;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               iface |= 0x0080;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               iface |= 0x0010;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       wm8988_write(codec, WM8988_IFACE, iface);
+       return 0;
+}
+
+static int wm8988_pcm_startup(struct snd_pcm_substream *substream,
+                             struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct wm8988_priv *wm8988 = codec->private_data;
+
+       /* The set of sample rates that can be supported depends on the
+        * MCLK supplied to the CODEC - enforce this.
+        */
+       if (!wm8988->sysclk) {
+               dev_err(codec->dev,
+                       "No MCLK configured, call set_sysclk() on init\n");
+               return -EINVAL;
+       }
+
+       snd_pcm_hw_constraint_list(substream->runtime, 0,
+                                  SNDRV_PCM_HW_PARAM_RATE,
+                                  wm8988->sysclk_constraints);
+
+       return 0;
+}
+
+static int wm8988_pcm_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       struct wm8988_priv *wm8988 = codec->private_data;
+       u16 iface = wm8988_read_reg_cache(codec, WM8988_IFACE) & 0x1f3;
+       u16 srate = wm8988_read_reg_cache(codec, WM8988_SRATE) & 0x180;
+       int coeff;
+
+       coeff = get_coeff(wm8988->sysclk, params_rate(params));
+       if (coeff < 0) {
+               coeff = get_coeff(wm8988->sysclk / 2, params_rate(params));
+               srate |= 0x40;
+       }
+       if (coeff < 0) {
+               dev_err(codec->dev,
+                       "Unable to configure sample rate %dHz with %dHz MCLK\n",
+                       params_rate(params), wm8988->sysclk);
+               return coeff;
+       }
+
+       /* bit size */
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               iface |= 0x0004;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               iface |= 0x0008;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               iface |= 0x000c;
+               break;
+       }
+
+       /* set iface & srate */
+       wm8988_write(codec, WM8988_IFACE, iface);
+       if (coeff >= 0)
+               wm8988_write(codec, WM8988_SRATE, srate |
+                       (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
+
+       return 0;
+}
+
+static int wm8988_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u16 mute_reg = wm8988_read_reg_cache(codec, WM8988_ADCDAC) & 0xfff7;
+
+       if (mute)
+               wm8988_write(codec, WM8988_ADCDAC, mute_reg | 0x8);
+       else
+               wm8988_write(codec, WM8988_ADCDAC, mute_reg);
+       return 0;
+}
+
+static int wm8988_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       u16 pwr_reg = wm8988_read_reg_cache(codec, WM8988_PWR1) & ~0x1c1;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               /* VREF, VMID=2x50k, digital enabled */
+               wm8988_write(codec, WM8988_PWR1, pwr_reg | 0x00c0);
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->bias_level == SND_SOC_BIAS_OFF) {
+                       /* VREF, VMID=2x5k */
+                       wm8988_write(codec, WM8988_PWR1, pwr_reg | 0x1c1);
+
+                       /* Charge caps */
+                       msleep(100);
+               }
+
+               /* VREF, VMID=2*500k, digital stopped */
+               wm8988_write(codec, WM8988_PWR1, pwr_reg | 0x0141);
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               wm8988_write(codec, WM8988_PWR1, 0x0000);
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+#define WM8988_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8988_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+       SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8988_ops = {
+       .startup = wm8988_pcm_startup,
+       .hw_params = wm8988_pcm_hw_params,
+       .set_fmt = wm8988_set_dai_fmt,
+       .set_sysclk = wm8988_set_dai_sysclk,
+       .digital_mute = wm8988_mute,
+};
+
+struct snd_soc_dai wm8988_dai = {
+       .name = "WM8988",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = WM8988_RATES,
+               .formats = WM8988_FORMATS,
+       },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = WM8988_RATES,
+               .formats = WM8988_FORMATS,
+        },
+       .ops = &wm8988_ops,
+       .symmetric_rates = 1,
+};
+EXPORT_SYMBOL_GPL(wm8988_dai);
+
+static int wm8988_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       wm8988_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int wm8988_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+       int i;
+       u8 data[2];
+       u16 *cache = codec->reg_cache;
+
+       /* Sync reg_cache with the hardware */
+       for (i = 0; i < WM8988_NUM_REG; i++) {
+               if (i == WM8988_RESET)
+                       continue;
+               data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+               data[1] = cache[i] & 0x00ff;
+               codec->hw_write(codec->control_data, data, 2);
+       }
+
+       wm8988_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+}
+
+static struct snd_soc_codec *wm8988_codec;
+
+static int wm8988_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       int ret = 0;
+
+       if (wm8988_codec == NULL) {
+               dev_err(&pdev->dev, "Codec device not registered\n");
+               return -ENODEV;
+       }
+
+       socdev->card->codec = wm8988_codec;
+       codec = wm8988_codec;
+
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+               goto pcm_err;
+       }
+
+       snd_soc_add_controls(codec, wm8988_snd_controls,
+                               ARRAY_SIZE(wm8988_snd_controls));
+       snd_soc_dapm_new_controls(codec, wm8988_dapm_widgets,
+                                 ARRAY_SIZE(wm8988_dapm_widgets));
+       snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+       snd_soc_dapm_new_widgets(codec);
+
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to register card: %d\n", ret);
+               goto card_err;
+       }
+
+       return ret;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+pcm_err:
+       return ret;
+}
+
+static int wm8988_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+       return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8988 = {
+       .probe =        wm8988_probe,
+       .remove =       wm8988_remove,
+       .suspend =      wm8988_suspend,
+       .resume =       wm8988_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8988);
+
+static int wm8988_register(struct wm8988_priv *wm8988)
+{
+       struct snd_soc_codec *codec = &wm8988->codec;
+       int ret;
+       u16 reg;
+
+       if (wm8988_codec) {
+               dev_err(codec->dev, "Another WM8988 is registered\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->private_data = wm8988;
+       codec->name = "WM8988";
+       codec->owner = THIS_MODULE;
+       codec->read = wm8988_read_reg_cache;
+       codec->write = wm8988_write;
+       codec->dai = &wm8988_dai;
+       codec->num_dai = 1;
+       codec->reg_cache_size = ARRAY_SIZE(wm8988->reg_cache);
+       codec->reg_cache = &wm8988->reg_cache;
+       codec->bias_level = SND_SOC_BIAS_OFF;
+       codec->set_bias_level = wm8988_set_bias_level;
+
+       memcpy(codec->reg_cache, wm8988_reg,
+              sizeof(wm8988_reg));
+
+       ret = wm8988_reset(codec);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to issue reset\n");
+               return ret;
+       }
+
+       /* set the update bits (we always update left then right) */
+       reg = wm8988_read_reg_cache(codec, WM8988_RADC);
+       wm8988_write(codec, WM8988_RADC, reg | 0x100);
+       reg = wm8988_read_reg_cache(codec, WM8988_RDAC);
+       wm8988_write(codec, WM8988_RDAC, reg | 0x0100);
+       reg = wm8988_read_reg_cache(codec, WM8988_ROUT1V);
+       wm8988_write(codec, WM8988_ROUT1V, reg | 0x0100);
+       reg = wm8988_read_reg_cache(codec, WM8988_ROUT2V);
+       wm8988_write(codec, WM8988_ROUT2V, reg | 0x0100);
+       reg = wm8988_read_reg_cache(codec, WM8988_RINVOL);
+       wm8988_write(codec, WM8988_RINVOL, reg | 0x0100);
+
+       wm8988_set_bias_level(&wm8988->codec, SND_SOC_BIAS_STANDBY);
+
+       wm8988_dai.dev = codec->dev;
+
+       wm8988_codec = codec;
+
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+               return ret;
+       }
+
+       ret = snd_soc_register_dai(&wm8988_dai);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+               snd_soc_unregister_codec(codec);
+               return ret;
+       }
+
+       return 0;
+
+err:
+       kfree(wm8988);
+       return ret;
+}
+
+static void wm8988_unregister(struct wm8988_priv *wm8988)
+{
+       wm8988_set_bias_level(&wm8988->codec, SND_SOC_BIAS_OFF);
+       snd_soc_unregister_dai(&wm8988_dai);
+       snd_soc_unregister_codec(&wm8988->codec);
+       kfree(wm8988);
+       wm8988_codec = NULL;
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static int wm8988_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       struct wm8988_priv *wm8988;
+       struct snd_soc_codec *codec;
+
+       wm8988 = kzalloc(sizeof(struct wm8988_priv), GFP_KERNEL);
+       if (wm8988 == NULL)
+               return -ENOMEM;
+
+       codec = &wm8988->codec;
+       codec->hw_write = (hw_write_t)i2c_master_send;
+
+       i2c_set_clientdata(i2c, wm8988);
+       codec->control_data = i2c;
+
+       codec->dev = &i2c->dev;
+
+       return wm8988_register(wm8988);
+}
+
+static int wm8988_i2c_remove(struct i2c_client *client)
+{
+       struct wm8988_priv *wm8988 = i2c_get_clientdata(client);
+       wm8988_unregister(wm8988);
+       return 0;
+}
+
+static const struct i2c_device_id wm8988_i2c_id[] = {
+       { "wm8988", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8988_i2c_id);
+
+static struct i2c_driver wm8988_i2c_driver = {
+       .driver = {
+               .name = "WM8988",
+               .owner = THIS_MODULE,
+       },
+       .probe = wm8988_i2c_probe,
+       .remove = wm8988_i2c_remove,
+       .id_table = wm8988_i2c_id,
+};
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+static int wm8988_spi_write(struct spi_device *spi, const char *data, int len)
+{
+       struct spi_transfer t;
+       struct spi_message m;
+       u8 msg[2];
+
+       if (len <= 0)
+               return 0;
+
+       msg[0] = data[0];
+       msg[1] = data[1];
+
+       spi_message_init(&m);
+       memset(&t, 0, (sizeof t));
+
+       t.tx_buf = &msg[0];
+       t.len = len;
+
+       spi_message_add_tail(&t, &m);
+       spi_sync(spi, &m);
+
+       return len;
+}
+
+static int __devinit wm8988_spi_probe(struct spi_device *spi)
+{
+       struct wm8988_priv *wm8988;
+       struct snd_soc_codec *codec;
+
+       wm8988 = kzalloc(sizeof(struct wm8988_priv), GFP_KERNEL);
+       if (wm8988 == NULL)
+               return -ENOMEM;
+
+       codec = &wm8988->codec;
+       codec->hw_write = (hw_write_t)wm8988_spi_write;
+       codec->control_data = spi;
+       codec->dev = &spi->dev;
+
+       spi->dev.driver_data = wm8988;
+
+       return wm8988_register(wm8988);
+}
+
+static int __devexit wm8988_spi_remove(struct spi_device *spi)
+{
+       struct wm8988_priv *wm8988 = spi->dev.driver_data;
+
+       wm8988_unregister(wm8988);
+
+       return 0;
+}
+
+static struct spi_driver wm8988_spi_driver = {
+       .driver = {
+               .name   = "wm8988",
+               .bus    = &spi_bus_type,
+               .owner  = THIS_MODULE,
+       },
+       .probe          = wm8988_spi_probe,
+       .remove         = __devexit_p(wm8988_spi_remove),
+};
+#endif
+
+static int __init wm8988_modinit(void)
+{
+       int ret;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       ret = i2c_add_driver(&wm8988_i2c_driver);
+       if (ret != 0)
+               pr_err("WM8988: Unable to register I2C driver: %d\n", ret);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+       ret = spi_register_driver(&wm8988_spi_driver);
+       if (ret != 0)
+               pr_err("WM8988: Unable to register SPI driver: %d\n", ret);
+#endif
+       return ret;
+}
+module_init(wm8988_modinit);
+
+static void __exit wm8988_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       i2c_del_driver(&wm8988_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+       spi_unregister_driver(&wm8988_spi_driver);
+#endif
+}
+module_exit(wm8988_exit);
+
+
+MODULE_DESCRIPTION("ASoC WM8988 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8988.h b/sound/soc/codecs/wm8988.h
new file mode 100644 (file)
index 0000000..4552d37
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on WM8753.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef _WM8988_H
+#define _WM8988_H
+
+/* WM8988 register space */
+
+#define WM8988_LINVOL    0x00
+#define WM8988_RINVOL    0x01
+#define WM8988_LOUT1V    0x02
+#define WM8988_ROUT1V    0x03
+#define WM8988_ADCDAC    0x05
+#define WM8988_IFACE     0x07
+#define WM8988_SRATE     0x08
+#define WM8988_LDAC      0x0a
+#define WM8988_RDAC      0x0b
+#define WM8988_BASS      0x0c
+#define WM8988_TREBLE    0x0d
+#define WM8988_RESET     0x0f
+#define WM8988_3D        0x10
+#define WM8988_ALC1      0x11
+#define WM8988_ALC2      0x12
+#define WM8988_ALC3      0x13
+#define WM8988_NGATE     0x14
+#define WM8988_LADC      0x15
+#define WM8988_RADC      0x16
+#define WM8988_ADCTL1    0x17
+#define WM8988_ADCTL2    0x18
+#define WM8988_PWR1      0x19
+#define WM8988_PWR2      0x1a
+#define WM8988_ADCTL3    0x1b
+#define WM8988_ADCIN     0x1f
+#define WM8988_LADCIN    0x20
+#define WM8988_RADCIN    0x21
+#define WM8988_LOUTM1    0x22
+#define WM8988_LOUTM2    0x23
+#define WM8988_ROUTM1    0x24
+#define WM8988_ROUTM2    0x25
+#define WM8988_LOUT2V    0x28
+#define WM8988_ROUT2V    0x29
+#define WM8988_LPPB      0x43
+#define WM8988_NUM_REG   0x44
+
+#define WM8988_SYSCLK  0
+
+extern struct snd_soc_dai wm8988_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8988;
+
+#endif
index 40cd274eb1ef51f60588ffaf7ba09e6c44f10a1f..d029818350e9fe9d5778c3fbb4087b0245d58023 100644 (file)
@@ -998,7 +998,7 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int target,
 
        if ((Ndiv < 6) || (Ndiv > 12))
                printk(KERN_WARNING
-               "WM8990 N value outwith recommended range! N = %d\n", Ndiv);
+               "WM8990 N value outwith recommended range! N = %u\n", Ndiv);
 
        pll_div->n = Ndiv;
        Nmod = target % source;
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
new file mode 100644 (file)
index 0000000..86fc57e
--- /dev/null
@@ -0,0 +1,1534 @@
+/*
+ * wm9081.c  --  WM9081 ALSA SoC Audio driver
+ *
+ * Author: Mark Brown
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include <sound/wm9081.h>
+#include "wm9081.h"
+
+static u16 wm9081_reg_defaults[] = {
+       0x0000,     /* R0  - Software Reset */
+       0x0000,     /* R1 */
+       0x00B9,     /* R2  - Analogue Lineout */
+       0x00B9,     /* R3  - Analogue Speaker PGA */
+       0x0001,     /* R4  - VMID Control */
+       0x0068,     /* R5  - Bias Control 1 */
+       0x0000,     /* R6 */
+       0x0000,     /* R7  - Analogue Mixer */
+       0x0000,     /* R8  - Anti Pop Control */
+       0x01DB,     /* R9  - Analogue Speaker 1 */
+       0x0018,     /* R10 - Analogue Speaker 2 */
+       0x0180,     /* R11 - Power Management */
+       0x0000,     /* R12 - Clock Control 1 */
+       0x0038,     /* R13 - Clock Control 2 */
+       0x4000,     /* R14 - Clock Control 3 */
+       0x0000,     /* R15 */
+       0x0000,     /* R16 - FLL Control 1 */
+       0x0200,     /* R17 - FLL Control 2 */
+       0x0000,     /* R18 - FLL Control 3 */
+       0x0204,     /* R19 - FLL Control 4 */
+       0x0000,     /* R20 - FLL Control 5 */
+       0x0000,     /* R21 */
+       0x0000,     /* R22 - Audio Interface 1 */
+       0x0002,     /* R23 - Audio Interface 2 */
+       0x0008,     /* R24 - Audio Interface 3 */
+       0x0022,     /* R25 - Audio Interface 4 */
+       0x0000,     /* R26 - Interrupt Status */
+       0x0006,     /* R27 - Interrupt Status Mask */
+       0x0000,     /* R28 - Interrupt Polarity */
+       0x0000,     /* R29 - Interrupt Control */
+       0x00C0,     /* R30 - DAC Digital 1 */
+       0x0008,     /* R31 - DAC Digital 2 */
+       0x09AF,     /* R32 - DRC 1 */
+       0x4201,     /* R33 - DRC 2 */
+       0x0000,     /* R34 - DRC 3 */
+       0x0000,     /* R35 - DRC 4 */
+       0x0000,     /* R36 */
+       0x0000,     /* R37 */
+       0x0000,     /* R38 - Write Sequencer 1 */
+       0x0000,     /* R39 - Write Sequencer 2 */
+       0x0002,     /* R40 - MW Slave 1 */
+       0x0000,     /* R41 */
+       0x0000,     /* R42 - EQ 1 */
+       0x0000,     /* R43 - EQ 2 */
+       0x0FCA,     /* R44 - EQ 3 */
+       0x0400,     /* R45 - EQ 4 */
+       0x00B8,     /* R46 - EQ 5 */
+       0x1EB5,     /* R47 - EQ 6 */
+       0xF145,     /* R48 - EQ 7 */
+       0x0B75,     /* R49 - EQ 8 */
+       0x01C5,     /* R50 - EQ 9 */
+       0x169E,     /* R51 - EQ 10 */
+       0xF829,     /* R52 - EQ 11 */
+       0x07AD,     /* R53 - EQ 12 */
+       0x1103,     /* R54 - EQ 13 */
+       0x1C58,     /* R55 - EQ 14 */
+       0xF373,     /* R56 - EQ 15 */
+       0x0A54,     /* R57 - EQ 16 */
+       0x0558,     /* R58 - EQ 17 */
+       0x0564,     /* R59 - EQ 18 */
+       0x0559,     /* R60 - EQ 19 */
+       0x4000,     /* R61 - EQ 20 */
+};
+
+static struct {
+       int ratio;
+       int clk_sys_rate;
+} clk_sys_rates[] = {
+       { 64,   0 },
+       { 128,  1 },
+       { 192,  2 },
+       { 256,  3 },
+       { 384,  4 },
+       { 512,  5 },
+       { 768,  6 },
+       { 1024, 7 },
+       { 1408, 8 },
+       { 1536, 9 },
+};
+
+static struct {
+       int rate;
+       int sample_rate;
+} sample_rates[] = {
+       { 8000,  0  },
+       { 11025, 1  },
+       { 12000, 2  },
+       { 16000, 3  },
+       { 22050, 4  },
+       { 24000, 5  },
+       { 32000, 6  },
+       { 44100, 7  },
+       { 48000, 8  },
+       { 88200, 9  },
+       { 96000, 10 },
+};
+
+static struct {
+       int div; /* *10 due to .5s */
+       int bclk_div;
+} bclk_divs[] = {
+       { 10,  0  },
+       { 15,  1  },
+       { 20,  2  },
+       { 30,  3  },
+       { 40,  4  },
+       { 50,  5  },
+       { 55,  6  },
+       { 60,  7  },
+       { 80,  8  },
+       { 100, 9  },
+       { 110, 10 },
+       { 120, 11 },
+       { 160, 12 },
+       { 200, 13 },
+       { 220, 14 },
+       { 240, 15 },
+       { 250, 16 },
+       { 300, 17 },
+       { 320, 18 },
+       { 440, 19 },
+       { 480, 20 },
+};
+
+struct wm9081_priv {
+       struct snd_soc_codec codec;
+       u16 reg_cache[WM9081_MAX_REGISTER + 1];
+       int sysclk_source;
+       int mclk_rate;
+       int sysclk_rate;
+       int fs;
+       int bclk;
+       int master;
+       int fll_fref;
+       int fll_fout;
+       struct wm9081_retune_mobile_config *retune;
+};
+
+static int wm9081_reg_is_volatile(int reg)
+{
+       switch (reg) {
+       default:
+               return 0;
+       }
+}
+
+static unsigned int wm9081_read_reg_cache(struct snd_soc_codec *codec,
+                                         unsigned int reg)
+{
+       u16 *cache = codec->reg_cache;
+       BUG_ON(reg > WM9081_MAX_REGISTER);
+       return cache[reg];
+}
+
+static unsigned int wm9081_read_hw(struct snd_soc_codec *codec, u8 reg)
+{
+       struct i2c_msg xfer[2];
+       u16 data;
+       int ret;
+       struct i2c_client *client = codec->control_data;
+
+       BUG_ON(reg > WM9081_MAX_REGISTER);
+
+       /* Write register */
+       xfer[0].addr = client->addr;
+       xfer[0].flags = 0;
+       xfer[0].len = 1;
+       xfer[0].buf = &reg;
+
+       /* Read data */
+       xfer[1].addr = client->addr;
+       xfer[1].flags = I2C_M_RD;
+       xfer[1].len = 2;
+       xfer[1].buf = (u8 *)&data;
+
+       ret = i2c_transfer(client->adapter, xfer, 2);
+       if (ret != 2) {
+               dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
+               return 0;
+       }
+
+       return (data >> 8) | ((data & 0xff) << 8);
+}
+
+static unsigned int wm9081_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+       if (wm9081_reg_is_volatile(reg))
+               return wm9081_read_hw(codec, reg);
+       else
+               return wm9081_read_reg_cache(codec, reg);
+}
+
+static int wm9081_write(struct snd_soc_codec *codec, unsigned int reg,
+                       unsigned int value)
+{
+       u16 *cache = codec->reg_cache;
+       u8 data[3];
+
+       BUG_ON(reg > WM9081_MAX_REGISTER);
+
+       if (!wm9081_reg_is_volatile(reg))
+               cache[reg] = value;
+
+       data[0] = reg;
+       data[1] = value >> 8;
+       data[2] = value & 0x00ff;
+
+       if (codec->hw_write(codec->control_data, data, 3) == 3)
+               return 0;
+       else
+               return -EIO;
+}
+
+static int wm9081_reset(struct snd_soc_codec *codec)
+{
+       return wm9081_write(codec, WM9081_SOFTWARE_RESET, 0);
+}
+
+static const DECLARE_TLV_DB_SCALE(drc_in_tlv, -4500, 75, 0);
+static const DECLARE_TLV_DB_SCALE(drc_out_tlv, -2250, 75, 0);
+static const DECLARE_TLV_DB_SCALE(drc_min_tlv, -1800, 600, 0);
+static unsigned int drc_max_tlv[] = {
+       TLV_DB_RANGE_HEAD(4),
+       0, 0, TLV_DB_SCALE_ITEM(1200, 0, 0),
+       1, 1, TLV_DB_SCALE_ITEM(1800, 0, 0),
+       2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+       3, 3, TLV_DB_SCALE_ITEM(3600, 0, 0),
+};
+static const DECLARE_TLV_DB_SCALE(drc_qr_tlv, 1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(drc_startup_tlv, -300, 50, 0);
+
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+
+static const DECLARE_TLV_DB_SCALE(in_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0);
+
+static const char *drc_high_text[] = {
+       "1",
+       "1/2",
+       "1/4",
+       "1/8",
+       "1/16",
+       "0",
+};
+
+static const struct soc_enum drc_high =
+       SOC_ENUM_SINGLE(WM9081_DRC_3, 3, 6, drc_high_text);
+
+static const char *drc_low_text[] = {
+       "1",
+       "1/2",
+       "1/4",
+       "1/8",
+       "0",
+};
+
+static const struct soc_enum drc_low =
+       SOC_ENUM_SINGLE(WM9081_DRC_3, 0, 5, drc_low_text);
+
+static const char *drc_atk_text[] = {
+       "181us",
+       "181us",
+       "363us",
+       "726us",
+       "1.45ms",
+       "2.9ms",
+       "5.8ms",
+       "11.6ms",
+       "23.2ms",
+       "46.4ms",
+       "92.8ms",
+       "185.6ms",
+};
+
+static const struct soc_enum drc_atk =
+       SOC_ENUM_SINGLE(WM9081_DRC_2, 12, 12, drc_atk_text);
+
+static const char *drc_dcy_text[] = {
+       "186ms",
+       "372ms",
+       "743ms",
+       "1.49s",
+       "2.97s",
+       "5.94s",
+       "11.89s",
+       "23.78s",
+       "47.56s",
+};
+
+static const struct soc_enum drc_dcy =
+       SOC_ENUM_SINGLE(WM9081_DRC_2, 8, 9, drc_dcy_text);
+
+static const char *drc_qr_dcy_text[] = {
+       "0.725ms",
+       "1.45ms",
+       "5.8ms",
+};
+
+static const struct soc_enum drc_qr_dcy =
+       SOC_ENUM_SINGLE(WM9081_DRC_2, 4, 3, drc_qr_dcy_text);
+
+static const char *dac_deemph_text[] = {
+       "None",
+       "32kHz",
+       "44.1kHz",
+       "48kHz",
+};
+
+static const struct soc_enum dac_deemph =
+       SOC_ENUM_SINGLE(WM9081_DAC_DIGITAL_2, 1, 4, dac_deemph_text);
+
+static const char *speaker_mode_text[] = {
+       "Class D",
+       "Class AB",
+};
+
+static const struct soc_enum speaker_mode =
+       SOC_ENUM_SINGLE(WM9081_ANALOGUE_SPEAKER_2, 6, 2, speaker_mode_text);
+
+static int speaker_mode_get(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       unsigned int reg;
+
+       reg = wm9081_read(codec, WM9081_ANALOGUE_SPEAKER_2);
+       if (reg & WM9081_SPK_MODE)
+               ucontrol->value.integer.value[0] = 1;
+       else
+               ucontrol->value.integer.value[0] = 0;
+
+       return 0;
+}
+
+/*
+ * Stop any attempts to change speaker mode while the speaker is enabled.
+ *
+ * We also have some special anti-pop controls dependant on speaker
+ * mode which must be changed along with the mode.
+ */
+static int speaker_mode_put(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       unsigned int reg_pwr = wm9081_read(codec, WM9081_POWER_MANAGEMENT);
+       unsigned int reg2 = wm9081_read(codec, WM9081_ANALOGUE_SPEAKER_2);
+
+       /* Are we changing anything? */
+       if (ucontrol->value.integer.value[0] ==
+           ((reg2 & WM9081_SPK_MODE) != 0))
+               return 0;
+
+       /* Don't try to change modes while enabled */
+       if (reg_pwr & WM9081_SPK_ENA)
+               return -EINVAL;
+
+       if (ucontrol->value.integer.value[0]) {
+               /* Class AB */
+               reg2 &= ~(WM9081_SPK_INV_MUTE | WM9081_OUT_SPK_CTRL);
+               reg2 |= WM9081_SPK_MODE;
+       } else {
+               /* Class D */
+               reg2 |= WM9081_SPK_INV_MUTE | WM9081_OUT_SPK_CTRL;
+               reg2 &= ~WM9081_SPK_MODE;
+       }
+
+       wm9081_write(codec, WM9081_ANALOGUE_SPEAKER_2, reg2);
+
+       return 0;
+}
+
+static const struct snd_kcontrol_new wm9081_snd_controls[] = {
+SOC_SINGLE_TLV("IN1 Volume", WM9081_ANALOGUE_MIXER, 1, 1, 1, in_tlv),
+SOC_SINGLE_TLV("IN2 Volume", WM9081_ANALOGUE_MIXER, 3, 1, 1, in_tlv),
+
+SOC_SINGLE_TLV("Playback Volume", WM9081_DAC_DIGITAL_1, 1, 96, 0, dac_tlv),
+
+SOC_SINGLE("LINEOUT Switch", WM9081_ANALOGUE_LINEOUT, 7, 1, 1),
+SOC_SINGLE("LINEOUT ZC Switch", WM9081_ANALOGUE_LINEOUT, 6, 1, 0),
+SOC_SINGLE_TLV("LINEOUT Volume", WM9081_ANALOGUE_LINEOUT, 0, 63, 0, out_tlv),
+
+SOC_SINGLE("DRC Switch", WM9081_DRC_1, 15, 1, 0),
+SOC_ENUM("DRC High Slope", drc_high),
+SOC_ENUM("DRC Low Slope", drc_low),
+SOC_SINGLE_TLV("DRC Input Volume", WM9081_DRC_4, 5, 60, 1, drc_in_tlv),
+SOC_SINGLE_TLV("DRC Output Volume", WM9081_DRC_4, 0, 30, 1, drc_out_tlv),
+SOC_SINGLE_TLV("DRC Minimum Volume", WM9081_DRC_2, 2, 3, 1, drc_min_tlv),
+SOC_SINGLE_TLV("DRC Maximum Volume", WM9081_DRC_2, 0, 3, 0, drc_max_tlv),
+SOC_ENUM("DRC Attack", drc_atk),
+SOC_ENUM("DRC Decay", drc_dcy),
+SOC_SINGLE("DRC Quick Release Switch", WM9081_DRC_1, 2, 1, 0),
+SOC_SINGLE_TLV("DRC Quick Release Volume", WM9081_DRC_2, 6, 3, 0, drc_qr_tlv),
+SOC_ENUM("DRC Quick Release Decay", drc_qr_dcy),
+SOC_SINGLE_TLV("DRC Startup Volume", WM9081_DRC_1, 6, 18, 0, drc_startup_tlv),
+
+SOC_SINGLE("EQ Switch", WM9081_EQ_1, 0, 1, 0),
+
+SOC_SINGLE("Speaker DC Volume", WM9081_ANALOGUE_SPEAKER_1, 3, 5, 0),
+SOC_SINGLE("Speaker AC Volume", WM9081_ANALOGUE_SPEAKER_1, 0, 5, 0),
+SOC_SINGLE("Speaker Switch", WM9081_ANALOGUE_SPEAKER_PGA, 7, 1, 1),
+SOC_SINGLE("Speaker ZC Switch", WM9081_ANALOGUE_SPEAKER_PGA, 6, 1, 0),
+SOC_SINGLE_TLV("Speaker Volume", WM9081_ANALOGUE_SPEAKER_PGA, 0, 63, 0,
+              out_tlv),
+SOC_ENUM("DAC Deemphasis", dac_deemph),
+SOC_ENUM_EXT("Speaker Mode", speaker_mode, speaker_mode_get, speaker_mode_put),
+};
+
+static const struct snd_kcontrol_new wm9081_eq_controls[] = {
+SOC_SINGLE_TLV("EQ1 Volume", WM9081_EQ_1, 11, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 Volume", WM9081_EQ_1, 6, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 Volume", WM9081_EQ_1, 1, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 Volume", WM9081_EQ_2, 11, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ5 Volume", WM9081_EQ_2, 6, 24, 0, eq_tlv),
+};
+
+static const struct snd_kcontrol_new mixer[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM9081_ANALOGUE_MIXER, 0, 1, 0),
+SOC_DAPM_SINGLE("IN2 Switch", WM9081_ANALOGUE_MIXER, 2, 1, 0),
+SOC_DAPM_SINGLE("Playback Switch", WM9081_ANALOGUE_MIXER, 4, 1, 0),
+};
+
+static int speaker_event(struct snd_soc_dapm_widget *w,
+                        struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       unsigned int reg = wm9081_read(codec, WM9081_POWER_MANAGEMENT);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               reg |= WM9081_SPK_ENA;
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               reg &= ~WM9081_SPK_ENA;
+               break;
+       }
+
+       wm9081_write(codec, WM9081_POWER_MANAGEMENT, reg);
+
+       return 0;
+}
+
+struct _fll_div {
+       u16 fll_fratio;
+       u16 fll_outdiv;
+       u16 fll_clk_ref_div;
+       u16 n;
+       u16 k;
+};
+
+/* The size in bits of the FLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+static struct {
+       unsigned int min;
+       unsigned int max;
+       u16 fll_fratio;
+       int ratio;
+} fll_fratios[] = {
+       {       0,    64000, 4, 16 },
+       {   64000,   128000, 3,  8 },
+       {  128000,   256000, 2,  4 },
+       {  256000,  1000000, 1,  2 },
+       { 1000000, 13500000, 0,  1 },
+};
+
+static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
+                      unsigned int Fout)
+{
+       u64 Kpart;
+       unsigned int K, Ndiv, Nmod, target;
+       unsigned int div;
+       int i;
+
+       /* Fref must be <=13.5MHz */
+       div = 1;
+       while ((Fref / div) > 13500000) {
+               div *= 2;
+
+               if (div > 8) {
+                       pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
+                              Fref);
+                       return -EINVAL;
+               }
+       }
+       fll_div->fll_clk_ref_div = div / 2;
+
+       pr_debug("Fref=%u Fout=%u\n", Fref, Fout);
+
+       /* Apply the division for our remaining calculations */
+       Fref /= div;
+
+       /* Fvco should be 90-100MHz; don't check the upper bound */
+       div = 0;
+       target = Fout * 2;
+       while (target < 90000000) {
+               div++;
+               target *= 2;
+               if (div > 7) {
+                       pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
+                              Fout);
+                       return -EINVAL;
+               }
+       }
+       fll_div->fll_outdiv = div;
+
+       pr_debug("Fvco=%dHz\n", target);
+
+       /* Find an appropraite FLL_FRATIO and factor it out of the target */
+       for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+               if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+                       fll_div->fll_fratio = fll_fratios[i].fll_fratio;
+                       target /= fll_fratios[i].ratio;
+                       break;
+               }
+       }
+       if (i == ARRAY_SIZE(fll_fratios)) {
+               pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
+               return -EINVAL;
+       }
+
+       /* Now, calculate N.K */
+       Ndiv = target / Fref;
+
+       fll_div->n = Ndiv;
+       Nmod = target % Fref;
+       pr_debug("Nmod=%d\n", Nmod);
+
+       /* Calculate fractional part - scale up so we can round. */
+       Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+       do_div(Kpart, Fref);
+
+       K = Kpart & 0xFFFFFFFF;
+
+       if ((K % 10) >= 5)
+               K += 5;
+
+       /* Move down to proper range now rounding is done */
+       fll_div->k = K / 10;
+
+       pr_debug("N=%x K=%x FLL_FRATIO=%x FLL_OUTDIV=%x FLL_CLK_REF_DIV=%x\n",
+                fll_div->n, fll_div->k,
+                fll_div->fll_fratio, fll_div->fll_outdiv,
+                fll_div->fll_clk_ref_div);
+
+       return 0;
+}
+
+static int wm9081_set_fll(struct snd_soc_codec *codec, int fll_id,
+                         unsigned int Fref, unsigned int Fout)
+{
+       struct wm9081_priv *wm9081 = codec->private_data;
+       u16 reg1, reg4, reg5;
+       struct _fll_div fll_div;
+       int ret;
+       int clk_sys_reg;
+
+       /* Any change? */
+       if (Fref == wm9081->fll_fref && Fout == wm9081->fll_fout)
+               return 0;
+
+       /* Disable the FLL */
+       if (Fout == 0) {
+               dev_dbg(codec->dev, "FLL disabled\n");
+               wm9081->fll_fref = 0;
+               wm9081->fll_fout = 0;
+
+               return 0;
+       }
+
+       ret = fll_factors(&fll_div, Fref, Fout);
+       if (ret != 0)
+               return ret;
+
+       reg5 = wm9081_read(codec, WM9081_FLL_CONTROL_5);
+       reg5 &= ~WM9081_FLL_CLK_SRC_MASK;
+
+       switch (fll_id) {
+       case WM9081_SYSCLK_FLL_MCLK:
+               reg5 |= 0x1;
+               break;
+
+       default:
+               dev_err(codec->dev, "Unknown FLL ID %d\n", fll_id);
+               return -EINVAL;
+       }
+
+       /* Disable CLK_SYS while we reconfigure */
+       clk_sys_reg = wm9081_read(codec, WM9081_CLOCK_CONTROL_3);
+       if (clk_sys_reg & WM9081_CLK_SYS_ENA)
+               wm9081_write(codec, WM9081_CLOCK_CONTROL_3,
+                            clk_sys_reg & ~WM9081_CLK_SYS_ENA);
+
+       /* Any FLL configuration change requires that the FLL be
+        * disabled first. */
+       reg1 = wm9081_read(codec, WM9081_FLL_CONTROL_1);
+       reg1 &= ~WM9081_FLL_ENA;
+       wm9081_write(codec, WM9081_FLL_CONTROL_1, reg1);
+
+       /* Apply the configuration */
+       if (fll_div.k)
+               reg1 |= WM9081_FLL_FRAC_MASK;
+       else
+               reg1 &= ~WM9081_FLL_FRAC_MASK;
+       wm9081_write(codec, WM9081_FLL_CONTROL_1, reg1);
+
+       wm9081_write(codec, WM9081_FLL_CONTROL_2,
+                    (fll_div.fll_outdiv << WM9081_FLL_OUTDIV_SHIFT) |
+                    (fll_div.fll_fratio << WM9081_FLL_FRATIO_SHIFT));
+       wm9081_write(codec, WM9081_FLL_CONTROL_3, fll_div.k);
+
+       reg4 = wm9081_read(codec, WM9081_FLL_CONTROL_4);
+       reg4 &= ~WM9081_FLL_N_MASK;
+       reg4 |= fll_div.n << WM9081_FLL_N_SHIFT;
+       wm9081_write(codec, WM9081_FLL_CONTROL_4, reg4);
+
+       reg5 &= ~WM9081_FLL_CLK_REF_DIV_MASK;
+       reg5 |= fll_div.fll_clk_ref_div << WM9081_FLL_CLK_REF_DIV_SHIFT;
+       wm9081_write(codec, WM9081_FLL_CONTROL_5, reg5);
+
+       /* Enable the FLL */
+       wm9081_write(codec, WM9081_FLL_CONTROL_1, reg1 | WM9081_FLL_ENA);
+
+       /* Then bring CLK_SYS up again if it was disabled */
+       if (clk_sys_reg & WM9081_CLK_SYS_ENA)
+               wm9081_write(codec, WM9081_CLOCK_CONTROL_3, clk_sys_reg);
+
+       dev_dbg(codec->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout);
+
+       wm9081->fll_fref = Fref;
+       wm9081->fll_fout = Fout;
+
+       return 0;
+}
+
+static int configure_clock(struct snd_soc_codec *codec)
+{
+       struct wm9081_priv *wm9081 = codec->private_data;
+       int new_sysclk, i, target;
+       unsigned int reg;
+       int ret = 0;
+       int mclkdiv = 0;
+       int fll = 0;
+
+       switch (wm9081->sysclk_source) {
+       case WM9081_SYSCLK_MCLK:
+               if (wm9081->mclk_rate > 12225000) {
+                       mclkdiv = 1;
+                       wm9081->sysclk_rate = wm9081->mclk_rate / 2;
+               } else {
+                       wm9081->sysclk_rate = wm9081->mclk_rate;
+               }
+               wm9081_set_fll(codec, WM9081_SYSCLK_FLL_MCLK, 0, 0);
+               break;
+
+       case WM9081_SYSCLK_FLL_MCLK:
+               /* If we have a sample rate calculate a CLK_SYS that
+                * gives us a suitable DAC configuration, plus BCLK.
+                * Ideally we would check to see if we can clock
+                * directly from MCLK and only use the FLL if this is
+                * not the case, though care must be taken with free
+                * running mode.
+                */
+               if (wm9081->master && wm9081->bclk) {
+                       /* Make sure we can generate CLK_SYS and BCLK
+                        * and that we've got 3MHz for optimal
+                        * performance. */
+                       for (i = 0; i < ARRAY_SIZE(clk_sys_rates); i++) {
+                               target = wm9081->fs * clk_sys_rates[i].ratio;
+                               new_sysclk = target;
+                               if (target >= wm9081->bclk &&
+                                   target > 3000000)
+                                       break;
+                       }
+               } else if (wm9081->fs) {
+                       for (i = 0; i < ARRAY_SIZE(clk_sys_rates); i++) {
+                               new_sysclk = clk_sys_rates[i].ratio
+                                       * wm9081->fs;
+                               if (new_sysclk > 3000000)
+                                       break;
+                       }
+               } else {
+                       new_sysclk = 12288000;
+               }
+
+               ret = wm9081_set_fll(codec, WM9081_SYSCLK_FLL_MCLK,
+                                    wm9081->mclk_rate, new_sysclk);
+               if (ret == 0) {
+                       wm9081->sysclk_rate = new_sysclk;
+
+                       /* Switch SYSCLK over to FLL */
+                       fll = 1;
+               } else {
+                       wm9081->sysclk_rate = wm9081->mclk_rate;
+               }
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       reg = wm9081_read(codec, WM9081_CLOCK_CONTROL_1);
+       if (mclkdiv)
+               reg |= WM9081_MCLKDIV2;
+       else
+               reg &= ~WM9081_MCLKDIV2;
+       wm9081_write(codec, WM9081_CLOCK_CONTROL_1, reg);
+
+       reg = wm9081_read(codec, WM9081_CLOCK_CONTROL_3);
+       if (fll)
+               reg |= WM9081_CLK_SRC_SEL;
+       else
+               reg &= ~WM9081_CLK_SRC_SEL;
+       wm9081_write(codec, WM9081_CLOCK_CONTROL_3, reg);
+
+       dev_dbg(codec->dev, "CLK_SYS is %dHz\n", wm9081->sysclk_rate);
+
+       return ret;
+}
+
+static int clk_sys_event(struct snd_soc_dapm_widget *w,
+                        struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct wm9081_priv *wm9081 = codec->private_data;
+
+       /* This should be done on init() for bypass paths */
+       switch (wm9081->sysclk_source) {
+       case WM9081_SYSCLK_MCLK:
+               dev_dbg(codec->dev, "Using %dHz MCLK\n", wm9081->mclk_rate);
+               break;
+       case WM9081_SYSCLK_FLL_MCLK:
+               dev_dbg(codec->dev, "Using %dHz MCLK with FLL\n",
+                       wm9081->mclk_rate);
+               break;
+       default:
+               dev_err(codec->dev, "System clock not configured\n");
+               return -EINVAL;
+       }
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               configure_clock(codec);
+               break;
+
+       case SND_SOC_DAPM_POST_PMD:
+               /* Disable the FLL if it's running */
+               wm9081_set_fll(codec, 0, 0, 0);
+               break;
+       }
+
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget wm9081_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1"),
+SND_SOC_DAPM_INPUT("IN2"),
+
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM9081_POWER_MANAGEMENT, 0, 0),
+
+SND_SOC_DAPM_MIXER_NAMED_CTL("Mixer", SND_SOC_NOPM, 0, 0,
+                            mixer, ARRAY_SIZE(mixer)),
+
+SND_SOC_DAPM_PGA("LINEOUT PGA", WM9081_POWER_MANAGEMENT, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_E("Speaker PGA", WM9081_POWER_MANAGEMENT, 2, 0, NULL, 0,
+                  speaker_event,
+                  SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_OUTPUT("LINEOUT"),
+SND_SOC_DAPM_OUTPUT("SPKN"),
+SND_SOC_DAPM_OUTPUT("SPKP"),
+
+SND_SOC_DAPM_SUPPLY("CLK_SYS", WM9081_CLOCK_CONTROL_3, 0, 0, clk_sys_event,
+                   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("CLK_DSP", WM9081_CLOCK_CONTROL_3, 1, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("TOCLK", WM9081_CLOCK_CONTROL_3, 2, 0, NULL, 0),
+};
+
+
+static const struct snd_soc_dapm_route audio_paths[] = {
+       { "DAC", NULL, "CLK_SYS" },
+       { "DAC", NULL, "CLK_DSP" },
+
+       { "Mixer", "IN1 Switch", "IN1" },
+       { "Mixer", "IN2 Switch", "IN2" },
+       { "Mixer", "Playback Switch", "DAC" },
+
+       { "LINEOUT PGA", NULL, "Mixer" },
+       { "LINEOUT PGA", NULL, "TOCLK" },
+       { "LINEOUT PGA", NULL, "CLK_SYS" },
+
+       { "LINEOUT", NULL, "LINEOUT PGA" },
+
+       { "Speaker PGA", NULL, "Mixer" },
+       { "Speaker PGA", NULL, "TOCLK" },
+       { "Speaker PGA", NULL, "CLK_SYS" },
+
+       { "SPKN", NULL, "Speaker PGA" },
+       { "SPKP", NULL, "Speaker PGA" },
+};
+
+static int wm9081_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       u16 reg;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               /* VMID=2*40k */
+               reg = wm9081_read(codec, WM9081_VMID_CONTROL);
+               reg &= ~WM9081_VMID_SEL_MASK;
+               reg |= 0x2;
+               wm9081_write(codec, WM9081_VMID_CONTROL, reg);
+
+               /* Normal bias current */
+               reg = wm9081_read(codec, WM9081_BIAS_CONTROL_1);
+               reg &= ~WM9081_STBY_BIAS_ENA;
+               wm9081_write(codec, WM9081_BIAS_CONTROL_1, reg);
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               /* Initial cold start */
+               if (codec->bias_level == SND_SOC_BIAS_OFF) {
+                       /* Disable LINEOUT discharge */
+                       reg = wm9081_read(codec, WM9081_ANTI_POP_CONTROL);
+                       reg &= ~WM9081_LINEOUT_DISCH;
+                       wm9081_write(codec, WM9081_ANTI_POP_CONTROL, reg);
+
+                       /* Select startup bias source */
+                       reg = wm9081_read(codec, WM9081_BIAS_CONTROL_1);
+                       reg |= WM9081_BIAS_SRC | WM9081_BIAS_ENA;
+                       wm9081_write(codec, WM9081_BIAS_CONTROL_1, reg);
+
+                       /* VMID 2*4k; Soft VMID ramp enable */
+                       reg = wm9081_read(codec, WM9081_VMID_CONTROL);
+                       reg |= WM9081_VMID_RAMP | 0x6;
+                       wm9081_write(codec, WM9081_VMID_CONTROL, reg);
+
+                       mdelay(100);
+
+                       /* Normal bias enable & soft start off */
+                       reg |= WM9081_BIAS_ENA;
+                       reg &= ~WM9081_VMID_RAMP;
+                       wm9081_write(codec, WM9081_VMID_CONTROL, reg);
+
+                       /* Standard bias source */
+                       reg = wm9081_read(codec, WM9081_BIAS_CONTROL_1);
+                       reg &= ~WM9081_BIAS_SRC;
+                       wm9081_write(codec, WM9081_BIAS_CONTROL_1, reg);
+               }
+
+               /* VMID 2*240k */
+               reg = wm9081_read(codec, WM9081_BIAS_CONTROL_1);
+               reg &= ~WM9081_VMID_SEL_MASK;
+               reg |= 0x40;
+               wm9081_write(codec, WM9081_VMID_CONTROL, reg);
+
+               /* Standby bias current on */
+               reg = wm9081_read(codec, WM9081_BIAS_CONTROL_1);
+               reg |= WM9081_STBY_BIAS_ENA;
+               wm9081_write(codec, WM9081_BIAS_CONTROL_1, reg);
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               /* Startup bias source */
+               reg = wm9081_read(codec, WM9081_BIAS_CONTROL_1);
+               reg |= WM9081_BIAS_SRC;
+               wm9081_write(codec, WM9081_BIAS_CONTROL_1, reg);
+
+               /* Disable VMID and biases with soft ramping */
+               reg = wm9081_read(codec, WM9081_VMID_CONTROL);
+               reg &= ~(WM9081_VMID_SEL_MASK | WM9081_BIAS_ENA);
+               reg |= WM9081_VMID_RAMP;
+               wm9081_write(codec, WM9081_VMID_CONTROL, reg);
+
+               /* Actively discharge LINEOUT */
+               reg = wm9081_read(codec, WM9081_ANTI_POP_CONTROL);
+               reg |= WM9081_LINEOUT_DISCH;
+               wm9081_write(codec, WM9081_ANTI_POP_CONTROL, reg);
+               break;
+       }
+
+       codec->bias_level = level;
+
+       return 0;
+}
+
+static int wm9081_set_dai_fmt(struct snd_soc_dai *dai,
+                             unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct wm9081_priv *wm9081 = codec->private_data;
+       unsigned int aif2 = wm9081_read(codec, WM9081_AUDIO_INTERFACE_2);
+
+       aif2 &= ~(WM9081_AIF_BCLK_INV | WM9081_AIF_LRCLK_INV |
+                 WM9081_BCLK_DIR | WM9081_LRCLK_DIR | WM9081_AIF_FMT_MASK);
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               wm9081->master = 0;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFM:
+               aif2 |= WM9081_LRCLK_DIR;
+               wm9081->master = 1;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFS:
+               aif2 |= WM9081_BCLK_DIR;
+               wm9081->master = 1;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               aif2 |= WM9081_LRCLK_DIR | WM9081_BCLK_DIR;
+               wm9081->master = 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_B:
+               aif2 |= WM9081_AIF_LRCLK_INV;
+       case SND_SOC_DAIFMT_DSP_A:
+               aif2 |= 0x3;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               aif2 |= 0x2;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               aif2 |= 0x1;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_A:
+       case SND_SOC_DAIFMT_DSP_B:
+               /* frame inversion not valid for DSP modes */
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       aif2 |= WM9081_AIF_BCLK_INV;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+
+       case SND_SOC_DAIFMT_I2S:
+       case SND_SOC_DAIFMT_RIGHT_J:
+       case SND_SOC_DAIFMT_LEFT_J:
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       aif2 |= WM9081_AIF_BCLK_INV | WM9081_AIF_LRCLK_INV;
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       aif2 |= WM9081_AIF_BCLK_INV;
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       aif2 |= WM9081_AIF_LRCLK_INV;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       wm9081_write(codec, WM9081_AUDIO_INTERFACE_2, aif2);
+
+       return 0;
+}
+
+static int wm9081_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct wm9081_priv *wm9081 = codec->private_data;
+       int ret, i, best, best_val, cur_val;
+       unsigned int clk_ctrl2, aif1, aif2, aif3, aif4;
+
+       clk_ctrl2 = wm9081_read(codec, WM9081_CLOCK_CONTROL_2);
+       clk_ctrl2 &= ~(WM9081_CLK_SYS_RATE_MASK | WM9081_SAMPLE_RATE_MASK);
+
+       aif1 = wm9081_read(codec, WM9081_AUDIO_INTERFACE_1);
+
+       aif2 = wm9081_read(codec, WM9081_AUDIO_INTERFACE_2);
+       aif2 &= ~WM9081_AIF_WL_MASK;
+
+       aif3 = wm9081_read(codec, WM9081_AUDIO_INTERFACE_3);
+       aif3 &= ~WM9081_BCLK_DIV_MASK;
+
+       aif4 = wm9081_read(codec, WM9081_AUDIO_INTERFACE_4);
+       aif4 &= ~WM9081_LRCLK_RATE_MASK;
+
+       /* What BCLK do we need? */
+       wm9081->fs = params_rate(params);
+       wm9081->bclk = 2 * wm9081->fs;
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               wm9081->bclk *= 16;
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               wm9081->bclk *= 20;
+               aif2 |= 0x4;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               wm9081->bclk *= 24;
+               aif2 |= 0x8;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               wm9081->bclk *= 32;
+               aif2 |= 0xc;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (aif1 & WM9081_AIFDAC_TDM_MODE_MASK) {
+               int slots = ((aif1 & WM9081_AIFDAC_TDM_MODE_MASK) >>
+                            WM9081_AIFDAC_TDM_MODE_SHIFT) + 1;
+               wm9081->bclk *= slots;
+       }
+
+       dev_dbg(codec->dev, "Target BCLK is %dHz\n", wm9081->bclk);
+
+       ret = configure_clock(codec);
+       if (ret != 0)
+               return ret;
+
+       /* Select nearest CLK_SYS_RATE */
+       best = 0;
+       best_val = abs((wm9081->sysclk_rate / clk_sys_rates[0].ratio)
+                      - wm9081->fs);
+       for (i = 1; i < ARRAY_SIZE(clk_sys_rates); i++) {
+               cur_val = abs((wm9081->sysclk_rate /
+                              clk_sys_rates[i].ratio) - wm9081->fs);;
+               if (cur_val < best_val) {
+                       best = i;
+                       best_val = cur_val;
+               }
+       }
+       dev_dbg(codec->dev, "Selected CLK_SYS_RATIO of %d\n",
+               clk_sys_rates[best].ratio);
+       clk_ctrl2 |= (clk_sys_rates[best].clk_sys_rate
+                     << WM9081_CLK_SYS_RATE_SHIFT);
+
+       /* SAMPLE_RATE */
+       best = 0;
+       best_val = abs(wm9081->fs - sample_rates[0].rate);
+       for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
+               /* Closest match */
+               cur_val = abs(wm9081->fs - sample_rates[i].rate);
+               if (cur_val < best_val) {
+                       best = i;
+                       best_val = cur_val;
+               }
+       }
+       dev_dbg(codec->dev, "Selected SAMPLE_RATE of %dHz\n",
+               sample_rates[best].rate);
+       clk_ctrl2 |= (sample_rates[best].sample_rate
+                       << WM9081_SAMPLE_RATE_SHIFT);
+
+       /* BCLK_DIV */
+       best = 0;
+       best_val = INT_MAX;
+       for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+               cur_val = ((wm9081->sysclk_rate * 10) / bclk_divs[i].div)
+                       - wm9081->bclk;
+               if (cur_val < 0) /* Table is sorted */
+                       break;
+               if (cur_val < best_val) {
+                       best = i;
+                       best_val = cur_val;
+               }
+       }
+       wm9081->bclk = (wm9081->sysclk_rate * 10) / bclk_divs[best].div;
+       dev_dbg(codec->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n",
+               bclk_divs[best].div, wm9081->bclk);
+       aif3 |= bclk_divs[best].bclk_div;
+
+       /* LRCLK is a simple fraction of BCLK */
+       dev_dbg(codec->dev, "LRCLK_RATE is %d\n", wm9081->bclk / wm9081->fs);
+       aif4 |= wm9081->bclk / wm9081->fs;
+
+       /* Apply a ReTune Mobile configuration if it's in use */
+       if (wm9081->retune) {
+               struct wm9081_retune_mobile_config *retune = wm9081->retune;
+               struct wm9081_retune_mobile_setting *s;
+               int eq1;
+
+               best = 0;
+               best_val = abs(retune->configs[0].rate - wm9081->fs);
+               for (i = 0; i < retune->num_configs; i++) {
+                       cur_val = abs(retune->configs[i].rate - wm9081->fs);
+                       if (cur_val < best_val) {
+                               best_val = cur_val;
+                               best = i;
+                       }
+               }
+               s = &retune->configs[best];
+
+               dev_dbg(codec->dev, "ReTune Mobile %s tuned for %dHz\n",
+                       s->name, s->rate);
+
+               /* If the EQ is enabled then disable it while we write out */
+               eq1 = wm9081_read(codec, WM9081_EQ_1) & WM9081_EQ_ENA;
+               if (eq1 & WM9081_EQ_ENA)
+                       wm9081_write(codec, WM9081_EQ_1, 0);
+
+               /* Write out the other values */
+               for (i = 1; i < ARRAY_SIZE(s->config); i++)
+                       wm9081_write(codec, WM9081_EQ_1 + i, s->config[i]);
+
+               eq1 |= (s->config[0] & ~WM9081_EQ_ENA);
+               wm9081_write(codec, WM9081_EQ_1, eq1);
+       }
+
+       wm9081_write(codec, WM9081_CLOCK_CONTROL_2, clk_ctrl2);
+       wm9081_write(codec, WM9081_AUDIO_INTERFACE_2, aif2);
+       wm9081_write(codec, WM9081_AUDIO_INTERFACE_3, aif3);
+       wm9081_write(codec, WM9081_AUDIO_INTERFACE_4, aif4);
+
+       return 0;
+}
+
+static int wm9081_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       unsigned int reg;
+
+       reg = wm9081_read(codec, WM9081_DAC_DIGITAL_2);
+
+       if (mute)
+               reg |= WM9081_DAC_MUTE;
+       else
+               reg &= ~WM9081_DAC_MUTE;
+
+       wm9081_write(codec, WM9081_DAC_DIGITAL_2, reg);
+
+       return 0;
+}
+
+static int wm9081_set_sysclk(struct snd_soc_dai *codec_dai,
+                            int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct wm9081_priv *wm9081 = codec->private_data;
+
+       switch (clk_id) {
+       case WM9081_SYSCLK_MCLK:
+       case WM9081_SYSCLK_FLL_MCLK:
+               wm9081->sysclk_source = clk_id;
+               wm9081->mclk_rate = freq;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int wm9081_set_tdm_slot(struct snd_soc_dai *dai,
+                              unsigned int mask, int slots)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       unsigned int aif1 = wm9081_read(codec, WM9081_AUDIO_INTERFACE_1);
+
+       aif1 &= ~(WM9081_AIFDAC_TDM_SLOT_MASK | WM9081_AIFDAC_TDM_MODE_MASK);
+
+       if (slots < 1 || slots > 4)
+               return -EINVAL;
+
+       aif1 |= (slots - 1) << WM9081_AIFDAC_TDM_MODE_SHIFT;
+
+       switch (mask) {
+       case 1:
+               break;
+       case 2:
+               aif1 |= 0x10;
+               break;
+       case 4:
+               aif1 |= 0x20;
+               break;
+       case 8:
+               aif1 |= 0x30;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       wm9081_write(codec, WM9081_AUDIO_INTERFACE_1, aif1);
+
+       return 0;
+}
+
+#define WM9081_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM9081_FORMATS \
+       (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops wm9081_dai_ops = {
+       .hw_params = wm9081_hw_params,
+       .set_sysclk = wm9081_set_sysclk,
+       .set_fmt = wm9081_set_dai_fmt,
+       .digital_mute = wm9081_digital_mute,
+       .set_tdm_slot = wm9081_set_tdm_slot,
+};
+
+/* We report two channels because the CODEC processes a stereo signal, even
+ * though it is only capable of handling a mono output.
+ */
+struct snd_soc_dai wm9081_dai = {
+       .name = "WM9081",
+       .playback = {
+               .stream_name = "HiFi Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = WM9081_RATES,
+               .formats = WM9081_FORMATS,
+       },
+       .ops = &wm9081_dai_ops,
+};
+EXPORT_SYMBOL_GPL(wm9081_dai);
+
+
+static struct snd_soc_codec *wm9081_codec;
+
+static int wm9081_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       struct wm9081_priv *wm9081;
+       int ret = 0;
+
+       if (wm9081_codec == NULL) {
+               dev_err(&pdev->dev, "Codec device not registered\n");
+               return -ENODEV;
+       }
+
+       socdev->card->codec = wm9081_codec;
+       codec = wm9081_codec;
+       wm9081 = codec->private_data;
+
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+               goto pcm_err;
+       }
+
+       snd_soc_add_controls(codec, wm9081_snd_controls,
+                            ARRAY_SIZE(wm9081_snd_controls));
+       if (!wm9081->retune) {
+               dev_dbg(codec->dev,
+                       "No ReTune Mobile data, using normal EQ\n");
+               snd_soc_add_controls(codec, wm9081_eq_controls,
+                                    ARRAY_SIZE(wm9081_eq_controls));
+       }
+
+       snd_soc_dapm_new_controls(codec, wm9081_dapm_widgets,
+                                 ARRAY_SIZE(wm9081_dapm_widgets));
+       snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
+       snd_soc_dapm_new_widgets(codec);
+
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to register card: %d\n", ret);
+               goto card_err;
+       }
+
+       return ret;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+pcm_err:
+       return ret;
+}
+
+static int wm9081_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm9081_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       wm9081_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int wm9081_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+       u16 *reg_cache = codec->reg_cache;
+       int i;
+
+       for (i = 0; i < codec->reg_cache_size; i++) {
+               if (i == WM9081_SOFTWARE_RESET)
+                       continue;
+
+               wm9081_write(codec, i, reg_cache[i]);
+       }
+
+       wm9081_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+}
+#else
+#define wm9081_suspend NULL
+#define wm9081_resume NULL
+#endif
+
+struct snd_soc_codec_device soc_codec_dev_wm9081 = {
+       .probe =        wm9081_probe,
+       .remove =       wm9081_remove,
+       .suspend =      wm9081_suspend,
+       .resume =       wm9081_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm9081);
+
+static int wm9081_register(struct wm9081_priv *wm9081)
+{
+       struct snd_soc_codec *codec = &wm9081->codec;
+       int ret;
+       u16 reg;
+
+       if (wm9081_codec) {
+               dev_err(codec->dev, "Another WM9081 is registered\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->private_data = wm9081;
+       codec->name = "WM9081";
+       codec->owner = THIS_MODULE;
+       codec->read = wm9081_read;
+       codec->write = wm9081_write;
+       codec->dai = &wm9081_dai;
+       codec->num_dai = 1;
+       codec->reg_cache_size = ARRAY_SIZE(wm9081->reg_cache);
+       codec->reg_cache = &wm9081->reg_cache;
+       codec->bias_level = SND_SOC_BIAS_OFF;
+       codec->set_bias_level = wm9081_set_bias_level;
+
+       memcpy(codec->reg_cache, wm9081_reg_defaults,
+              sizeof(wm9081_reg_defaults));
+
+       reg = wm9081_read_hw(codec, WM9081_SOFTWARE_RESET);
+       if (reg != 0x9081) {
+               dev_err(codec->dev, "Device is not a WM9081: ID=0x%x\n", reg);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ret = wm9081_reset(codec);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to issue reset\n");
+               return ret;
+       }
+
+       wm9081_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       /* Enable zero cross by default */
+       reg = wm9081_read(codec, WM9081_ANALOGUE_LINEOUT);
+       wm9081_write(codec, WM9081_ANALOGUE_LINEOUT, reg | WM9081_LINEOUTZC);
+       reg = wm9081_read(codec, WM9081_ANALOGUE_SPEAKER_PGA);
+       wm9081_write(codec, WM9081_ANALOGUE_SPEAKER_PGA,
+                    reg | WM9081_SPKPGAZC);
+
+       wm9081_dai.dev = codec->dev;
+
+       wm9081_codec = codec;
+
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+               return ret;
+       }
+
+       ret = snd_soc_register_dai(&wm9081_dai);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+               snd_soc_unregister_codec(codec);
+               return ret;
+       }
+
+       return 0;
+
+err:
+       kfree(wm9081);
+       return ret;
+}
+
+static void wm9081_unregister(struct wm9081_priv *wm9081)
+{
+       wm9081_set_bias_level(&wm9081->codec, SND_SOC_BIAS_OFF);
+       snd_soc_unregister_dai(&wm9081_dai);
+       snd_soc_unregister_codec(&wm9081->codec);
+       kfree(wm9081);
+       wm9081_codec = NULL;
+}
+
+static __devinit int wm9081_i2c_probe(struct i2c_client *i2c,
+                                     const struct i2c_device_id *id)
+{
+       struct wm9081_priv *wm9081;
+       struct snd_soc_codec *codec;
+
+       wm9081 = kzalloc(sizeof(struct wm9081_priv), GFP_KERNEL);
+       if (wm9081 == NULL)
+               return -ENOMEM;
+
+       codec = &wm9081->codec;
+       codec->hw_write = (hw_write_t)i2c_master_send;
+       wm9081->retune = i2c->dev.platform_data;
+
+       i2c_set_clientdata(i2c, wm9081);
+       codec->control_data = i2c;
+
+       codec->dev = &i2c->dev;
+
+       return wm9081_register(wm9081);
+}
+
+static __devexit int wm9081_i2c_remove(struct i2c_client *client)
+{
+       struct wm9081_priv *wm9081 = i2c_get_clientdata(client);
+       wm9081_unregister(wm9081);
+       return 0;
+}
+
+static const struct i2c_device_id wm9081_i2c_id[] = {
+       { "wm9081", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm9081_i2c_id);
+
+static struct i2c_driver wm9081_i2c_driver = {
+       .driver = {
+               .name = "wm9081",
+               .owner = THIS_MODULE,
+       },
+       .probe =    wm9081_i2c_probe,
+       .remove =   __devexit_p(wm9081_i2c_remove),
+       .id_table = wm9081_i2c_id,
+};
+
+static int __init wm9081_modinit(void)
+{
+       int ret;
+
+       ret = i2c_add_driver(&wm9081_i2c_driver);
+       if (ret != 0) {
+               printk(KERN_ERR "Failed to register WM9081 I2C driver: %d\n",
+                      ret);
+       }
+
+       return ret;
+}
+module_init(wm9081_modinit);
+
+static void __exit wm9081_exit(void)
+{
+       i2c_del_driver(&wm9081_i2c_driver);
+}
+module_exit(wm9081_exit);
+
+
+MODULE_DESCRIPTION("ASoC WM9081 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm9081.h b/sound/soc/codecs/wm9081.h
new file mode 100644 (file)
index 0000000..42d3bc7
--- /dev/null
@@ -0,0 +1,787 @@
+#ifndef WM9081_H
+#define WM9081_H
+
+/*
+ * wm9081.c  --  WM9081 ALSA SoC Audio driver
+ *
+ * Author: Mark Brown
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <sound/soc.h>
+
+extern struct snd_soc_dai wm9081_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm9081;
+
+/*
+ * SYSCLK sources
+ */
+#define WM9081_SYSCLK_MCLK      1   /* Use MCLK without FLL */
+#define WM9081_SYSCLK_FLL_MCLK  2   /* Use MCLK, enabling FLL if required */
+
+/*
+ * Register values.
+ */
+#define WM9081_SOFTWARE_RESET                   0x00
+#define WM9081_ANALOGUE_LINEOUT                 0x02
+#define WM9081_ANALOGUE_SPEAKER_PGA             0x03
+#define WM9081_VMID_CONTROL                     0x04
+#define WM9081_BIAS_CONTROL_1                   0x05
+#define WM9081_ANALOGUE_MIXER                   0x07
+#define WM9081_ANTI_POP_CONTROL                 0x08
+#define WM9081_ANALOGUE_SPEAKER_1               0x09
+#define WM9081_ANALOGUE_SPEAKER_2               0x0A
+#define WM9081_POWER_MANAGEMENT                 0x0B
+#define WM9081_CLOCK_CONTROL_1                  0x0C
+#define WM9081_CLOCK_CONTROL_2                  0x0D
+#define WM9081_CLOCK_CONTROL_3                  0x0E
+#define WM9081_FLL_CONTROL_1                    0x10
+#define WM9081_FLL_CONTROL_2                    0x11
+#define WM9081_FLL_CONTROL_3                    0x12
+#define WM9081_FLL_CONTROL_4                    0x13
+#define WM9081_FLL_CONTROL_5                    0x14
+#define WM9081_AUDIO_INTERFACE_1                0x16
+#define WM9081_AUDIO_INTERFACE_2                0x17
+#define WM9081_AUDIO_INTERFACE_3                0x18
+#define WM9081_AUDIO_INTERFACE_4                0x19
+#define WM9081_INTERRUPT_STATUS                 0x1A
+#define WM9081_INTERRUPT_STATUS_MASK            0x1B
+#define WM9081_INTERRUPT_POLARITY               0x1C
+#define WM9081_INTERRUPT_CONTROL                0x1D
+#define WM9081_DAC_DIGITAL_1                    0x1E
+#define WM9081_DAC_DIGITAL_2                    0x1F
+#define WM9081_DRC_1                            0x20
+#define WM9081_DRC_2                            0x21
+#define WM9081_DRC_3                            0x22
+#define WM9081_DRC_4                            0x23
+#define WM9081_WRITE_SEQUENCER_1                0x26
+#define WM9081_WRITE_SEQUENCER_2                0x27
+#define WM9081_MW_SLAVE_1                       0x28
+#define WM9081_EQ_1                             0x2A
+#define WM9081_EQ_2                             0x2B
+#define WM9081_EQ_3                             0x2C
+#define WM9081_EQ_4                             0x2D
+#define WM9081_EQ_5                             0x2E
+#define WM9081_EQ_6                             0x2F
+#define WM9081_EQ_7                             0x30
+#define WM9081_EQ_8                             0x31
+#define WM9081_EQ_9                             0x32
+#define WM9081_EQ_10                            0x33
+#define WM9081_EQ_11                            0x34
+#define WM9081_EQ_12                            0x35
+#define WM9081_EQ_13                            0x36
+#define WM9081_EQ_14                            0x37
+#define WM9081_EQ_15                            0x38
+#define WM9081_EQ_16                            0x39
+#define WM9081_EQ_17                            0x3A
+#define WM9081_EQ_18                            0x3B
+#define WM9081_EQ_19                            0x3C
+#define WM9081_EQ_20                            0x3D
+
+#define WM9081_REGISTER_COUNT                   55
+#define WM9081_MAX_REGISTER                     0x3D
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Software Reset
+ */
+#define WM9081_SW_RST_DEV_ID1_MASK              0xFFFF  /* SW_RST_DEV_ID1 - [15:0] */
+#define WM9081_SW_RST_DEV_ID1_SHIFT                  0  /* SW_RST_DEV_ID1 - [15:0] */
+#define WM9081_SW_RST_DEV_ID1_WIDTH                 16  /* SW_RST_DEV_ID1 - [15:0] */
+
+/*
+ * R2 (0x02) - Analogue Lineout
+ */
+#define WM9081_LINEOUT_MUTE                     0x0080  /* LINEOUT_MUTE */
+#define WM9081_LINEOUT_MUTE_MASK                0x0080  /* LINEOUT_MUTE */
+#define WM9081_LINEOUT_MUTE_SHIFT                    7  /* LINEOUT_MUTE */
+#define WM9081_LINEOUT_MUTE_WIDTH                    1  /* LINEOUT_MUTE */
+#define WM9081_LINEOUTZC                        0x0040  /* LINEOUTZC */
+#define WM9081_LINEOUTZC_MASK                   0x0040  /* LINEOUTZC */
+#define WM9081_LINEOUTZC_SHIFT                       6  /* LINEOUTZC */
+#define WM9081_LINEOUTZC_WIDTH                       1  /* LINEOUTZC */
+#define WM9081_LINEOUT_VOL_MASK                 0x003F  /* LINEOUT_VOL - [5:0] */
+#define WM9081_LINEOUT_VOL_SHIFT                     0  /* LINEOUT_VOL - [5:0] */
+#define WM9081_LINEOUT_VOL_WIDTH                     6  /* LINEOUT_VOL - [5:0] */
+
+/*
+ * R3 (0x03) - Analogue Speaker PGA
+ */
+#define WM9081_SPKPGA_MUTE                      0x0080  /* SPKPGA_MUTE */
+#define WM9081_SPKPGA_MUTE_MASK                 0x0080  /* SPKPGA_MUTE */
+#define WM9081_SPKPGA_MUTE_SHIFT                     7  /* SPKPGA_MUTE */
+#define WM9081_SPKPGA_MUTE_WIDTH                     1  /* SPKPGA_MUTE */
+#define WM9081_SPKPGAZC                         0x0040  /* SPKPGAZC */
+#define WM9081_SPKPGAZC_MASK                    0x0040  /* SPKPGAZC */
+#define WM9081_SPKPGAZC_SHIFT                        6  /* SPKPGAZC */
+#define WM9081_SPKPGAZC_WIDTH                        1  /* SPKPGAZC */
+#define WM9081_SPKPGA_VOL_MASK                  0x003F  /* SPKPGA_VOL - [5:0] */
+#define WM9081_SPKPGA_VOL_SHIFT                      0  /* SPKPGA_VOL - [5:0] */
+#define WM9081_SPKPGA_VOL_WIDTH                      6  /* SPKPGA_VOL - [5:0] */
+
+/*
+ * R4 (0x04) - VMID Control
+ */
+#define WM9081_VMID_BUF_ENA                     0x0020  /* VMID_BUF_ENA */
+#define WM9081_VMID_BUF_ENA_MASK                0x0020  /* VMID_BUF_ENA */
+#define WM9081_VMID_BUF_ENA_SHIFT                    5  /* VMID_BUF_ENA */
+#define WM9081_VMID_BUF_ENA_WIDTH                    1  /* VMID_BUF_ENA */
+#define WM9081_VMID_RAMP                        0x0008  /* VMID_RAMP */
+#define WM9081_VMID_RAMP_MASK                   0x0008  /* VMID_RAMP */
+#define WM9081_VMID_RAMP_SHIFT                       3  /* VMID_RAMP */
+#define WM9081_VMID_RAMP_WIDTH                       1  /* VMID_RAMP */
+#define WM9081_VMID_SEL_MASK                    0x0006  /* VMID_SEL - [2:1] */
+#define WM9081_VMID_SEL_SHIFT                        1  /* VMID_SEL - [2:1] */
+#define WM9081_VMID_SEL_WIDTH                        2  /* VMID_SEL - [2:1] */
+#define WM9081_VMID_FAST_ST                     0x0001  /* VMID_FAST_ST */
+#define WM9081_VMID_FAST_ST_MASK                0x0001  /* VMID_FAST_ST */
+#define WM9081_VMID_FAST_ST_SHIFT                    0  /* VMID_FAST_ST */
+#define WM9081_VMID_FAST_ST_WIDTH                    1  /* VMID_FAST_ST */
+
+/*
+ * R5 (0x05) - Bias Control 1
+ */
+#define WM9081_BIAS_SRC                         0x0040  /* BIAS_SRC */
+#define WM9081_BIAS_SRC_MASK                    0x0040  /* BIAS_SRC */
+#define WM9081_BIAS_SRC_SHIFT                        6  /* BIAS_SRC */
+#define WM9081_BIAS_SRC_WIDTH                        1  /* BIAS_SRC */
+#define WM9081_STBY_BIAS_LVL                    0x0020  /* STBY_BIAS_LVL */
+#define WM9081_STBY_BIAS_LVL_MASK               0x0020  /* STBY_BIAS_LVL */
+#define WM9081_STBY_BIAS_LVL_SHIFT                   5  /* STBY_BIAS_LVL */
+#define WM9081_STBY_BIAS_LVL_WIDTH                   1  /* STBY_BIAS_LVL */
+#define WM9081_STBY_BIAS_ENA                    0x0010  /* STBY_BIAS_ENA */
+#define WM9081_STBY_BIAS_ENA_MASK               0x0010  /* STBY_BIAS_ENA */
+#define WM9081_STBY_BIAS_ENA_SHIFT                   4  /* STBY_BIAS_ENA */
+#define WM9081_STBY_BIAS_ENA_WIDTH                   1  /* STBY_BIAS_ENA */
+#define WM9081_BIAS_LVL_MASK                    0x000C  /* BIAS_LVL - [3:2] */
+#define WM9081_BIAS_LVL_SHIFT                        2  /* BIAS_LVL - [3:2] */
+#define WM9081_BIAS_LVL_WIDTH                        2  /* BIAS_LVL - [3:2] */
+#define WM9081_BIAS_ENA                         0x0002  /* BIAS_ENA */
+#define WM9081_BIAS_ENA_MASK                    0x0002  /* BIAS_ENA */
+#define WM9081_BIAS_ENA_SHIFT                        1  /* BIAS_ENA */
+#define WM9081_BIAS_ENA_WIDTH                        1  /* BIAS_ENA */
+#define WM9081_STARTUP_BIAS_ENA                 0x0001  /* STARTUP_BIAS_ENA */
+#define WM9081_STARTUP_BIAS_ENA_MASK            0x0001  /* STARTUP_BIAS_ENA */
+#define WM9081_STARTUP_BIAS_ENA_SHIFT                0  /* STARTUP_BIAS_ENA */
+#define WM9081_STARTUP_BIAS_ENA_WIDTH                1  /* STARTUP_BIAS_ENA */
+
+/*
+ * R7 (0x07) - Analogue Mixer
+ */
+#define WM9081_DAC_SEL                          0x0010  /* DAC_SEL */
+#define WM9081_DAC_SEL_MASK                     0x0010  /* DAC_SEL */
+#define WM9081_DAC_SEL_SHIFT                         4  /* DAC_SEL */
+#define WM9081_DAC_SEL_WIDTH                         1  /* DAC_SEL */
+#define WM9081_IN2_VOL                          0x0008  /* IN2_VOL */
+#define WM9081_IN2_VOL_MASK                     0x0008  /* IN2_VOL */
+#define WM9081_IN2_VOL_SHIFT                         3  /* IN2_VOL */
+#define WM9081_IN2_VOL_WIDTH                         1  /* IN2_VOL */
+#define WM9081_IN2_ENA                          0x0004  /* IN2_ENA */
+#define WM9081_IN2_ENA_MASK                     0x0004  /* IN2_ENA */
+#define WM9081_IN2_ENA_SHIFT                         2  /* IN2_ENA */
+#define WM9081_IN2_ENA_WIDTH                         1  /* IN2_ENA */
+#define WM9081_IN1_VOL                          0x0002  /* IN1_VOL */
+#define WM9081_IN1_VOL_MASK                     0x0002  /* IN1_VOL */
+#define WM9081_IN1_VOL_SHIFT                         1  /* IN1_VOL */
+#define WM9081_IN1_VOL_WIDTH                         1  /* IN1_VOL */
+#define WM9081_IN1_ENA                          0x0001  /* IN1_ENA */
+#define WM9081_IN1_ENA_MASK                     0x0001  /* IN1_ENA */
+#define WM9081_IN1_ENA_SHIFT                         0  /* IN1_ENA */
+#define WM9081_IN1_ENA_WIDTH                         1  /* IN1_ENA */
+
+/*
+ * R8 (0x08) - Anti Pop Control
+ */
+#define WM9081_LINEOUT_DISCH                    0x0004  /* LINEOUT_DISCH */
+#define WM9081_LINEOUT_DISCH_MASK               0x0004  /* LINEOUT_DISCH */
+#define WM9081_LINEOUT_DISCH_SHIFT                   2  /* LINEOUT_DISCH */
+#define WM9081_LINEOUT_DISCH_WIDTH                   1  /* LINEOUT_DISCH */
+#define WM9081_LINEOUT_VROI                     0x0002  /* LINEOUT_VROI */
+#define WM9081_LINEOUT_VROI_MASK                0x0002  /* LINEOUT_VROI */
+#define WM9081_LINEOUT_VROI_SHIFT                    1  /* LINEOUT_VROI */
+#define WM9081_LINEOUT_VROI_WIDTH                    1  /* LINEOUT_VROI */
+#define WM9081_LINEOUT_CLAMP                    0x0001  /* LINEOUT_CLAMP */
+#define WM9081_LINEOUT_CLAMP_MASK               0x0001  /* LINEOUT_CLAMP */
+#define WM9081_LINEOUT_CLAMP_SHIFT                   0  /* LINEOUT_CLAMP */
+#define WM9081_LINEOUT_CLAMP_WIDTH                   1  /* LINEOUT_CLAMP */
+
+/*
+ * R9 (0x09) - Analogue Speaker 1
+ */
+#define WM9081_SPK_DCGAIN_MASK                  0x0038  /* SPK_DCGAIN - [5:3] */
+#define WM9081_SPK_DCGAIN_SHIFT                      3  /* SPK_DCGAIN - [5:3] */
+#define WM9081_SPK_DCGAIN_WIDTH                      3  /* SPK_DCGAIN - [5:3] */
+#define WM9081_SPK_ACGAIN_MASK                  0x0007  /* SPK_ACGAIN - [2:0] */
+#define WM9081_SPK_ACGAIN_SHIFT                      0  /* SPK_ACGAIN - [2:0] */
+#define WM9081_SPK_ACGAIN_WIDTH                      3  /* SPK_ACGAIN - [2:0] */
+
+/*
+ * R10 (0x0A) - Analogue Speaker 2
+ */
+#define WM9081_SPK_MODE                         0x0040  /* SPK_MODE */
+#define WM9081_SPK_MODE_MASK                    0x0040  /* SPK_MODE */
+#define WM9081_SPK_MODE_SHIFT                        6  /* SPK_MODE */
+#define WM9081_SPK_MODE_WIDTH                        1  /* SPK_MODE */
+#define WM9081_SPK_INV_MUTE                     0x0010  /* SPK_INV_MUTE */
+#define WM9081_SPK_INV_MUTE_MASK                0x0010  /* SPK_INV_MUTE */
+#define WM9081_SPK_INV_MUTE_SHIFT                    4  /* SPK_INV_MUTE */
+#define WM9081_SPK_INV_MUTE_WIDTH                    1  /* SPK_INV_MUTE */
+#define WM9081_OUT_SPK_CTRL                     0x0008  /* OUT_SPK_CTRL */
+#define WM9081_OUT_SPK_CTRL_MASK                0x0008  /* OUT_SPK_CTRL */
+#define WM9081_OUT_SPK_CTRL_SHIFT                    3  /* OUT_SPK_CTRL */
+#define WM9081_OUT_SPK_CTRL_WIDTH                    1  /* OUT_SPK_CTRL */
+
+/*
+ * R11 (0x0B) - Power Management
+ */
+#define WM9081_TSHUT_ENA                        0x0100  /* TSHUT_ENA */
+#define WM9081_TSHUT_ENA_MASK                   0x0100  /* TSHUT_ENA */
+#define WM9081_TSHUT_ENA_SHIFT                       8  /* TSHUT_ENA */
+#define WM9081_TSHUT_ENA_WIDTH                       1  /* TSHUT_ENA */
+#define WM9081_TSENSE_ENA                       0x0080  /* TSENSE_ENA */
+#define WM9081_TSENSE_ENA_MASK                  0x0080  /* TSENSE_ENA */
+#define WM9081_TSENSE_ENA_SHIFT                      7  /* TSENSE_ENA */
+#define WM9081_TSENSE_ENA_WIDTH                      1  /* TSENSE_ENA */
+#define WM9081_TEMP_SHUT                        0x0040  /* TEMP_SHUT */
+#define WM9081_TEMP_SHUT_MASK                   0x0040  /* TEMP_SHUT */
+#define WM9081_TEMP_SHUT_SHIFT                       6  /* TEMP_SHUT */
+#define WM9081_TEMP_SHUT_WIDTH                       1  /* TEMP_SHUT */
+#define WM9081_LINEOUT_ENA                      0x0010  /* LINEOUT_ENA */
+#define WM9081_LINEOUT_ENA_MASK                 0x0010  /* LINEOUT_ENA */
+#define WM9081_LINEOUT_ENA_SHIFT                     4  /* LINEOUT_ENA */
+#define WM9081_LINEOUT_ENA_WIDTH                     1  /* LINEOUT_ENA */
+#define WM9081_SPKPGA_ENA                       0x0004  /* SPKPGA_ENA */
+#define WM9081_SPKPGA_ENA_MASK                  0x0004  /* SPKPGA_ENA */
+#define WM9081_SPKPGA_ENA_SHIFT                      2  /* SPKPGA_ENA */
+#define WM9081_SPKPGA_ENA_WIDTH                      1  /* SPKPGA_ENA */
+#define WM9081_SPK_ENA                          0x0002  /* SPK_ENA */
+#define WM9081_SPK_ENA_MASK                     0x0002  /* SPK_ENA */
+#define WM9081_SPK_ENA_SHIFT                         1  /* SPK_ENA */
+#define WM9081_SPK_ENA_WIDTH                         1  /* SPK_ENA */
+#define WM9081_DAC_ENA                          0x0001  /* DAC_ENA */
+#define WM9081_DAC_ENA_MASK                     0x0001  /* DAC_ENA */
+#define WM9081_DAC_ENA_SHIFT                         0  /* DAC_ENA */
+#define WM9081_DAC_ENA_WIDTH                         1  /* DAC_ENA */
+
+/*
+ * R12 (0x0C) - Clock Control 1
+ */
+#define WM9081_CLK_OP_DIV_MASK                  0x1C00  /* CLK_OP_DIV - [12:10] */
+#define WM9081_CLK_OP_DIV_SHIFT                     10  /* CLK_OP_DIV - [12:10] */
+#define WM9081_CLK_OP_DIV_WIDTH                      3  /* CLK_OP_DIV - [12:10] */
+#define WM9081_CLK_TO_DIV_MASK                  0x0300  /* CLK_TO_DIV - [9:8] */
+#define WM9081_CLK_TO_DIV_SHIFT                      8  /* CLK_TO_DIV - [9:8] */
+#define WM9081_CLK_TO_DIV_WIDTH                      2  /* CLK_TO_DIV - [9:8] */
+#define WM9081_MCLKDIV2                         0x0080  /* MCLKDIV2 */
+#define WM9081_MCLKDIV2_MASK                    0x0080  /* MCLKDIV2 */
+#define WM9081_MCLKDIV2_SHIFT                        7  /* MCLKDIV2 */
+#define WM9081_MCLKDIV2_WIDTH                        1  /* MCLKDIV2 */
+
+/*
+ * R13 (0x0D) - Clock Control 2
+ */
+#define WM9081_CLK_SYS_RATE_MASK                0x00F0  /* CLK_SYS_RATE - [7:4] */
+#define WM9081_CLK_SYS_RATE_SHIFT                    4  /* CLK_SYS_RATE - [7:4] */
+#define WM9081_CLK_SYS_RATE_WIDTH                    4  /* CLK_SYS_RATE - [7:4] */
+#define WM9081_SAMPLE_RATE_MASK                 0x000F  /* SAMPLE_RATE - [3:0] */
+#define WM9081_SAMPLE_RATE_SHIFT                     0  /* SAMPLE_RATE - [3:0] */
+#define WM9081_SAMPLE_RATE_WIDTH                     4  /* SAMPLE_RATE - [3:0] */
+
+/*
+ * R14 (0x0E) - Clock Control 3
+ */
+#define WM9081_CLK_SRC_SEL                      0x2000  /* CLK_SRC_SEL */
+#define WM9081_CLK_SRC_SEL_MASK                 0x2000  /* CLK_SRC_SEL */
+#define WM9081_CLK_SRC_SEL_SHIFT                    13  /* CLK_SRC_SEL */
+#define WM9081_CLK_SRC_SEL_WIDTH                     1  /* CLK_SRC_SEL */
+#define WM9081_CLK_OP_ENA                       0x0020  /* CLK_OP_ENA */
+#define WM9081_CLK_OP_ENA_MASK                  0x0020  /* CLK_OP_ENA */
+#define WM9081_CLK_OP_ENA_SHIFT                      5  /* CLK_OP_ENA */
+#define WM9081_CLK_OP_ENA_WIDTH                      1  /* CLK_OP_ENA */
+#define WM9081_CLK_TO_ENA                       0x0004  /* CLK_TO_ENA */
+#define WM9081_CLK_TO_ENA_MASK                  0x0004  /* CLK_TO_ENA */
+#define WM9081_CLK_TO_ENA_SHIFT                      2  /* CLK_TO_ENA */
+#define WM9081_CLK_TO_ENA_WIDTH                      1  /* CLK_TO_ENA */
+#define WM9081_CLK_DSP_ENA                      0x0002  /* CLK_DSP_ENA */
+#define WM9081_CLK_DSP_ENA_MASK                 0x0002  /* CLK_DSP_ENA */
+#define WM9081_CLK_DSP_ENA_SHIFT                     1  /* CLK_DSP_ENA */
+#define WM9081_CLK_DSP_ENA_WIDTH                     1  /* CLK_DSP_ENA */
+#define WM9081_CLK_SYS_ENA                      0x0001  /* CLK_SYS_ENA */
+#define WM9081_CLK_SYS_ENA_MASK                 0x0001  /* CLK_SYS_ENA */
+#define WM9081_CLK_SYS_ENA_SHIFT                     0  /* CLK_SYS_ENA */
+#define WM9081_CLK_SYS_ENA_WIDTH                     1  /* CLK_SYS_ENA */
+
+/*
+ * R16 (0x10) - FLL Control 1
+ */
+#define WM9081_FLL_HOLD                         0x0008  /* FLL_HOLD */
+#define WM9081_FLL_HOLD_MASK                    0x0008  /* FLL_HOLD */
+#define WM9081_FLL_HOLD_SHIFT                        3  /* FLL_HOLD */
+#define WM9081_FLL_HOLD_WIDTH                        1  /* FLL_HOLD */
+#define WM9081_FLL_FRAC                         0x0004  /* FLL_FRAC */
+#define WM9081_FLL_FRAC_MASK                    0x0004  /* FLL_FRAC */
+#define WM9081_FLL_FRAC_SHIFT                        2  /* FLL_FRAC */
+#define WM9081_FLL_FRAC_WIDTH                        1  /* FLL_FRAC */
+#define WM9081_FLL_ENA                          0x0001  /* FLL_ENA */
+#define WM9081_FLL_ENA_MASK                     0x0001  /* FLL_ENA */
+#define WM9081_FLL_ENA_SHIFT                         0  /* FLL_ENA */
+#define WM9081_FLL_ENA_WIDTH                         1  /* FLL_ENA */
+
+/*
+ * R17 (0x11) - FLL Control 2
+ */
+#define WM9081_FLL_OUTDIV_MASK                  0x0700  /* FLL_OUTDIV - [10:8] */
+#define WM9081_FLL_OUTDIV_SHIFT                      8  /* FLL_OUTDIV - [10:8] */
+#define WM9081_FLL_OUTDIV_WIDTH                      3  /* FLL_OUTDIV - [10:8] */
+#define WM9081_FLL_CTRL_RATE_MASK               0x0070  /* FLL_CTRL_RATE - [6:4] */
+#define WM9081_FLL_CTRL_RATE_SHIFT                   4  /* FLL_CTRL_RATE - [6:4] */
+#define WM9081_FLL_CTRL_RATE_WIDTH                   3  /* FLL_CTRL_RATE - [6:4] */
+#define WM9081_FLL_FRATIO_MASK                  0x0007  /* FLL_FRATIO - [2:0] */
+#define WM9081_FLL_FRATIO_SHIFT                      0  /* FLL_FRATIO - [2:0] */
+#define WM9081_FLL_FRATIO_WIDTH                      3  /* FLL_FRATIO - [2:0] */
+
+/*
+ * R18 (0x12) - FLL Control 3
+ */
+#define WM9081_FLL_K_MASK                       0xFFFF  /* FLL_K - [15:0] */
+#define WM9081_FLL_K_SHIFT                           0  /* FLL_K - [15:0] */
+#define WM9081_FLL_K_WIDTH                          16  /* FLL_K - [15:0] */
+
+/*
+ * R19 (0x13) - FLL Control 4
+ */
+#define WM9081_FLL_N_MASK                       0x7FE0  /* FLL_N - [14:5] */
+#define WM9081_FLL_N_SHIFT                           5  /* FLL_N - [14:5] */
+#define WM9081_FLL_N_WIDTH                          10  /* FLL_N - [14:5] */
+#define WM9081_FLL_GAIN_MASK                    0x000F  /* FLL_GAIN - [3:0] */
+#define WM9081_FLL_GAIN_SHIFT                        0  /* FLL_GAIN - [3:0] */
+#define WM9081_FLL_GAIN_WIDTH                        4  /* FLL_GAIN - [3:0] */
+
+/*
+ * R20 (0x14) - FLL Control 5
+ */
+#define WM9081_FLL_CLK_REF_DIV_MASK             0x0018  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM9081_FLL_CLK_REF_DIV_SHIFT                 3  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM9081_FLL_CLK_REF_DIV_WIDTH                 2  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM9081_FLL_CLK_SRC_MASK                 0x0003  /* FLL_CLK_SRC - [1:0] */
+#define WM9081_FLL_CLK_SRC_SHIFT                     0  /* FLL_CLK_SRC - [1:0] */
+#define WM9081_FLL_CLK_SRC_WIDTH                     2  /* FLL_CLK_SRC - [1:0] */
+
+/*
+ * R22 (0x16) - Audio Interface 1
+ */
+#define WM9081_AIFDAC_CHAN                      0x0040  /* AIFDAC_CHAN */
+#define WM9081_AIFDAC_CHAN_MASK                 0x0040  /* AIFDAC_CHAN */
+#define WM9081_AIFDAC_CHAN_SHIFT                     6  /* AIFDAC_CHAN */
+#define WM9081_AIFDAC_CHAN_WIDTH                     1  /* AIFDAC_CHAN */
+#define WM9081_AIFDAC_TDM_SLOT_MASK             0x0030  /* AIFDAC_TDM_SLOT - [5:4] */
+#define WM9081_AIFDAC_TDM_SLOT_SHIFT                 4  /* AIFDAC_TDM_SLOT - [5:4] */
+#define WM9081_AIFDAC_TDM_SLOT_WIDTH                 2  /* AIFDAC_TDM_SLOT - [5:4] */
+#define WM9081_AIFDAC_TDM_MODE_MASK             0x000C  /* AIFDAC_TDM_MODE - [3:2] */
+#define WM9081_AIFDAC_TDM_MODE_SHIFT                 2  /* AIFDAC_TDM_MODE - [3:2] */
+#define WM9081_AIFDAC_TDM_MODE_WIDTH                 2  /* AIFDAC_TDM_MODE - [3:2] */
+#define WM9081_DAC_COMP                         0x0002  /* DAC_COMP */
+#define WM9081_DAC_COMP_MASK                    0x0002  /* DAC_COMP */
+#define WM9081_DAC_COMP_SHIFT                        1  /* DAC_COMP */
+#define WM9081_DAC_COMP_WIDTH                        1  /* DAC_COMP */
+#define WM9081_DAC_COMPMODE                     0x0001  /* DAC_COMPMODE */
+#define WM9081_DAC_COMPMODE_MASK                0x0001  /* DAC_COMPMODE */
+#define WM9081_DAC_COMPMODE_SHIFT                    0  /* DAC_COMPMODE */
+#define WM9081_DAC_COMPMODE_WIDTH                    1  /* DAC_COMPMODE */
+
+/*
+ * R23 (0x17) - Audio Interface 2
+ */
+#define WM9081_AIF_TRIS                         0x0200  /* AIF_TRIS */
+#define WM9081_AIF_TRIS_MASK                    0x0200  /* AIF_TRIS */
+#define WM9081_AIF_TRIS_SHIFT                        9  /* AIF_TRIS */
+#define WM9081_AIF_TRIS_WIDTH                        1  /* AIF_TRIS */
+#define WM9081_DAC_DAT_INV                      0x0100  /* DAC_DAT_INV */
+#define WM9081_DAC_DAT_INV_MASK                 0x0100  /* DAC_DAT_INV */
+#define WM9081_DAC_DAT_INV_SHIFT                     8  /* DAC_DAT_INV */
+#define WM9081_DAC_DAT_INV_WIDTH                     1  /* DAC_DAT_INV */
+#define WM9081_AIF_BCLK_INV                     0x0080  /* AIF_BCLK_INV */
+#define WM9081_AIF_BCLK_INV_MASK                0x0080  /* AIF_BCLK_INV */
+#define WM9081_AIF_BCLK_INV_SHIFT                    7  /* AIF_BCLK_INV */
+#define WM9081_AIF_BCLK_INV_WIDTH                    1  /* AIF_BCLK_INV */
+#define WM9081_BCLK_DIR                         0x0040  /* BCLK_DIR */
+#define WM9081_BCLK_DIR_MASK                    0x0040  /* BCLK_DIR */
+#define WM9081_BCLK_DIR_SHIFT                        6  /* BCLK_DIR */
+#define WM9081_BCLK_DIR_WIDTH                        1  /* BCLK_DIR */
+#define WM9081_LRCLK_DIR                        0x0020  /* LRCLK_DIR */
+#define WM9081_LRCLK_DIR_MASK                   0x0020  /* LRCLK_DIR */
+#define WM9081_LRCLK_DIR_SHIFT                       5  /* LRCLK_DIR */
+#define WM9081_LRCLK_DIR_WIDTH                       1  /* LRCLK_DIR */
+#define WM9081_AIF_LRCLK_INV                    0x0010  /* AIF_LRCLK_INV */
+#define WM9081_AIF_LRCLK_INV_MASK               0x0010  /* AIF_LRCLK_INV */
+#define WM9081_AIF_LRCLK_INV_SHIFT                   4  /* AIF_LRCLK_INV */
+#define WM9081_AIF_LRCLK_INV_WIDTH                   1  /* AIF_LRCLK_INV */
+#define WM9081_AIF_WL_MASK                      0x000C  /* AIF_WL - [3:2] */
+#define WM9081_AIF_WL_SHIFT                          2  /* AIF_WL - [3:2] */
+#define WM9081_AIF_WL_WIDTH                          2  /* AIF_WL - [3:2] */
+#define WM9081_AIF_FMT_MASK                     0x0003  /* AIF_FMT - [1:0] */
+#define WM9081_AIF_FMT_SHIFT                         0  /* AIF_FMT - [1:0] */
+#define WM9081_AIF_FMT_WIDTH                         2  /* AIF_FMT - [1:0] */
+
+/*
+ * R24 (0x18) - Audio Interface 3
+ */
+#define WM9081_BCLK_DIV_MASK                    0x001F  /* BCLK_DIV - [4:0] */
+#define WM9081_BCLK_DIV_SHIFT                        0  /* BCLK_DIV - [4:0] */
+#define WM9081_BCLK_DIV_WIDTH                        5  /* BCLK_DIV - [4:0] */
+
+/*
+ * R25 (0x19) - Audio Interface 4
+ */
+#define WM9081_LRCLK_RATE_MASK                  0x07FF  /* LRCLK_RATE - [10:0] */
+#define WM9081_LRCLK_RATE_SHIFT                      0  /* LRCLK_RATE - [10:0] */
+#define WM9081_LRCLK_RATE_WIDTH                     11  /* LRCLK_RATE - [10:0] */
+
+/*
+ * R26 (0x1A) - Interrupt Status
+ */
+#define WM9081_WSEQ_BUSY_EINT                   0x0004  /* WSEQ_BUSY_EINT */
+#define WM9081_WSEQ_BUSY_EINT_MASK              0x0004  /* WSEQ_BUSY_EINT */
+#define WM9081_WSEQ_BUSY_EINT_SHIFT                  2  /* WSEQ_BUSY_EINT */
+#define WM9081_WSEQ_BUSY_EINT_WIDTH                  1  /* WSEQ_BUSY_EINT */
+#define WM9081_TSHUT_EINT                       0x0001  /* TSHUT_EINT */
+#define WM9081_TSHUT_EINT_MASK                  0x0001  /* TSHUT_EINT */
+#define WM9081_TSHUT_EINT_SHIFT                      0  /* TSHUT_EINT */
+#define WM9081_TSHUT_EINT_WIDTH                      1  /* TSHUT_EINT */
+
+/*
+ * R27 (0x1B) - Interrupt Status Mask
+ */
+#define WM9081_IM_WSEQ_BUSY_EINT                0x0004  /* IM_WSEQ_BUSY_EINT */
+#define WM9081_IM_WSEQ_BUSY_EINT_MASK           0x0004  /* IM_WSEQ_BUSY_EINT */
+#define WM9081_IM_WSEQ_BUSY_EINT_SHIFT               2  /* IM_WSEQ_BUSY_EINT */
+#define WM9081_IM_WSEQ_BUSY_EINT_WIDTH               1  /* IM_WSEQ_BUSY_EINT */
+#define WM9081_IM_TSHUT_EINT                    0x0001  /* IM_TSHUT_EINT */
+#define WM9081_IM_TSHUT_EINT_MASK               0x0001  /* IM_TSHUT_EINT */
+#define WM9081_IM_TSHUT_EINT_SHIFT                   0  /* IM_TSHUT_EINT */
+#define WM9081_IM_TSHUT_EINT_WIDTH                   1  /* IM_TSHUT_EINT */
+
+/*
+ * R28 (0x1C) - Interrupt Polarity
+ */
+#define WM9081_TSHUT_INV                        0x0001  /* TSHUT_INV */
+#define WM9081_TSHUT_INV_MASK                   0x0001  /* TSHUT_INV */
+#define WM9081_TSHUT_INV_SHIFT                       0  /* TSHUT_INV */
+#define WM9081_TSHUT_INV_WIDTH                       1  /* TSHUT_INV */
+
+/*
+ * R29 (0x1D) - Interrupt Control
+ */
+#define WM9081_IRQ_POL                          0x8000  /* IRQ_POL */
+#define WM9081_IRQ_POL_MASK                     0x8000  /* IRQ_POL */
+#define WM9081_IRQ_POL_SHIFT                        15  /* IRQ_POL */
+#define WM9081_IRQ_POL_WIDTH                         1  /* IRQ_POL */
+#define WM9081_IRQ_OP_CTRL                      0x0001  /* IRQ_OP_CTRL */
+#define WM9081_IRQ_OP_CTRL_MASK                 0x0001  /* IRQ_OP_CTRL */
+#define WM9081_IRQ_OP_CTRL_SHIFT                     0  /* IRQ_OP_CTRL */
+#define WM9081_IRQ_OP_CTRL_WIDTH                     1  /* IRQ_OP_CTRL */
+
+/*
+ * R30 (0x1E) - DAC Digital 1
+ */
+#define WM9081_DAC_VOL_MASK                     0x00FF  /* DAC_VOL - [7:0] */
+#define WM9081_DAC_VOL_SHIFT                         0  /* DAC_VOL - [7:0] */
+#define WM9081_DAC_VOL_WIDTH                         8  /* DAC_VOL - [7:0] */
+
+/*
+ * R31 (0x1F) - DAC Digital 2
+ */
+#define WM9081_DAC_MUTERATE                     0x0400  /* DAC_MUTERATE */
+#define WM9081_DAC_MUTERATE_MASK                0x0400  /* DAC_MUTERATE */
+#define WM9081_DAC_MUTERATE_SHIFT                   10  /* DAC_MUTERATE */
+#define WM9081_DAC_MUTERATE_WIDTH                    1  /* DAC_MUTERATE */
+#define WM9081_DAC_MUTEMODE                     0x0200  /* DAC_MUTEMODE */
+#define WM9081_DAC_MUTEMODE_MASK                0x0200  /* DAC_MUTEMODE */
+#define WM9081_DAC_MUTEMODE_SHIFT                    9  /* DAC_MUTEMODE */
+#define WM9081_DAC_MUTEMODE_WIDTH                    1  /* DAC_MUTEMODE */
+#define WM9081_DAC_MUTE                         0x0008  /* DAC_MUTE */
+#define WM9081_DAC_MUTE_MASK                    0x0008  /* DAC_MUTE */
+#define WM9081_DAC_MUTE_SHIFT                        3  /* DAC_MUTE */
+#define WM9081_DAC_MUTE_WIDTH                        1  /* DAC_MUTE */
+#define WM9081_DEEMPH_MASK                      0x0006  /* DEEMPH - [2:1] */
+#define WM9081_DEEMPH_SHIFT                          1  /* DEEMPH - [2:1] */
+#define WM9081_DEEMPH_WIDTH                          2  /* DEEMPH - [2:1] */
+
+/*
+ * R32 (0x20) - DRC 1
+ */
+#define WM9081_DRC_ENA                          0x8000  /* DRC_ENA */
+#define WM9081_DRC_ENA_MASK                     0x8000  /* DRC_ENA */
+#define WM9081_DRC_ENA_SHIFT                        15  /* DRC_ENA */
+#define WM9081_DRC_ENA_WIDTH                         1  /* DRC_ENA */
+#define WM9081_DRC_STARTUP_GAIN_MASK            0x07C0  /* DRC_STARTUP_GAIN - [10:6] */
+#define WM9081_DRC_STARTUP_GAIN_SHIFT                6  /* DRC_STARTUP_GAIN - [10:6] */
+#define WM9081_DRC_STARTUP_GAIN_WIDTH                5  /* DRC_STARTUP_GAIN - [10:6] */
+#define WM9081_DRC_FF_DLY                       0x0020  /* DRC_FF_DLY */
+#define WM9081_DRC_FF_DLY_MASK                  0x0020  /* DRC_FF_DLY */
+#define WM9081_DRC_FF_DLY_SHIFT                      5  /* DRC_FF_DLY */
+#define WM9081_DRC_FF_DLY_WIDTH                      1  /* DRC_FF_DLY */
+#define WM9081_DRC_QR                           0x0004  /* DRC_QR */
+#define WM9081_DRC_QR_MASK                      0x0004  /* DRC_QR */
+#define WM9081_DRC_QR_SHIFT                          2  /* DRC_QR */
+#define WM9081_DRC_QR_WIDTH                          1  /* DRC_QR */
+#define WM9081_DRC_ANTICLIP                     0x0002  /* DRC_ANTICLIP */
+#define WM9081_DRC_ANTICLIP_MASK                0x0002  /* DRC_ANTICLIP */
+#define WM9081_DRC_ANTICLIP_SHIFT                    1  /* DRC_ANTICLIP */
+#define WM9081_DRC_ANTICLIP_WIDTH                    1  /* DRC_ANTICLIP */
+
+/*
+ * R33 (0x21) - DRC 2
+ */
+#define WM9081_DRC_ATK_MASK                     0xF000  /* DRC_ATK - [15:12] */
+#define WM9081_DRC_ATK_SHIFT                        12  /* DRC_ATK - [15:12] */
+#define WM9081_DRC_ATK_WIDTH                         4  /* DRC_ATK - [15:12] */
+#define WM9081_DRC_DCY_MASK                     0x0F00  /* DRC_DCY - [11:8] */
+#define WM9081_DRC_DCY_SHIFT                         8  /* DRC_DCY - [11:8] */
+#define WM9081_DRC_DCY_WIDTH                         4  /* DRC_DCY - [11:8] */
+#define WM9081_DRC_QR_THR_MASK                  0x00C0  /* DRC_QR_THR - [7:6] */
+#define WM9081_DRC_QR_THR_SHIFT                      6  /* DRC_QR_THR - [7:6] */
+#define WM9081_DRC_QR_THR_WIDTH                      2  /* DRC_QR_THR - [7:6] */
+#define WM9081_DRC_QR_DCY_MASK                  0x0030  /* DRC_QR_DCY - [5:4] */
+#define WM9081_DRC_QR_DCY_SHIFT                      4  /* DRC_QR_DCY - [5:4] */
+#define WM9081_DRC_QR_DCY_WIDTH                      2  /* DRC_QR_DCY - [5:4] */
+#define WM9081_DRC_MINGAIN_MASK                 0x000C  /* DRC_MINGAIN - [3:2] */
+#define WM9081_DRC_MINGAIN_SHIFT                     2  /* DRC_MINGAIN - [3:2] */
+#define WM9081_DRC_MINGAIN_WIDTH                     2  /* DRC_MINGAIN - [3:2] */
+#define WM9081_DRC_MAXGAIN_MASK                 0x0003  /* DRC_MAXGAIN - [1:0] */
+#define WM9081_DRC_MAXGAIN_SHIFT                     0  /* DRC_MAXGAIN - [1:0] */
+#define WM9081_DRC_MAXGAIN_WIDTH                     2  /* DRC_MAXGAIN - [1:0] */
+
+/*
+ * R34 (0x22) - DRC 3
+ */
+#define WM9081_DRC_HI_COMP_MASK                 0x0038  /* DRC_HI_COMP - [5:3] */
+#define WM9081_DRC_HI_COMP_SHIFT                     3  /* DRC_HI_COMP - [5:3] */
+#define WM9081_DRC_HI_COMP_WIDTH                     3  /* DRC_HI_COMP - [5:3] */
+#define WM9081_DRC_LO_COMP_MASK                 0x0007  /* DRC_LO_COMP - [2:0] */
+#define WM9081_DRC_LO_COMP_SHIFT                     0  /* DRC_LO_COMP - [2:0] */
+#define WM9081_DRC_LO_COMP_WIDTH                     3  /* DRC_LO_COMP - [2:0] */
+
+/*
+ * R35 (0x23) - DRC 4
+ */
+#define WM9081_DRC_KNEE_IP_MASK                 0x07E0  /* DRC_KNEE_IP - [10:5] */
+#define WM9081_DRC_KNEE_IP_SHIFT                     5  /* DRC_KNEE_IP - [10:5] */
+#define WM9081_DRC_KNEE_IP_WIDTH                     6  /* DRC_KNEE_IP - [10:5] */
+#define WM9081_DRC_KNEE_OP_MASK                 0x001F  /* DRC_KNEE_OP - [4:0] */
+#define WM9081_DRC_KNEE_OP_SHIFT                     0  /* DRC_KNEE_OP - [4:0] */
+#define WM9081_DRC_KNEE_OP_WIDTH                     5  /* DRC_KNEE_OP - [4:0] */
+
+/*
+ * R38 (0x26) - Write Sequencer 1
+ */
+#define WM9081_WSEQ_ENA                         0x8000  /* WSEQ_ENA */
+#define WM9081_WSEQ_ENA_MASK                    0x8000  /* WSEQ_ENA */
+#define WM9081_WSEQ_ENA_SHIFT                       15  /* WSEQ_ENA */
+#define WM9081_WSEQ_ENA_WIDTH                        1  /* WSEQ_ENA */
+#define WM9081_WSEQ_ABORT                       0x0200  /* WSEQ_ABORT */
+#define WM9081_WSEQ_ABORT_MASK                  0x0200  /* WSEQ_ABORT */
+#define WM9081_WSEQ_ABORT_SHIFT                      9  /* WSEQ_ABORT */
+#define WM9081_WSEQ_ABORT_WIDTH                      1  /* WSEQ_ABORT */
+#define WM9081_WSEQ_START                       0x0100  /* WSEQ_START */
+#define WM9081_WSEQ_START_MASK                  0x0100  /* WSEQ_START */
+#define WM9081_WSEQ_START_SHIFT                      8  /* WSEQ_START */
+#define WM9081_WSEQ_START_WIDTH                      1  /* WSEQ_START */
+#define WM9081_WSEQ_START_INDEX_MASK            0x007F  /* WSEQ_START_INDEX - [6:0] */
+#define WM9081_WSEQ_START_INDEX_SHIFT                0  /* WSEQ_START_INDEX - [6:0] */
+#define WM9081_WSEQ_START_INDEX_WIDTH                7  /* WSEQ_START_INDEX - [6:0] */
+
+/*
+ * R39 (0x27) - Write Sequencer 2
+ */
+#define WM9081_WSEQ_CURRENT_INDEX_MASK          0x07F0  /* WSEQ_CURRENT_INDEX - [10:4] */
+#define WM9081_WSEQ_CURRENT_INDEX_SHIFT              4  /* WSEQ_CURRENT_INDEX - [10:4] */
+#define WM9081_WSEQ_CURRENT_INDEX_WIDTH              7  /* WSEQ_CURRENT_INDEX - [10:4] */
+#define WM9081_WSEQ_BUSY                        0x0001  /* WSEQ_BUSY */
+#define WM9081_WSEQ_BUSY_MASK                   0x0001  /* WSEQ_BUSY */
+#define WM9081_WSEQ_BUSY_SHIFT                       0  /* WSEQ_BUSY */
+#define WM9081_WSEQ_BUSY_WIDTH                       1  /* WSEQ_BUSY */
+
+/*
+ * R40 (0x28) - MW Slave 1
+ */
+#define WM9081_SPI_CFG                          0x0020  /* SPI_CFG */
+#define WM9081_SPI_CFG_MASK                     0x0020  /* SPI_CFG */
+#define WM9081_SPI_CFG_SHIFT                         5  /* SPI_CFG */
+#define WM9081_SPI_CFG_WIDTH                         1  /* SPI_CFG */
+#define WM9081_SPI_4WIRE                        0x0010  /* SPI_4WIRE */
+#define WM9081_SPI_4WIRE_MASK                   0x0010  /* SPI_4WIRE */
+#define WM9081_SPI_4WIRE_SHIFT                       4  /* SPI_4WIRE */
+#define WM9081_SPI_4WIRE_WIDTH                       1  /* SPI_4WIRE */
+#define WM9081_ARA_ENA                          0x0008  /* ARA_ENA */
+#define WM9081_ARA_ENA_MASK                     0x0008  /* ARA_ENA */
+#define WM9081_ARA_ENA_SHIFT                         3  /* ARA_ENA */
+#define WM9081_ARA_ENA_WIDTH                         1  /* ARA_ENA */
+#define WM9081_AUTO_INC                         0x0002  /* AUTO_INC */
+#define WM9081_AUTO_INC_MASK                    0x0002  /* AUTO_INC */
+#define WM9081_AUTO_INC_SHIFT                        1  /* AUTO_INC */
+#define WM9081_AUTO_INC_WIDTH                        1  /* AUTO_INC */
+
+/*
+ * R42 (0x2A) - EQ 1
+ */
+#define WM9081_EQ_B1_GAIN_MASK                  0xF800  /* EQ_B1_GAIN - [15:11] */
+#define WM9081_EQ_B1_GAIN_SHIFT                     11  /* EQ_B1_GAIN - [15:11] */
+#define WM9081_EQ_B1_GAIN_WIDTH                      5  /* EQ_B1_GAIN - [15:11] */
+#define WM9081_EQ_B2_GAIN_MASK                  0x07C0  /* EQ_B2_GAIN - [10:6] */
+#define WM9081_EQ_B2_GAIN_SHIFT                      6  /* EQ_B2_GAIN - [10:6] */
+#define WM9081_EQ_B2_GAIN_WIDTH                      5  /* EQ_B2_GAIN - [10:6] */
+#define WM9081_EQ_B4_GAIN_MASK                  0x003E  /* EQ_B4_GAIN - [5:1] */
+#define WM9081_EQ_B4_GAIN_SHIFT                      1  /* EQ_B4_GAIN - [5:1] */
+#define WM9081_EQ_B4_GAIN_WIDTH                      5  /* EQ_B4_GAIN - [5:1] */
+#define WM9081_EQ_ENA                           0x0001  /* EQ_ENA */
+#define WM9081_EQ_ENA_MASK                      0x0001  /* EQ_ENA */
+#define WM9081_EQ_ENA_SHIFT                          0  /* EQ_ENA */
+#define WM9081_EQ_ENA_WIDTH                          1  /* EQ_ENA */
+
+/*
+ * R43 (0x2B) - EQ 2
+ */
+#define WM9081_EQ_B3_GAIN_MASK                  0xF800  /* EQ_B3_GAIN - [15:11] */
+#define WM9081_EQ_B3_GAIN_SHIFT                     11  /* EQ_B3_GAIN - [15:11] */
+#define WM9081_EQ_B3_GAIN_WIDTH                      5  /* EQ_B3_GAIN - [15:11] */
+#define WM9081_EQ_B5_GAIN_MASK                  0x07C0  /* EQ_B5_GAIN - [10:6] */
+#define WM9081_EQ_B5_GAIN_SHIFT                      6  /* EQ_B5_GAIN - [10:6] */
+#define WM9081_EQ_B5_GAIN_WIDTH                      5  /* EQ_B5_GAIN - [10:6] */
+
+/*
+ * R44 (0x2C) - EQ 3
+ */
+#define WM9081_EQ_B1_A_MASK                     0xFFFF  /* EQ_B1_A - [15:0] */
+#define WM9081_EQ_B1_A_SHIFT                         0  /* EQ_B1_A - [15:0] */
+#define WM9081_EQ_B1_A_WIDTH                        16  /* EQ_B1_A - [15:0] */
+
+/*
+ * R45 (0x2D) - EQ 4
+ */
+#define WM9081_EQ_B1_B_MASK                     0xFFFF  /* EQ_B1_B - [15:0] */
+#define WM9081_EQ_B1_B_SHIFT                         0  /* EQ_B1_B - [15:0] */
+#define WM9081_EQ_B1_B_WIDTH                        16  /* EQ_B1_B - [15:0] */
+
+/*
+ * R46 (0x2E) - EQ 5
+ */
+#define WM9081_EQ_B1_PG_MASK                    0xFFFF  /* EQ_B1_PG - [15:0] */
+#define WM9081_EQ_B1_PG_SHIFT                        0  /* EQ_B1_PG - [15:0] */
+#define WM9081_EQ_B1_PG_WIDTH                       16  /* EQ_B1_PG - [15:0] */
+
+/*
+ * R47 (0x2F) - EQ 6
+ */
+#define WM9081_EQ_B2_A_MASK                     0xFFFF  /* EQ_B2_A - [15:0] */
+#define WM9081_EQ_B2_A_SHIFT                         0  /* EQ_B2_A - [15:0] */
+#define WM9081_EQ_B2_A_WIDTH                        16  /* EQ_B2_A - [15:0] */
+
+/*
+ * R48 (0x30) - EQ 7
+ */
+#define WM9081_EQ_B2_B_MASK                     0xFFFF  /* EQ_B2_B - [15:0] */
+#define WM9081_EQ_B2_B_SHIFT                         0  /* EQ_B2_B - [15:0] */
+#define WM9081_EQ_B2_B_WIDTH                        16  /* EQ_B2_B - [15:0] */
+
+/*
+ * R49 (0x31) - EQ 8
+ */
+#define WM9081_EQ_B2_C_MASK                     0xFFFF  /* EQ_B2_C - [15:0] */
+#define WM9081_EQ_B2_C_SHIFT                         0  /* EQ_B2_C - [15:0] */
+#define WM9081_EQ_B2_C_WIDTH                        16  /* EQ_B2_C - [15:0] */
+
+/*
+ * R50 (0x32) - EQ 9
+ */
+#define WM9081_EQ_B2_PG_MASK                    0xFFFF  /* EQ_B2_PG - [15:0] */
+#define WM9081_EQ_B2_PG_SHIFT                        0  /* EQ_B2_PG - [15:0] */
+#define WM9081_EQ_B2_PG_WIDTH                       16  /* EQ_B2_PG - [15:0] */
+
+/*
+ * R51 (0x33) - EQ 10
+ */
+#define WM9081_EQ_B4_A_MASK                     0xFFFF  /* EQ_B4_A - [15:0] */
+#define WM9081_EQ_B4_A_SHIFT                         0  /* EQ_B4_A - [15:0] */
+#define WM9081_EQ_B4_A_WIDTH                        16  /* EQ_B4_A - [15:0] */
+
+/*
+ * R52 (0x34) - EQ 11
+ */
+#define WM9081_EQ_B4_B_MASK                     0xFFFF  /* EQ_B4_B - [15:0] */
+#define WM9081_EQ_B4_B_SHIFT                         0  /* EQ_B4_B - [15:0] */
+#define WM9081_EQ_B4_B_WIDTH                        16  /* EQ_B4_B - [15:0] */
+
+/*
+ * R53 (0x35) - EQ 12
+ */
+#define WM9081_EQ_B4_C_MASK                     0xFFFF  /* EQ_B4_C - [15:0] */
+#define WM9081_EQ_B4_C_SHIFT                         0  /* EQ_B4_C - [15:0] */
+#define WM9081_EQ_B4_C_WIDTH                        16  /* EQ_B4_C - [15:0] */
+
+/*
+ * R54 (0x36) - EQ 13
+ */
+#define WM9081_EQ_B4_PG_MASK                    0xFFFF  /* EQ_B4_PG - [15:0] */
+#define WM9081_EQ_B4_PG_SHIFT                        0  /* EQ_B4_PG - [15:0] */
+#define WM9081_EQ_B4_PG_WIDTH                       16  /* EQ_B4_PG - [15:0] */
+
+/*
+ * R55 (0x37) - EQ 14
+ */
+#define WM9081_EQ_B3_A_MASK                     0xFFFF  /* EQ_B3_A - [15:0] */
+#define WM9081_EQ_B3_A_SHIFT                         0  /* EQ_B3_A - [15:0] */
+#define WM9081_EQ_B3_A_WIDTH                        16  /* EQ_B3_A - [15:0] */
+
+/*
+ * R56 (0x38) - EQ 15
+ */
+#define WM9081_EQ_B3_B_MASK                     0xFFFF  /* EQ_B3_B - [15:0] */
+#define WM9081_EQ_B3_B_SHIFT                         0  /* EQ_B3_B - [15:0] */
+#define WM9081_EQ_B3_B_WIDTH                        16  /* EQ_B3_B - [15:0] */
+
+/*
+ * R57 (0x39) - EQ 16
+ */
+#define WM9081_EQ_B3_C_MASK                     0xFFFF  /* EQ_B3_C - [15:0] */
+#define WM9081_EQ_B3_C_SHIFT                         0  /* EQ_B3_C - [15:0] */
+#define WM9081_EQ_B3_C_WIDTH                        16  /* EQ_B3_C - [15:0] */
+
+/*
+ * R58 (0x3A) - EQ 17
+ */
+#define WM9081_EQ_B3_PG_MASK                    0xFFFF  /* EQ_B3_PG - [15:0] */
+#define WM9081_EQ_B3_PG_SHIFT                        0  /* EQ_B3_PG - [15:0] */
+#define WM9081_EQ_B3_PG_WIDTH                       16  /* EQ_B3_PG - [15:0] */
+
+/*
+ * R59 (0x3B) - EQ 18
+ */
+#define WM9081_EQ_B5_A_MASK                     0xFFFF  /* EQ_B5_A - [15:0] */
+#define WM9081_EQ_B5_A_SHIFT                         0  /* EQ_B5_A - [15:0] */
+#define WM9081_EQ_B5_A_WIDTH                        16  /* EQ_B5_A - [15:0] */
+
+/*
+ * R60 (0x3C) - EQ 19
+ */
+#define WM9081_EQ_B5_B_MASK                     0xFFFF  /* EQ_B5_B - [15:0] */
+#define WM9081_EQ_B5_B_SHIFT                         0  /* EQ_B5_B - [15:0] */
+#define WM9081_EQ_B5_B_WIDTH                        16  /* EQ_B5_B - [15:0] */
+
+/*
+ * R61 (0x3D) - EQ 20
+ */
+#define WM9081_EQ_B5_PG_MASK                    0xFFFF  /* EQ_B5_PG - [15:0] */
+#define WM9081_EQ_B5_PG_SHIFT                        0  /* EQ_B5_PG - [15:0] */
+#define WM9081_EQ_B5_PG_WIDTH                       16  /* EQ_B5_PG - [15:0] */
+
+
+#endif
index c2d1a7a18fa3893757eef782372fa976314702e6..fa88b463e71f37f4a07bc830db69c50d2e52b39c 100644 (file)
@@ -282,14 +282,14 @@ struct snd_soc_dai wm9705_dai[] = {
                        .channels_min = 1,
                        .channels_max = 2,
                        .rates = WM9705_AC97_RATES,
-                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+                       .formats = SND_SOC_STD_AC97_FMTS,
                },
                .capture = {
                        .stream_name = "HiFi Capture",
                        .channels_min = 1,
                        .channels_max = 2,
                        .rates = WM9705_AC97_RATES,
-                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+                       .formats = SND_SOC_STD_AC97_FMTS,
                },
                .ops = &wm9705_dai_ops,
        },
index 765cf1e7369eb37c3a8f85a5e8257a22c2957f8e..1fd4e88f50cff74db693676172518601671532ab 100644 (file)
@@ -534,13 +534,13 @@ struct snd_soc_dai wm9712_dai[] = {
                .channels_min = 1,
                .channels_max = 2,
                .rates = WM9712_AC97_RATES,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+               .formats = SND_SOC_STD_AC97_FMTS,},
        .capture = {
                .stream_name = "HiFi Capture",
                .channels_min = 1,
                .channels_max = 2,
                .rates = WM9712_AC97_RATES,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+               .formats = SND_SOC_STD_AC97_FMTS,},
        .ops = &wm9712_dai_ops_hifi,
 },
 {
@@ -550,7 +550,7 @@ struct snd_soc_dai wm9712_dai[] = {
                .channels_min = 1,
                .channels_max = 1,
                .rates = WM9712_AC97_RATES,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+               .formats = SND_SOC_STD_AC97_FMTS,},
        .ops = &wm9712_dai_ops_aux,
 }
 };
@@ -585,6 +585,8 @@ static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
        }
 
        soc_ac97_ops.reset(codec->ac97);
+       if (soc_ac97_ops.warm_reset)
+               soc_ac97_ops.warm_reset(codec->ac97);
        if (ac97_read(codec, 0) != wm9712_reg[0])
                goto err;
        return 0;
index 523bad077fa04cc3f84a74feb7289bbb01c2c6a5..abed37acf78714be6b7f5dd534df94dc6c2be319 100644 (file)
@@ -189,6 +189,26 @@ SOC_SINGLE("3D Lower Cut-off Switch", AC97_REC_GAIN_MIC, 4, 1, 0),
 SOC_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1),
 };
 
+static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w,
+                                struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       u16 status, rate;
+
+       BUG_ON(event != SND_SOC_DAPM_PRE_PMD);
+
+       /* Gracefully shut down the voice interface. */
+       status = ac97_read(codec, AC97_EXTENDED_MID) | 0x1000;
+       rate = ac97_read(codec, AC97_HANDSET_RATE) & 0xF0FF;
+       ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0200);
+       schedule_timeout_interruptible(msecs_to_jiffies(1));
+       ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0F00);
+       ac97_write(codec, AC97_EXTENDED_MID, status);
+
+       return 0;
+}
+
+
 /* We have to create a fake left and right HP mixers because
  * the codec only has a single control that is shared by both channels.
  * This makes it impossible to determine the audio path using the current
@@ -400,7 +420,8 @@ SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
 SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
 SND_SOC_DAPM_MIXER("Line Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
 SND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
-SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1),
+SND_SOC_DAPM_DAC_E("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1,
+                  wm9713_voice_shutdown, SND_SOC_DAPM_PRE_PMD),
 SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1),
 SND_SOC_DAPM_PGA("Left ADC", AC97_EXTENDED_MID, 5, 1, NULL, 0),
 SND_SOC_DAPM_PGA("Right ADC", AC97_EXTENDED_MID, 4, 1, NULL, 0),
@@ -689,7 +710,7 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int source)
        Ndiv = target / source;
        if ((Ndiv < 5) || (Ndiv > 12))
                printk(KERN_WARNING
-                       "WM9713 PLL N value %d out of recommended range!\n",
+                       "WM9713 PLL N value %u out of recommended range!\n",
                        Ndiv);
 
        pll_div->n = Ndiv;
@@ -936,21 +957,6 @@ static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static void wm9713_voiceshutdown(struct snd_pcm_substream *substream,
-                                struct snd_soc_dai *dai)
-{
-       struct snd_soc_codec *codec = dai->codec;
-       u16 status, rate;
-
-       /* Gracefully shut down the voice interface. */
-       status = ac97_read(codec, AC97_EXTENDED_STATUS) | 0x1000;
-       rate = ac97_read(codec, AC97_HANDSET_RATE) & 0xF0FF;
-       ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0200);
-       schedule_timeout_interruptible(msecs_to_jiffies(1));
-       ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0F00);
-       ac97_write(codec, AC97_EXTENDED_MID, status);
-}
-
 static int ac97_hifi_prepare(struct snd_pcm_substream *substream,
                             struct snd_soc_dai *dai)
 {
@@ -1019,7 +1025,6 @@ static struct snd_soc_dai_ops wm9713_dai_ops_aux = {
 
 static struct snd_soc_dai_ops wm9713_dai_ops_voice = {
        .hw_params      = wm9713_pcm_hw_params,
-       .shutdown       = wm9713_voiceshutdown,
        .set_clkdiv     = wm9713_set_dai_clkdiv,
        .set_pll        = wm9713_set_dai_pll,
        .set_fmt        = wm9713_set_dai_fmt,
@@ -1035,13 +1040,13 @@ struct snd_soc_dai wm9713_dai[] = {
                .channels_min = 1,
                .channels_max = 2,
                .rates = WM9713_RATES,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+               .formats = SND_SOC_STD_AC97_FMTS,},
        .capture = {
                .stream_name = "HiFi Capture",
                .channels_min = 1,
                .channels_max = 2,
                .rates = WM9713_RATES,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+               .formats = SND_SOC_STD_AC97_FMTS,},
        .ops = &wm9713_dai_ops_hifi,
        },
        {
@@ -1051,7 +1056,7 @@ struct snd_soc_dai wm9713_dai[] = {
                .channels_min = 1,
                .channels_max = 1,
                .rates = WM9713_RATES,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+               .formats = SND_SOC_STD_AC97_FMTS,},
        .ops = &wm9713_dai_ops_aux,
        },
        {
@@ -1069,6 +1074,7 @@ struct snd_soc_dai wm9713_dai[] = {
                .rates = WM9713_PCM_RATES,
                .formats = WM9713_PCM_FORMATS,},
        .ops = &wm9713_dai_ops_voice,
+       .symmetric_rates = 1,
        },
 };
 EXPORT_SYMBOL_GPL(wm9713_dai);
index 9fc9082833716dc4b16df21bf6e5e30267913f8f..5dbebf82249ca735776d942ed9e3335e7b300b09 100644 (file)
@@ -1,5 +1,8 @@
 config SND_SOC_OF_SIMPLE
        tristate
+       
+config SND_MPC52xx_DMA
+       tristate
 
 # ASoC platform support for the Freescale MPC8610 SOC.  This compiles drivers
 # for the SSI and the Elo DMA controller.  You will still need to select
@@ -22,7 +25,34 @@ config SND_SOC_MPC8610_HPCD
 config SND_SOC_MPC5200_I2S
        tristate "Freescale MPC5200 PSC in I2S mode driver"
        depends on PPC_MPC52xx && PPC_BESTCOMM
-       select SND_SOC_OF_SIMPLE
+       select SND_MPC52xx_DMA
        select PPC_BESTCOMM_GEN_BD
        help
          Say Y here to support the MPC5200 PSCs in I2S mode.
+
+config SND_SOC_MPC5200_AC97
+       tristate "Freescale MPC5200 PSC in AC97 mode driver"
+       depends on PPC_MPC52xx && PPC_BESTCOMM
+       select AC97_BUS
+       select SND_MPC52xx_DMA
+       select PPC_BESTCOMM_GEN_BD
+       help
+         Say Y here to support the MPC5200 PSCs in AC97 mode.
+
+config SND_MPC52xx_SOC_PCM030
+       tristate "SoC AC97 Audio support for Phytec pcm030 and WM9712"
+       depends on PPC_MPC5200_SIMPLE && BROKEN
+       select SND_SOC_MPC5200_AC97
+       select SND_SOC_WM9712
+       help
+         Say Y if you want to add support for sound on the Phytec pcm030
+         baseboard.
+
+config SND_MPC52xx_SOC_EFIKA
+       tristate "SoC AC97 Audio support for bbplan Efika and STAC9766"
+       depends on PPC_EFIKA && BROKEN
+       select SND_SOC_MPC5200_AC97
+       select SND_SOC_STAC9766
+       help
+         Say Y if you want to add support for sound on the Efika.
+
index f85134c863871f6a73a5dca1e96f371893a8e602..a83a73967ec666a14409bb8f64c3b540c8b0f401 100644 (file)
@@ -10,5 +10,12 @@ snd-soc-fsl-ssi-objs := fsl_ssi.o
 snd-soc-fsl-dma-objs := fsl_dma.o
 obj-$(CONFIG_SND_SOC_MPC8610) += snd-soc-fsl-ssi.o snd-soc-fsl-dma.o
 
+# MPC5200 Platform Support
+obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o
 obj-$(CONFIG_SND_SOC_MPC5200_I2S) += mpc5200_psc_i2s.o
+obj-$(CONFIG_SND_SOC_MPC5200_AC97) += mpc5200_psc_ac97.o
+
+# MPC5200 Machine Support
+obj-$(CONFIG_SND_MPC52xx_SOC_PCM030) += pcm030-audio-fabric.o
+obj-$(CONFIG_SND_MPC52xx_SOC_EFIKA) += efika-audio-fabric.o
 
diff --git a/sound/soc/fsl/efika-audio-fabric.c b/sound/soc/fsl/efika-audio-fabric.c
new file mode 100644 (file)
index 0000000..85b0e75
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Efika driver for the PSC of the Freescale MPC52xx
+ * configured as AC97 interface
+ *
+ * Copyright 2008 Jon Smirl, Digispeaker
+ * Author: Jon Smirl <jonsmirl@gmail.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/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/soc-of-simple.h>
+
+#include "mpc5200_dma.h"
+#include "mpc5200_psc_ac97.h"
+#include "../codecs/stac9766.h"
+
+static struct snd_soc_device device;
+static struct snd_soc_card card;
+
+static struct snd_soc_dai_link efika_fabric_dai[] = {
+{
+       .name = "AC97",
+       .stream_name = "AC97 Analog",
+       .codec_dai = &stac9766_dai[STAC9766_DAI_AC97_ANALOG],
+       .cpu_dai = &psc_ac97_dai[MPC5200_AC97_NORMAL],
+},
+{
+       .name = "AC97",
+       .stream_name = "AC97 IEC958",
+       .codec_dai = &stac9766_dai[STAC9766_DAI_AC97_DIGITAL],
+       .cpu_dai = &psc_ac97_dai[MPC5200_AC97_SPDIF],
+},
+};
+
+static __init int efika_fabric_init(void)
+{
+       struct platform_device *pdev;
+       int rc;
+
+       if (!machine_is_compatible("bplan,efika"))
+               return -ENODEV;
+
+       card.platform = &mpc5200_audio_dma_platform;
+       card.name = "Efika";
+       card.dai_link = efika_fabric_dai;
+       card.num_links = ARRAY_SIZE(efika_fabric_dai);
+
+       device.card = &card;
+       device.codec_dev = &soc_codec_dev_stac9766;
+
+       pdev = platform_device_alloc("soc-audio", 1);
+       if (!pdev) {
+               pr_err("efika_fabric_init: platform_device_alloc() failed\n");
+               return -ENODEV;
+       }
+
+       platform_set_drvdata(pdev, &device);
+       device.dev = &pdev->dev;
+
+       rc = platform_device_add(pdev);
+       if (rc) {
+               pr_err("efika_fabric_init: platform_device_add() failed\n");
+               return -ENODEV;
+       }
+       return 0;
+}
+
+module_init(efika_fabric_init);
+
+
+MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
+MODULE_DESCRIPTION(DRV_NAME ": mpc5200 Efika fabric driver");
+MODULE_LICENSE("GPL");
+
index 3711d8454d96b893117c77941d8bd6794e0890db..93f0f38a32c99fb26299c288d3ce0ca442f55914 100644 (file)
@@ -375,18 +375,14 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
                struct snd_pcm_runtime *first_runtime =
                        ssi_private->first_stream->runtime;
 
-               if (!first_runtime->rate || !first_runtime->sample_bits) {
+               if (!first_runtime->sample_bits) {
                        dev_err(substream->pcm->card->dev,
-                               "set sample rate and size in %s stream first\n",
+                               "set sample size in %s stream first\n",
                                substream->stream == SNDRV_PCM_STREAM_PLAYBACK
                                ? "capture" : "playback");
                        return -EAGAIN;
                }
 
-               snd_pcm_hw_constraint_minmax(substream->runtime,
-                       SNDRV_PCM_HW_PARAM_RATE,
-                       first_runtime->rate, first_runtime->rate);
-
                /* If we're in synchronous mode, then we need to constrain
                 * the sample size as well.  We don't support independent sample
                 * rates in asynchronous mode.
@@ -674,7 +670,7 @@ struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
        ssi_private->dev = ssi_info->dev;
        ssi_private->asynchronous = ssi_info->asynchronous;
 
-       ssi_private->dev->driver_data = fsl_ssi_dai;
+       dev_set_drvdata(ssi_private->dev, fsl_ssi_dai);
 
        /* Initialize the the device_attribute structure */
        dev_attr->attr.name = "ssi-stats";
@@ -693,6 +689,7 @@ struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
        fsl_ssi_dai->name = ssi_private->name;
        fsl_ssi_dai->id = ssi_info->id;
        fsl_ssi_dai->dev = ssi_info->dev;
+       fsl_ssi_dai->symmetric_rates = 1;
 
        ret = snd_soc_register_dai(fsl_ssi_dai);
        if (ret != 0) {
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c
new file mode 100644 (file)
index 0000000..efec33a
--- /dev/null
@@ -0,0 +1,564 @@
+/*
+ * Freescale MPC5200 PSC DMA
+ * ALSA SoC Platform driver
+ *
+ * Copyright (C) 2008 Secret Lab Technologies Ltd.
+ * Copyright (C) 2009 Jon Smirl, Digispeaker
+ */
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+#include <sound/soc.h>
+
+#include <sysdev/bestcomm/bestcomm.h>
+#include <sysdev/bestcomm/gen_bd.h>
+#include <asm/mpc52xx_psc.h>
+
+#include "mpc5200_dma.h"
+
+/*
+ * Interrupt handlers
+ */
+static irqreturn_t psc_dma_status_irq(int irq, void *_psc_dma)
+{
+       struct psc_dma *psc_dma = _psc_dma;
+       struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs;
+       u16 isr;
+
+       isr = in_be16(&regs->mpc52xx_psc_isr);
+
+       /* Playback underrun error */
+       if (psc_dma->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP))
+               psc_dma->stats.underrun_count++;
+
+       /* Capture overrun error */
+       if (psc_dma->capture.active && (isr & MPC52xx_PSC_IMR_ORERR))
+               psc_dma->stats.overrun_count++;
+
+       out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * psc_dma_bcom_enqueue_next_buffer - Enqueue another audio buffer
+ * @s: pointer to stream private data structure
+ *
+ * Enqueues another audio period buffer into the bestcomm queue.
+ *
+ * Note: The routine must only be called when there is space available in
+ * the queue.  Otherwise the enqueue will fail and the audio ring buffer
+ * will get out of sync
+ */
+static void psc_dma_bcom_enqueue_next_buffer(struct psc_dma_stream *s)
+{
+       struct bcom_bd *bd;
+
+       /* Prepare and enqueue the next buffer descriptor */
+       bd = bcom_prepare_next_buffer(s->bcom_task);
+       bd->status = s->period_bytes;
+       bd->data[0] = s->period_next_pt;
+       bcom_submit_next_buffer(s->bcom_task, NULL);
+
+       /* Update for next period */
+       s->period_next_pt += s->period_bytes;
+       if (s->period_next_pt >= s->period_end)
+               s->period_next_pt = s->period_start;
+}
+
+static void psc_dma_bcom_enqueue_tx(struct psc_dma_stream *s)
+{
+       while (s->appl_ptr < s->runtime->control->appl_ptr) {
+
+               if (bcom_queue_full(s->bcom_task))
+                       return;
+
+               s->appl_ptr += s->period_size;
+
+               psc_dma_bcom_enqueue_next_buffer(s);
+       }
+}
+
+/* Bestcomm DMA irq handler */
+static irqreturn_t psc_dma_bcom_irq_tx(int irq, void *_psc_dma_stream)
+{
+       struct psc_dma_stream *s = _psc_dma_stream;
+
+       spin_lock(&s->psc_dma->lock);
+       /* For each finished period, dequeue the completed period buffer
+        * and enqueue a new one in it's place. */
+       while (bcom_buffer_done(s->bcom_task)) {
+               bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
+
+               s->period_current_pt += s->period_bytes;
+               if (s->period_current_pt >= s->period_end)
+                       s->period_current_pt = s->period_start;
+       }
+       psc_dma_bcom_enqueue_tx(s);
+       spin_unlock(&s->psc_dma->lock);
+
+       /* If the stream is active, then also inform the PCM middle layer
+        * of the period finished event. */
+       if (s->active)
+               snd_pcm_period_elapsed(s->stream);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t psc_dma_bcom_irq_rx(int irq, void *_psc_dma_stream)
+{
+       struct psc_dma_stream *s = _psc_dma_stream;
+
+       spin_lock(&s->psc_dma->lock);
+       /* For each finished period, dequeue the completed period buffer
+        * and enqueue a new one in it's place. */
+       while (bcom_buffer_done(s->bcom_task)) {
+               bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
+
+               s->period_current_pt += s->period_bytes;
+               if (s->period_current_pt >= s->period_end)
+                       s->period_current_pt = s->period_start;
+
+               psc_dma_bcom_enqueue_next_buffer(s);
+       }
+       spin_unlock(&s->psc_dma->lock);
+
+       /* If the stream is active, then also inform the PCM middle layer
+        * of the period finished event. */
+       if (s->active)
+               snd_pcm_period_elapsed(s->stream);
+
+       return IRQ_HANDLED;
+}
+
+static int psc_dma_hw_free(struct snd_pcm_substream *substream)
+{
+       snd_pcm_set_runtime_buffer(substream, NULL);
+       return 0;
+}
+
+/**
+ * psc_dma_trigger: start and stop the DMA transfer.
+ *
+ * This function is called by ALSA to start, stop, pause, and resume the DMA
+ * transfer of data.
+ */
+static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct psc_dma_stream *s;
+       struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs;
+       u16 imr;
+       unsigned long flags;
+       int i;
+
+       if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+               s = &psc_dma->capture;
+       else
+               s = &psc_dma->playback;
+
+       dev_dbg(psc_dma->dev, "psc_dma_trigger(substream=%p, cmd=%i)"
+               " stream_id=%i\n",
+               substream, cmd, substream->pstr->stream);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               s->period_bytes = frames_to_bytes(runtime,
+                                                 runtime->period_size);
+               s->period_start = virt_to_phys(runtime->dma_area);
+               s->period_end = s->period_start +
+                               (s->period_bytes * runtime->periods);
+               s->period_next_pt = s->period_start;
+               s->period_current_pt = s->period_start;
+               s->period_size = runtime->period_size;
+               s->active = 1;
+
+               /* track appl_ptr so that we have a better chance of detecting
+                * end of stream and not over running it.
+                */
+               s->runtime = runtime;
+               s->appl_ptr = s->runtime->control->appl_ptr -
+                               (runtime->period_size * runtime->periods);
+
+               /* Fill up the bestcomm bd queue and enable DMA.
+                * This will begin filling the PSC's fifo.
+                */
+               spin_lock_irqsave(&psc_dma->lock, flags);
+
+               if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
+                       bcom_gen_bd_rx_reset(s->bcom_task);
+                       for (i = 0; i < runtime->periods; i++)
+                               if (!bcom_queue_full(s->bcom_task))
+                                       psc_dma_bcom_enqueue_next_buffer(s);
+               } else {
+                       bcom_gen_bd_tx_reset(s->bcom_task);
+                       psc_dma_bcom_enqueue_tx(s);
+               }
+
+               bcom_enable(s->bcom_task);
+               spin_unlock_irqrestore(&psc_dma->lock, flags);
+
+               out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);
+
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+               s->active = 0;
+
+               spin_lock_irqsave(&psc_dma->lock, flags);
+               bcom_disable(s->bcom_task);
+               if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+                       bcom_gen_bd_rx_reset(s->bcom_task);
+               else
+                       bcom_gen_bd_tx_reset(s->bcom_task);
+               spin_unlock_irqrestore(&psc_dma->lock, flags);
+
+               break;
+
+       default:
+               dev_dbg(psc_dma->dev, "invalid command\n");
+               return -EINVAL;
+       }
+
+       /* Update interrupt enable settings */
+       imr = 0;
+       if (psc_dma->playback.active)
+               imr |= MPC52xx_PSC_IMR_TXEMP;
+       if (psc_dma->capture.active)
+               imr |= MPC52xx_PSC_IMR_ORERR;
+       out_be16(&regs->isr_imr.imr, psc_dma->imr | imr);
+
+       return 0;
+}
+
+
+/* ---------------------------------------------------------------------
+ * The PSC DMA 'ASoC platform' driver
+ *
+ * Can be referenced by an 'ASoC machine' driver
+ * This driver only deals with the audio bus; it doesn't have any
+ * interaction with the attached codec
+ */
+
+static const struct snd_pcm_hardware psc_dma_hardware = {
+       .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_BATCH,
+       .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |
+               SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE,
+       .rate_min = 8000,
+       .rate_max = 48000,
+       .channels_min = 1,
+       .channels_max = 2,
+       .period_bytes_max       = 1024 * 1024,
+       .period_bytes_min       = 32,
+       .periods_min            = 2,
+       .periods_max            = 256,
+       .buffer_bytes_max       = 2 * 1024 * 1024,
+       .fifo_size              = 512,
+};
+
+static int psc_dma_open(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
+       struct psc_dma_stream *s;
+       int rc;
+
+       dev_dbg(psc_dma->dev, "psc_dma_open(substream=%p)\n", substream);
+
+       if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+               s = &psc_dma->capture;
+       else
+               s = &psc_dma->playback;
+
+       snd_soc_set_runtime_hwparams(substream, &psc_dma_hardware);
+
+       rc = snd_pcm_hw_constraint_integer(runtime,
+               SNDRV_PCM_HW_PARAM_PERIODS);
+       if (rc < 0) {
+               dev_err(substream->pcm->card->dev, "invalid buffer size\n");
+               return rc;
+       }
+
+       s->stream = substream;
+       return 0;
+}
+
+static int psc_dma_close(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
+       struct psc_dma_stream *s;
+
+       dev_dbg(psc_dma->dev, "psc_dma_close(substream=%p)\n", substream);
+
+       if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+               s = &psc_dma->capture;
+       else
+               s = &psc_dma->playback;
+
+       if (!psc_dma->playback.active &&
+           !psc_dma->capture.active) {
+
+               /* Disable all interrupts and reset the PSC */
+               out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr);
+               out_8(&psc_dma->psc_regs->command, 4 << 4); /* reset error */
+       }
+       s->stream = NULL;
+       return 0;
+}
+
+static snd_pcm_uframes_t
+psc_dma_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
+       struct psc_dma_stream *s;
+       dma_addr_t count;
+
+       if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+               s = &psc_dma->capture;
+       else
+               s = &psc_dma->playback;
+
+       count = s->period_current_pt - s->period_start;
+
+       return bytes_to_frames(substream->runtime, count);
+}
+
+static int
+psc_dma_hw_params(struct snd_pcm_substream *substream,
+                        struct snd_pcm_hw_params *params)
+{
+       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+       return 0;
+}
+
+static struct snd_pcm_ops psc_dma_ops = {
+       .open           = psc_dma_open,
+       .close          = psc_dma_close,
+       .hw_free        = psc_dma_hw_free,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .pointer        = psc_dma_pointer,
+       .trigger        = psc_dma_trigger,
+       .hw_params      = psc_dma_hw_params,
+};
+
+static u64 psc_dma_dmamask = 0xffffffff;
+static int psc_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
+                          struct snd_pcm *pcm)
+{
+       struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+       struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
+       size_t size = psc_dma_hardware.buffer_bytes_max;
+       int rc = 0;
+
+       dev_dbg(rtd->socdev->dev, "psc_dma_new(card=%p, dai=%p, pcm=%p)\n",
+               card, dai, pcm);
+
+       if (!card->dev->dma_mask)
+               card->dev->dma_mask = &psc_dma_dmamask;
+       if (!card->dev->coherent_dma_mask)
+               card->dev->coherent_dma_mask = 0xffffffff;
+
+       if (pcm->streams[0].substream) {
+               rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev,
+                               size, &pcm->streams[0].substream->dma_buffer);
+               if (rc)
+                       goto playback_alloc_err;
+       }
+
+       if (pcm->streams[1].substream) {
+               rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev,
+                               size, &pcm->streams[1].substream->dma_buffer);
+               if (rc)
+                       goto capture_alloc_err;
+       }
+
+       if (rtd->socdev->card->codec->ac97)
+               rtd->socdev->card->codec->ac97->private_data = psc_dma;
+
+       return 0;
+
+ capture_alloc_err:
+       if (pcm->streams[0].substream)
+               snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer);
+
+ playback_alloc_err:
+       dev_err(card->dev, "Cannot allocate buffer(s)\n");
+
+       return -ENOMEM;
+}
+
+static void psc_dma_free(struct snd_pcm *pcm)
+{
+       struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+       struct snd_pcm_substream *substream;
+       int stream;
+
+       dev_dbg(rtd->socdev->dev, "psc_dma_free(pcm=%p)\n", pcm);
+
+       for (stream = 0; stream < 2; stream++) {
+               substream = pcm->streams[stream].substream;
+               if (substream) {
+                       snd_dma_free_pages(&substream->dma_buffer);
+                       substream->dma_buffer.area = NULL;
+                       substream->dma_buffer.addr = 0;
+               }
+       }
+}
+
+struct snd_soc_platform mpc5200_audio_dma_platform = {
+       .name           = "mpc5200-psc-audio",
+       .pcm_ops        = &psc_dma_ops,
+       .pcm_new        = &psc_dma_new,
+       .pcm_free       = &psc_dma_free,
+};
+EXPORT_SYMBOL_GPL(mpc5200_audio_dma_platform);
+
+int mpc5200_audio_dma_create(struct of_device *op)
+{
+       phys_addr_t fifo;
+       struct psc_dma *psc_dma;
+       struct resource res;
+       int size, irq, rc;
+       const __be32 *prop;
+       void __iomem *regs;
+
+       /* Fetch the registers and IRQ of the PSC */
+       irq = irq_of_parse_and_map(op->node, 0);
+       if (of_address_to_resource(op->node, 0, &res)) {
+               dev_err(&op->dev, "Missing reg property\n");
+               return -ENODEV;
+       }
+       regs = ioremap(res.start, 1 + res.end - res.start);
+       if (!regs) {
+               dev_err(&op->dev, "Could not map registers\n");
+               return -ENODEV;
+       }
+
+       /* Allocate and initialize the driver private data */
+       psc_dma = kzalloc(sizeof *psc_dma, GFP_KERNEL);
+       if (!psc_dma) {
+               iounmap(regs);
+               return -ENOMEM;
+       }
+
+       /* Get the PSC ID */
+       prop = of_get_property(op->node, "cell-index", &size);
+       if (!prop || size < sizeof *prop)
+               return -ENODEV;
+
+       spin_lock_init(&psc_dma->lock);
+       psc_dma->id = be32_to_cpu(*prop);
+       psc_dma->irq = irq;
+       psc_dma->psc_regs = regs;
+       psc_dma->fifo_regs = regs + sizeof *psc_dma->psc_regs;
+       psc_dma->dev = &op->dev;
+       psc_dma->playback.psc_dma = psc_dma;
+       psc_dma->capture.psc_dma = psc_dma;
+       snprintf(psc_dma->name, sizeof psc_dma->name, "PSC%u", psc_dma->id);
+
+       /* Find the address of the fifo data registers and setup the
+        * DMA tasks */
+       fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32);
+       psc_dma->capture.bcom_task =
+               bcom_psc_gen_bd_rx_init(psc_dma->id, 10, fifo, 512);
+       psc_dma->playback.bcom_task =
+               bcom_psc_gen_bd_tx_init(psc_dma->id, 10, fifo);
+       if (!psc_dma->capture.bcom_task ||
+           !psc_dma->playback.bcom_task) {
+               dev_err(&op->dev, "Could not allocate bestcomm tasks\n");
+               iounmap(regs);
+               kfree(psc_dma);
+               return -ENODEV;
+       }
+
+       /* Disable all interrupts and reset the PSC */
+       out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr);
+        /* reset receiver */
+       out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_RX);
+        /* reset transmitter */
+       out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_TX);
+        /* reset error */
+       out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_ERR_STAT);
+        /* reset mode */
+       out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_SEL_MODE_REG_1);
+
+       /* Set up mode register;
+        * First write: RxRdy (FIFO Alarm) generates rx FIFO irq
+        * Second write: register Normal mode for non loopback
+        */
+       out_8(&psc_dma->psc_regs->mode, 0);
+       out_8(&psc_dma->psc_regs->mode, 0);
+
+       /* Set the TX and RX fifo alarm thresholds */
+       out_be16(&psc_dma->fifo_regs->rfalarm, 0x100);
+       out_8(&psc_dma->fifo_regs->rfcntl, 0x4);
+       out_be16(&psc_dma->fifo_regs->tfalarm, 0x100);
+       out_8(&psc_dma->fifo_regs->tfcntl, 0x7);
+
+       /* Lookup the IRQ numbers */
+       psc_dma->playback.irq =
+               bcom_get_task_irq(psc_dma->playback.bcom_task);
+       psc_dma->capture.irq =
+               bcom_get_task_irq(psc_dma->capture.bcom_task);
+
+       rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED,
+                        "psc-dma-status", psc_dma);
+       rc |= request_irq(psc_dma->capture.irq,
+                         &psc_dma_bcom_irq_rx, IRQF_SHARED,
+                         "psc-dma-capture", &psc_dma->capture);
+       rc |= request_irq(psc_dma->playback.irq,
+                         &psc_dma_bcom_irq_tx, IRQF_SHARED,
+                         "psc-dma-playback", &psc_dma->playback);
+       if (rc) {
+               free_irq(psc_dma->irq, psc_dma);
+               free_irq(psc_dma->capture.irq,
+                        &psc_dma->capture);
+               free_irq(psc_dma->playback.irq,
+                        &psc_dma->playback);
+               return -ENODEV;
+       }
+
+       /* Save what we've done so it can be found again later */
+       dev_set_drvdata(&op->dev, psc_dma);
+
+       /* Tell the ASoC OF helpers about it */
+       return snd_soc_register_platform(&mpc5200_audio_dma_platform);
+}
+EXPORT_SYMBOL_GPL(mpc5200_audio_dma_create);
+
+int mpc5200_audio_dma_destroy(struct of_device *op)
+{
+       struct psc_dma *psc_dma = dev_get_drvdata(&op->dev);
+
+       dev_dbg(&op->dev, "mpc5200_audio_dma_destroy()\n");
+
+       snd_soc_unregister_platform(&mpc5200_audio_dma_platform);
+
+       bcom_gen_bd_rx_release(psc_dma->capture.bcom_task);
+       bcom_gen_bd_tx_release(psc_dma->playback.bcom_task);
+
+       /* Release irqs */
+       free_irq(psc_dma->irq, psc_dma);
+       free_irq(psc_dma->capture.irq, &psc_dma->capture);
+       free_irq(psc_dma->playback.irq, &psc_dma->playback);
+
+       iounmap(psc_dma->psc_regs);
+       kfree(psc_dma);
+       dev_set_drvdata(&op->dev, NULL);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mpc5200_audio_dma_destroy);
+
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_DESCRIPTION("Freescale MPC5200 PSC in DMA mode ASoC Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h
new file mode 100644 (file)
index 0000000..2000803
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Freescale MPC5200 Audio DMA driver
+ */
+
+#ifndef __SOUND_SOC_FSL_MPC5200_DMA_H__
+#define __SOUND_SOC_FSL_MPC5200_DMA_H__
+
+#define PSC_STREAM_NAME_LEN 32
+
+/**
+ * psc_ac97_stream - Data specific to a single stream (playback or capture)
+ * @active:            flag indicating if the stream is active
+ * @psc_dma:           pointer back to parent psc_dma data structure
+ * @bcom_task:         bestcomm task structure
+ * @irq:               irq number for bestcomm task
+ * @period_start:      physical address of start of DMA region
+ * @period_end:                physical address of end of DMA region
+ * @period_next_pt:    physical address of next DMA buffer to enqueue
+ * @period_bytes:      size of DMA period in bytes
+ */
+struct psc_dma_stream {
+       struct snd_pcm_runtime *runtime;
+       snd_pcm_uframes_t appl_ptr;
+
+       int active;
+       struct psc_dma *psc_dma;
+       struct bcom_task *bcom_task;
+       int irq;
+       struct snd_pcm_substream *stream;
+       dma_addr_t period_start;
+       dma_addr_t period_end;
+       dma_addr_t period_next_pt;
+       dma_addr_t period_current_pt;
+       int period_bytes;
+       int period_size;
+};
+
+/**
+ * psc_dma - Private driver data
+ * @name: short name for this device ("PSC0", "PSC1", etc)
+ * @psc_regs: pointer to the PSC's registers
+ * @fifo_regs: pointer to the PSC's FIFO registers
+ * @irq: IRQ of this PSC
+ * @dev: struct device pointer
+ * @dai: the CPU DAI for this device
+ * @sicr: Base value used in serial interface control register; mode is ORed
+ *        with this value.
+ * @playback: Playback stream context data
+ * @capture: Capture stream context data
+ */
+struct psc_dma {
+       char name[32];
+       struct mpc52xx_psc __iomem *psc_regs;
+       struct mpc52xx_psc_fifo __iomem *fifo_regs;
+       unsigned int irq;
+       struct device *dev;
+       spinlock_t lock;
+       u32 sicr;
+       uint sysclk;
+       int imr;
+       int id;
+       unsigned int slots;
+
+       /* per-stream data */
+       struct psc_dma_stream playback;
+       struct psc_dma_stream capture;
+
+       /* Statistics */
+       struct {
+               unsigned long overrun_count;
+               unsigned long underrun_count;
+       } stats;
+};
+
+int mpc5200_audio_dma_create(struct of_device *op);
+int mpc5200_audio_dma_destroy(struct of_device *op);
+
+extern struct snd_soc_platform mpc5200_audio_dma_platform;
+
+#endif /* __SOUND_SOC_FSL_MPC5200_DMA_H__ */
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c
new file mode 100644 (file)
index 0000000..794a247
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * linux/sound/mpc5200-ac97.c -- AC97 support for the Freescale MPC52xx chip.
+ *
+ * Copyright (C) 2009 Jon Smirl, Digispeaker
+ * Author: Jon Smirl <jonsmirl@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/time.h>
+#include <asm/delay.h>
+#include <asm/mpc52xx_psc.h>
+
+#include "mpc5200_dma.h"
+#include "mpc5200_psc_ac97.h"
+
+#define DRV_NAME "mpc5200-psc-ac97"
+
+/* ALSA only supports a single AC97 device so static is recommend here */
+static struct psc_dma *psc_dma;
+
+static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+       int status;
+       unsigned int val;
+
+       /* Wait for command send status zero = ready */
+       status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) &
+                               MPC52xx_PSC_SR_CMDSEND), 100, 0);
+       if (status == 0) {
+               pr_err("timeout on ac97 bus (rdy)\n");
+               return -ENODEV;
+       }
+       /* Send the read */
+       out_be32(&psc_dma->psc_regs->ac97_cmd, (1<<31) | ((reg & 0x7f) << 24));
+
+       /* Wait for the answer */
+       status = spin_event_timeout((in_be16(&psc_dma->psc_regs->sr_csr.status) &
+                               MPC52xx_PSC_SR_DATA_VAL), 100, 0);
+       if (status == 0) {
+               pr_err("timeout on ac97 read (val) %x\n",
+                               in_be16(&psc_dma->psc_regs->sr_csr.status));
+               return -ENODEV;
+       }
+       /* Get the data */
+       val = in_be32(&psc_dma->psc_regs->ac97_data);
+       if (((val >> 24) & 0x7f) != reg) {
+               pr_err("reg echo error on ac97 read\n");
+               return -ENODEV;
+       }
+       val = (val >> 8) & 0xffff;
+
+       return (unsigned short) val;
+}
+
+static void psc_ac97_write(struct snd_ac97 *ac97,
+                               unsigned short reg, unsigned short val)
+{
+       int status;
+
+       /* Wait for command status zero = ready */
+       status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) &
+                               MPC52xx_PSC_SR_CMDSEND), 100, 0);
+       if (status == 0) {
+               pr_err("timeout on ac97 bus (write)\n");
+               return;
+       }
+       /* Write data */
+       out_be32(&psc_dma->psc_regs->ac97_cmd,
+                       ((reg & 0x7f) << 24) | (val << 8));
+}
+
+static void psc_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+       struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs;
+
+       out_be32(&regs->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_AWR);
+       udelay(3);
+       out_be32(&regs->sicr, psc_dma->sicr);
+}
+
+static void psc_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+       struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs;
+
+       /* Do a cold reset */
+       out_8(&regs->op1, MPC52xx_PSC_OP_RES);
+       udelay(10);
+       out_8(&regs->op0, MPC52xx_PSC_OP_RES);
+       udelay(50);
+       psc_ac97_warm_reset(ac97);
+}
+
+struct snd_ac97_bus_ops soc_ac97_ops = {
+       .read           = psc_ac97_read,
+       .write          = psc_ac97_write,
+       .reset          = psc_ac97_cold_reset,
+       .warm_reset     = psc_ac97_warm_reset,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+static int psc_ac97_hw_analog_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *cpu_dai)
+{
+       struct psc_dma *psc_dma = cpu_dai->private_data;
+
+       dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i"
+               " periods=%i buffer_size=%i  buffer_bytes=%i channels=%i"
+               " rate=%i format=%i\n",
+               __func__, substream, params_period_size(params),
+               params_period_bytes(params), params_periods(params),
+               params_buffer_size(params), params_buffer_bytes(params),
+               params_channels(params), params_rate(params),
+               params_format(params));
+
+
+       if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               if (params_channels(params) == 1)
+                       psc_dma->slots |= 0x00000100;
+               else
+                       psc_dma->slots |= 0x00000300;
+       } else {
+               if (params_channels(params) == 1)
+                       psc_dma->slots |= 0x01000000;
+               else
+                       psc_dma->slots |= 0x03000000;
+       }
+       out_be32(&psc_dma->psc_regs->ac97_slots, psc_dma->slots);
+
+       return 0;
+}
+
+static int psc_ac97_hw_digital_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *cpu_dai)
+{
+       struct psc_dma *psc_dma = cpu_dai->private_data;
+
+       if (params_channels(params) == 1)
+               out_be32(&psc_dma->psc_regs->ac97_slots, 0x01000000);
+       else
+               out_be32(&psc_dma->psc_regs->ac97_slots, 0x03000000);
+
+       return 0;
+}
+
+static int psc_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
+                                                       struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_STOP:
+               if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+                       psc_dma->slots &= 0xFFFF0000;
+               else
+                       psc_dma->slots &= 0x0000FFFF;
+
+               out_be32(&psc_dma->psc_regs->ac97_slots, psc_dma->slots);
+               break;
+       }
+       return 0;
+}
+
+static int psc_ac97_probe(struct platform_device *pdev,
+                                       struct snd_soc_dai *cpu_dai)
+{
+       struct psc_dma *psc_dma = cpu_dai->private_data;
+       struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs;
+
+       /* Go */
+       out_8(&regs->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE);
+       return 0;
+}
+
+/* ---------------------------------------------------------------------
+ * ALSA SoC Bindings
+ *
+ * - Digital Audio Interface (DAI) template
+ * - create/destroy dai hooks
+ */
+
+/**
+ * psc_ac97_dai_template: template CPU Digital Audio Interface
+ */
+static struct snd_soc_dai_ops psc_ac97_analog_ops = {
+       .hw_params      = psc_ac97_hw_analog_params,
+       .trigger        = psc_ac97_trigger,
+};
+
+static struct snd_soc_dai_ops psc_ac97_digital_ops = {
+       .hw_params      = psc_ac97_hw_digital_params,
+};
+
+struct snd_soc_dai psc_ac97_dai[] = {
+{
+       .name   = "AC97",
+       .ac97_control = 1,
+       .probe  = psc_ac97_probe,
+       .playback = {
+               .channels_min   = 1,
+               .channels_max   = 6,
+               .rates          = SNDRV_PCM_RATE_8000_48000,
+               .formats = SNDRV_PCM_FMTBIT_S32_BE,
+       },
+       .capture = {
+               .channels_min   = 1,
+               .channels_max   = 2,
+               .rates          = SNDRV_PCM_RATE_8000_48000,
+               .formats = SNDRV_PCM_FMTBIT_S32_BE,
+       },
+       .ops = &psc_ac97_analog_ops,
+},
+{
+       .name   = "SPDIF",
+       .ac97_control = 1,
+       .playback = {
+               .channels_min   = 1,
+               .channels_max   = 2,
+               .rates          = SNDRV_PCM_RATE_32000 | \
+                       SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE,
+       },
+       .ops = &psc_ac97_digital_ops,
+} };
+EXPORT_SYMBOL_GPL(psc_ac97_dai);
+
+
+
+/* ---------------------------------------------------------------------
+ * OF platform bus binding code:
+ * - Probe/remove operations
+ * - OF device match table
+ */
+static int __devinit psc_ac97_of_probe(struct of_device *op,
+                                     const struct of_device_id *match)
+{
+       int rc, i;
+       struct snd_ac97 ac97;
+       struct mpc52xx_psc __iomem *regs;
+
+       rc = mpc5200_audio_dma_create(op);
+       if (rc != 0)
+               return rc;
+
+       for (i = 0; i < ARRAY_SIZE(psc_ac97_dai); i++)
+               psc_ac97_dai[i].dev = &op->dev;
+
+       rc = snd_soc_register_dais(psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai));
+       if (rc != 0) {
+               dev_err(&op->dev, "Failed to register DAI\n");
+               return rc;
+       }
+
+       psc_dma = dev_get_drvdata(&op->dev);
+       regs = psc_dma->psc_regs;
+       ac97.private_data = psc_dma;
+
+       for (i = 0; i < ARRAY_SIZE(psc_ac97_dai); i++)
+               psc_ac97_dai[i].private_data = psc_dma;
+
+       psc_dma->imr = 0;
+       out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr);
+
+       /* Configure the serial interface mode to AC97 */
+       psc_dma->sicr = MPC52xx_PSC_SICR_SIM_AC97 | MPC52xx_PSC_SICR_ENAC97;
+       out_be32(&regs->sicr, psc_dma->sicr);
+
+       /* No slots active */
+       out_be32(&regs->ac97_slots, 0x00000000);
+
+       return 0;
+}
+
+static int __devexit psc_ac97_of_remove(struct of_device *op)
+{
+       return mpc5200_audio_dma_destroy(op);
+}
+
+/* Match table for of_platform binding */
+static struct of_device_id psc_ac97_match[] __devinitdata = {
+       { .compatible = "fsl,mpc5200-psc-ac97", },
+       { .compatible = "fsl,mpc5200b-psc-ac97", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, psc_ac97_match);
+
+static struct of_platform_driver psc_ac97_driver = {
+       .match_table = psc_ac97_match,
+       .probe = psc_ac97_of_probe,
+       .remove = __devexit_p(psc_ac97_of_remove),
+       .driver = {
+               .name = "mpc5200-psc-ac97",
+               .owner = THIS_MODULE,
+       },
+};
+
+/* ---------------------------------------------------------------------
+ * Module setup and teardown; simply register the of_platform driver
+ * for the PSC in AC97 mode.
+ */
+static int __init psc_ac97_init(void)
+{
+       return of_register_platform_driver(&psc_ac97_driver);
+}
+module_init(psc_ac97_init);
+
+static void __exit psc_ac97_exit(void)
+{
+       of_unregister_platform_driver(&psc_ac97_driver);
+}
+module_exit(psc_ac97_exit);
+
+MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
+MODULE_DESCRIPTION("mpc5200 AC97 module");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.h b/sound/soc/fsl/mpc5200_psc_ac97.h
new file mode 100644 (file)
index 0000000..4bc18c3
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Freescale MPC5200 PSC in AC97 mode
+ * ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ */
+
+#ifndef __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__
+#define __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__
+
+extern struct snd_soc_dai psc_ac97_dai[];
+
+#define MPC5200_AC97_NORMAL 0
+#define MPC5200_AC97_SPDIF 1
+
+#endif /* __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__ */
index 1111c710118a2b8bd84fd6796d9c26a096437fa5..ce8de90fb94ae54d5ce7adbd05735f5395a4d7f7 100644 (file)
@@ -3,31 +3,21 @@
  * ALSA SoC Digital Audio Interface (DAI) driver
  *
  * Copyright (C) 2008 Secret Lab Technologies Ltd.
+ * Copyright (C) 2009 Jon Smirl, Digispeaker
  */
 
-#include <linux/init.h>
 #include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/delay.h>
 #include <linux/of_device.h>
 #include <linux/of_platform.h>
-#include <linux/dma-mapping.h>
 
-#include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
-#include <sound/initval.h>
 #include <sound/soc.h>
-#include <sound/soc-of-simple.h>
 
-#include <sysdev/bestcomm/bestcomm.h>
-#include <sysdev/bestcomm/gen_bd.h>
 #include <asm/mpc52xx_psc.h>
 
-MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
-MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver");
-MODULE_LICENSE("GPL");
+#include "mpc5200_psc_i2s.h"
+#include "mpc5200_dma.h"
 
 /**
  * PSC_I2S_RATES: sample rates supported by the I2S
@@ -44,191 +34,17 @@ MODULE_LICENSE("GPL");
  * PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode
  */
 #define PSC_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
-                        SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE | \
-                        SNDRV_PCM_FMTBIT_S32_BE)
-
-/**
- * psc_i2s_stream - Data specific to a single stream (playback or capture)
- * @active:            flag indicating if the stream is active
- * @psc_i2s:           pointer back to parent psc_i2s data structure
- * @bcom_task:         bestcomm task structure
- * @irq:               irq number for bestcomm task
- * @period_start:      physical address of start of DMA region
- * @period_end:                physical address of end of DMA region
- * @period_next_pt:    physical address of next DMA buffer to enqueue
- * @period_bytes:      size of DMA period in bytes
- */
-struct psc_i2s_stream {
-       int active;
-       struct psc_i2s *psc_i2s;
-       struct bcom_task *bcom_task;
-       int irq;
-       struct snd_pcm_substream *stream;
-       dma_addr_t period_start;
-       dma_addr_t period_end;
-       dma_addr_t period_next_pt;
-       dma_addr_t period_current_pt;
-       int period_bytes;
-};
-
-/**
- * psc_i2s - Private driver data
- * @name: short name for this device ("PSC0", "PSC1", etc)
- * @psc_regs: pointer to the PSC's registers
- * @fifo_regs: pointer to the PSC's FIFO registers
- * @irq: IRQ of this PSC
- * @dev: struct device pointer
- * @dai: the CPU DAI for this device
- * @sicr: Base value used in serial interface control register; mode is ORed
- *        with this value.
- * @playback: Playback stream context data
- * @capture: Capture stream context data
- */
-struct psc_i2s {
-       char name[32];
-       struct mpc52xx_psc __iomem *psc_regs;
-       struct mpc52xx_psc_fifo __iomem *fifo_regs;
-       unsigned int irq;
-       struct device *dev;
-       struct snd_soc_dai dai;
-       spinlock_t lock;
-       u32 sicr;
-
-       /* per-stream data */
-       struct psc_i2s_stream playback;
-       struct psc_i2s_stream capture;
-
-       /* Statistics */
-       struct {
-               int overrun_count;
-               int underrun_count;
-       } stats;
-};
-
-/*
- * Interrupt handlers
- */
-static irqreturn_t psc_i2s_status_irq(int irq, void *_psc_i2s)
-{
-       struct psc_i2s *psc_i2s = _psc_i2s;
-       struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs;
-       u16 isr;
-
-       isr = in_be16(&regs->mpc52xx_psc_isr);
-
-       /* Playback underrun error */
-       if (psc_i2s->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP))
-               psc_i2s->stats.underrun_count++;
-
-       /* Capture overrun error */
-       if (psc_i2s->capture.active && (isr & MPC52xx_PSC_IMR_ORERR))
-               psc_i2s->stats.overrun_count++;
-
-       out_8(&regs->command, 4 << 4);  /* reset the error status */
-
-       return IRQ_HANDLED;
-}
-
-/**
- * psc_i2s_bcom_enqueue_next_buffer - Enqueue another audio buffer
- * @s: pointer to stream private data structure
- *
- * Enqueues another audio period buffer into the bestcomm queue.
- *
- * Note: The routine must only be called when there is space available in
- * the queue.  Otherwise the enqueue will fail and the audio ring buffer
- * will get out of sync
- */
-static void psc_i2s_bcom_enqueue_next_buffer(struct psc_i2s_stream *s)
-{
-       struct bcom_bd *bd;
-
-       /* Prepare and enqueue the next buffer descriptor */
-       bd = bcom_prepare_next_buffer(s->bcom_task);
-       bd->status = s->period_bytes;
-       bd->data[0] = s->period_next_pt;
-       bcom_submit_next_buffer(s->bcom_task, NULL);
-
-       /* Update for next period */
-       s->period_next_pt += s->period_bytes;
-       if (s->period_next_pt >= s->period_end)
-               s->period_next_pt = s->period_start;
-}
-
-/* Bestcomm DMA irq handler */
-static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream)
-{
-       struct psc_i2s_stream *s = _psc_i2s_stream;
-
-       /* For each finished period, dequeue the completed period buffer
-        * and enqueue a new one in it's place. */
-       while (bcom_buffer_done(s->bcom_task)) {
-               bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
-               s->period_current_pt += s->period_bytes;
-               if (s->period_current_pt >= s->period_end)
-                       s->period_current_pt = s->period_start;
-               psc_i2s_bcom_enqueue_next_buffer(s);
-               bcom_enable(s->bcom_task);
-       }
-
-       /* If the stream is active, then also inform the PCM middle layer
-        * of the period finished event. */
-       if (s->active)
-               snd_pcm_period_elapsed(s->stream);
-
-       return IRQ_HANDLED;
-}
-
-/**
- * psc_i2s_startup: create a new substream
- *
- * This is the first function called when a stream is opened.
- *
- * If this is the first stream open, then grab the IRQ and program most of
- * the PSC registers.
- */
-static int psc_i2s_startup(struct snd_pcm_substream *substream,
-                          struct snd_soc_dai *dai)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
-       int rc;
-
-       dev_dbg(psc_i2s->dev, "psc_i2s_startup(substream=%p)\n", substream);
-
-       if (!psc_i2s->playback.active &&
-           !psc_i2s->capture.active) {
-               /* Setup the IRQs */
-               rc = request_irq(psc_i2s->irq, &psc_i2s_status_irq, IRQF_SHARED,
-                                "psc-i2s-status", psc_i2s);
-               rc |= request_irq(psc_i2s->capture.irq,
-                                 &psc_i2s_bcom_irq, IRQF_SHARED,
-                                 "psc-i2s-capture", &psc_i2s->capture);
-               rc |= request_irq(psc_i2s->playback.irq,
-                                 &psc_i2s_bcom_irq, IRQF_SHARED,
-                                 "psc-i2s-playback", &psc_i2s->playback);
-               if (rc) {
-                       free_irq(psc_i2s->irq, psc_i2s);
-                       free_irq(psc_i2s->capture.irq,
-                                &psc_i2s->capture);
-                       free_irq(psc_i2s->playback.irq,
-                                &psc_i2s->playback);
-                       return -ENODEV;
-               }
-       }
-
-       return 0;
-}
+                        SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE)
 
 static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
                                 struct snd_pcm_hw_params *params,
                                 struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+       struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
        u32 mode;
 
-       dev_dbg(psc_i2s->dev, "%s(substream=%p) p_size=%i p_bytes=%i"
+       dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i"
                " periods=%i buffer_size=%i  buffer_bytes=%i\n",
                __func__, substream, params_period_size(params),
                params_period_bytes(params), params_periods(params),
@@ -248,174 +64,14 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
                mode = MPC52xx_PSC_SICR_SIM_CODEC_32;
                break;
        default:
-               dev_dbg(psc_i2s->dev, "invalid format\n");
-               return -EINVAL;
-       }
-       out_be32(&psc_i2s->psc_regs->sicr, psc_i2s->sicr | mode);
-
-       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-
-       return 0;
-}
-
-static int psc_i2s_hw_free(struct snd_pcm_substream *substream,
-                          struct snd_soc_dai *dai)
-{
-       snd_pcm_set_runtime_buffer(substream, NULL);
-       return 0;
-}
-
-/**
- * psc_i2s_trigger: start and stop the DMA transfer.
- *
- * This function is called by ALSA to start, stop, pause, and resume the DMA
- * transfer of data.
- */
-static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
-                          struct snd_soc_dai *dai)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct psc_i2s_stream *s;
-       struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs;
-       u16 imr;
-       u8 psc_cmd;
-       unsigned long flags;
-
-       if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
-               s = &psc_i2s->capture;
-       else
-               s = &psc_i2s->playback;
-
-       dev_dbg(psc_i2s->dev, "psc_i2s_trigger(substream=%p, cmd=%i)"
-               " stream_id=%i\n",
-               substream, cmd, substream->pstr->stream);
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-               s->period_bytes = frames_to_bytes(runtime,
-                                                 runtime->period_size);
-               s->period_start = virt_to_phys(runtime->dma_area);
-               s->period_end = s->period_start +
-                               (s->period_bytes * runtime->periods);
-               s->period_next_pt = s->period_start;
-               s->period_current_pt = s->period_start;
-               s->active = 1;
-
-               /* First; reset everything */
-               if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
-                       out_8(&regs->command, MPC52xx_PSC_RST_RX);
-                       out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);
-               } else {
-                       out_8(&regs->command, MPC52xx_PSC_RST_TX);
-                       out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);
-               }
-
-               /* Next, fill up the bestcomm bd queue and enable DMA.
-                * This will begin filling the PSC's fifo. */
-               if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
-                       bcom_gen_bd_rx_reset(s->bcom_task);
-               else
-                       bcom_gen_bd_tx_reset(s->bcom_task);
-               while (!bcom_queue_full(s->bcom_task))
-                       psc_i2s_bcom_enqueue_next_buffer(s);
-               bcom_enable(s->bcom_task);
-
-               /* Due to errata in the i2s mode; need to line up enabling
-                * the transmitter with a transition on the frame sync
-                * line */
-
-               spin_lock_irqsave(&psc_i2s->lock, flags);
-               /* first make sure it is low */
-               while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) != 0)
-                       ;
-               /* then wait for the transition to high */
-               while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) == 0)
-                       ;
-               /* Finally, enable the PSC.
-                * Receiver must always be enabled; even when we only want
-                * transmit.  (see 15.3.2.3 of MPC5200B User's Guide) */
-               psc_cmd = MPC52xx_PSC_RX_ENABLE;
-               if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK)
-                       psc_cmd |= MPC52xx_PSC_TX_ENABLE;
-               out_8(&regs->command, psc_cmd);
-               spin_unlock_irqrestore(&psc_i2s->lock, flags);
-
-               break;
-
-       case SNDRV_PCM_TRIGGER_STOP:
-               /* Turn off the PSC */
-               s->active = 0;
-               if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
-                       if (!psc_i2s->playback.active) {
-                               out_8(&regs->command, 2 << 4);  /* reset rx */
-                               out_8(&regs->command, 3 << 4);  /* reset tx */
-                               out_8(&regs->command, 4 << 4);  /* reset err */
-                       }
-               } else {
-                       out_8(&regs->command, 3 << 4);  /* reset tx */
-                       out_8(&regs->command, 4 << 4);  /* reset err */
-                       if (!psc_i2s->capture.active)
-                               out_8(&regs->command, 2 << 4);  /* reset rx */
-               }
-
-               bcom_disable(s->bcom_task);
-               while (!bcom_queue_empty(s->bcom_task))
-                       bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
-
-               break;
-
-       default:
-               dev_dbg(psc_i2s->dev, "invalid command\n");
+               dev_dbg(psc_dma->dev, "invalid format\n");
                return -EINVAL;
        }
-
-       /* Update interrupt enable settings */
-       imr = 0;
-       if (psc_i2s->playback.active)
-               imr |= MPC52xx_PSC_IMR_TXEMP;
-       if (psc_i2s->capture.active)
-               imr |= MPC52xx_PSC_IMR_ORERR;
-       out_be16(&regs->isr_imr.imr, imr);
+       out_be32(&psc_dma->psc_regs->sicr, psc_dma->sicr | mode);
 
        return 0;
 }
 
-/**
- * psc_i2s_shutdown: shutdown the data transfer on a stream
- *
- * Shutdown the PSC if there are no other substreams open.
- */
-static void psc_i2s_shutdown(struct snd_pcm_substream *substream,
-                            struct snd_soc_dai *dai)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
-
-       dev_dbg(psc_i2s->dev, "psc_i2s_shutdown(substream=%p)\n", substream);
-
-       /*
-        * If this is the last active substream, disable the PSC and release
-        * the IRQ.
-        */
-       if (!psc_i2s->playback.active &&
-           !psc_i2s->capture.active) {
-
-               /* Disable all interrupts and reset the PSC */
-               out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0);
-               out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset tx */
-               out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset rx */
-               out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */
-               out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */
-
-               /* Release irqs */
-               free_irq(psc_i2s->irq, psc_i2s);
-               free_irq(psc_i2s->capture.irq, &psc_i2s->capture);
-               free_irq(psc_i2s->playback.irq, &psc_i2s->playback);
-       }
-}
-
 /**
  * psc_i2s_set_sysclk: set the clock frequency and direction
  *
@@ -433,8 +89,8 @@ static void psc_i2s_shutdown(struct snd_pcm_substream *substream,
 static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
                              int clk_id, unsigned int freq, int dir)
 {
-       struct psc_i2s *psc_i2s = cpu_dai->private_data;
-       dev_dbg(psc_i2s->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n",
+       struct psc_dma *psc_dma = cpu_dai->private_data;
+       dev_dbg(psc_dma->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n",
                                cpu_dai, dir);
        return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL;
 }
@@ -452,8 +108,8 @@ static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
  */
 static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format)
 {
-       struct psc_i2s *psc_i2s = cpu_dai->private_data;
-       dev_dbg(psc_i2s->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n",
+       struct psc_dma *psc_dma = cpu_dai->private_data;
+       dev_dbg(psc_dma->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n",
                                cpu_dai, format);
        return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL;
 }
@@ -469,16 +125,13 @@ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format)
  * psc_i2s_dai_template: template CPU Digital Audio Interface
  */
 static struct snd_soc_dai_ops psc_i2s_dai_ops = {
-       .startup        = psc_i2s_startup,
        .hw_params      = psc_i2s_hw_params,
-       .hw_free        = psc_i2s_hw_free,
-       .shutdown       = psc_i2s_shutdown,
-       .trigger        = psc_i2s_trigger,
        .set_sysclk     = psc_i2s_set_sysclk,
        .set_fmt        = psc_i2s_set_fmt,
 };
 
-static struct snd_soc_dai psc_i2s_dai_template = {
+struct snd_soc_dai psc_i2s_dai[] = {{
+       .name   = "I2S",
        .playback = {
                .channels_min = 2,
                .channels_max = 2,
@@ -492,223 +145,8 @@ static struct snd_soc_dai psc_i2s_dai_template = {
                .formats = PSC_I2S_FORMATS,
        },
        .ops = &psc_i2s_dai_ops,
-};
-
-/* ---------------------------------------------------------------------
- * The PSC I2S 'ASoC platform' driver
- *
- * Can be referenced by an 'ASoC machine' driver
- * This driver only deals with the audio bus; it doesn't have any
- * interaction with the attached codec
- */
-
-static const struct snd_pcm_hardware psc_i2s_pcm_hardware = {
-       .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
-               SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
-               SNDRV_PCM_INFO_BATCH,
-       .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |
-                  SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE,
-       .rate_min = 8000,
-       .rate_max = 48000,
-       .channels_min = 2,
-       .channels_max = 2,
-       .period_bytes_max       = 1024 * 1024,
-       .period_bytes_min       = 32,
-       .periods_min            = 2,
-       .periods_max            = 256,
-       .buffer_bytes_max       = 2 * 1024 * 1024,
-       .fifo_size              = 0,
-};
-
-static int psc_i2s_pcm_open(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
-       struct psc_i2s_stream *s;
-
-       dev_dbg(psc_i2s->dev, "psc_i2s_pcm_open(substream=%p)\n", substream);
-
-       if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
-               s = &psc_i2s->capture;
-       else
-               s = &psc_i2s->playback;
-
-       snd_soc_set_runtime_hwparams(substream, &psc_i2s_pcm_hardware);
-
-       s->stream = substream;
-       return 0;
-}
-
-static int psc_i2s_pcm_close(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
-       struct psc_i2s_stream *s;
-
-       dev_dbg(psc_i2s->dev, "psc_i2s_pcm_close(substream=%p)\n", substream);
-
-       if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
-               s = &psc_i2s->capture;
-       else
-               s = &psc_i2s->playback;
-
-       s->stream = NULL;
-       return 0;
-}
-
-static snd_pcm_uframes_t
-psc_i2s_pcm_pointer(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
-       struct psc_i2s_stream *s;
-       dma_addr_t count;
-
-       if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
-               s = &psc_i2s->capture;
-       else
-               s = &psc_i2s->playback;
-
-       count = s->period_current_pt - s->period_start;
-
-       return bytes_to_frames(substream->runtime, count);
-}
-
-static struct snd_pcm_ops psc_i2s_pcm_ops = {
-       .open           = psc_i2s_pcm_open,
-       .close          = psc_i2s_pcm_close,
-       .ioctl          = snd_pcm_lib_ioctl,
-       .pointer        = psc_i2s_pcm_pointer,
-};
-
-static u64 psc_i2s_pcm_dmamask = 0xffffffff;
-static int psc_i2s_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
-                          struct snd_pcm *pcm)
-{
-       struct snd_soc_pcm_runtime *rtd = pcm->private_data;
-       size_t size = psc_i2s_pcm_hardware.buffer_bytes_max;
-       int rc = 0;
-
-       dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_new(card=%p, dai=%p, pcm=%p)\n",
-               card, dai, pcm);
-
-       if (!card->dev->dma_mask)
-               card->dev->dma_mask = &psc_i2s_pcm_dmamask;
-       if (!card->dev->coherent_dma_mask)
-               card->dev->coherent_dma_mask = 0xffffffff;
-
-       if (pcm->streams[0].substream) {
-               rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size,
-                                       &pcm->streams[0].substream->dma_buffer);
-               if (rc)
-                       goto playback_alloc_err;
-       }
-
-       if (pcm->streams[1].substream) {
-               rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size,
-                                       &pcm->streams[1].substream->dma_buffer);
-               if (rc)
-                       goto capture_alloc_err;
-       }
-
-       return 0;
-
- capture_alloc_err:
-       if (pcm->streams[0].substream)
-               snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer);
- playback_alloc_err:
-       dev_err(card->dev, "Cannot allocate buffer(s)\n");
-       return -ENOMEM;
-}
-
-static void psc_i2s_pcm_free(struct snd_pcm *pcm)
-{
-       struct snd_soc_pcm_runtime *rtd = pcm->private_data;
-       struct snd_pcm_substream *substream;
-       int stream;
-
-       dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_free(pcm=%p)\n", pcm);
-
-       for (stream = 0; stream < 2; stream++) {
-               substream = pcm->streams[stream].substream;
-               if (substream) {
-                       snd_dma_free_pages(&substream->dma_buffer);
-                       substream->dma_buffer.area = NULL;
-                       substream->dma_buffer.addr = 0;
-               }
-       }
-}
-
-struct snd_soc_platform psc_i2s_pcm_soc_platform = {
-       .name           = "mpc5200-psc-audio",
-       .pcm_ops        = &psc_i2s_pcm_ops,
-       .pcm_new        = &psc_i2s_pcm_new,
-       .pcm_free       = &psc_i2s_pcm_free,
-};
-
-/* ---------------------------------------------------------------------
- * Sysfs attributes for debugging
- */
-
-static ssize_t psc_i2s_status_show(struct device *dev,
-                          struct device_attribute *attr, char *buf)
-{
-       struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
-
-       return sprintf(buf, "status=%.4x sicr=%.8x rfnum=%i rfstat=0x%.4x "
-                       "tfnum=%i tfstat=0x%.4x\n",
-                       in_be16(&psc_i2s->psc_regs->sr_csr.status),
-                       in_be32(&psc_i2s->psc_regs->sicr),
-                       in_be16(&psc_i2s->fifo_regs->rfnum) & 0x1ff,
-                       in_be16(&psc_i2s->fifo_regs->rfstat),
-                       in_be16(&psc_i2s->fifo_regs->tfnum) & 0x1ff,
-                       in_be16(&psc_i2s->fifo_regs->tfstat));
-}
-
-static int *psc_i2s_get_stat_attr(struct psc_i2s *psc_i2s, const char *name)
-{
-       if (strcmp(name, "playback_underrun") == 0)
-               return &psc_i2s->stats.underrun_count;
-       if (strcmp(name, "capture_overrun") == 0)
-               return &psc_i2s->stats.overrun_count;
-
-       return NULL;
-}
-
-static ssize_t psc_i2s_stat_show(struct device *dev,
-                                struct device_attribute *attr, char *buf)
-{
-       struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
-       int *attrib;
-
-       attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name);
-       if (!attrib)
-               return 0;
-
-       return sprintf(buf, "%i\n", *attrib);
-}
-
-static ssize_t psc_i2s_stat_store(struct device *dev,
-                                 struct device_attribute *attr,
-                                 const char *buf,
-                                 size_t count)
-{
-       struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
-       int *attrib;
-
-       attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name);
-       if (!attrib)
-               return 0;
-
-       *attrib = simple_strtoul(buf, NULL, 0);
-       return count;
-}
-
-static DEVICE_ATTR(status, 0644, psc_i2s_status_show, NULL);
-static DEVICE_ATTR(playback_underrun, 0644, psc_i2s_stat_show,
-                       psc_i2s_stat_store);
-static DEVICE_ATTR(capture_overrun, 0644, psc_i2s_stat_show,
-                       psc_i2s_stat_store);
+} };
+EXPORT_SYMBOL_GPL(psc_i2s_dai);
 
 /* ---------------------------------------------------------------------
  * OF platform bus binding code:
@@ -718,150 +156,65 @@ static DEVICE_ATTR(capture_overrun, 0644, psc_i2s_stat_show,
 static int __devinit psc_i2s_of_probe(struct of_device *op,
                                      const struct of_device_id *match)
 {
-       phys_addr_t fifo;
-       struct psc_i2s *psc_i2s;
-       struct resource res;
-       int size, psc_id, irq, rc;
-       const __be32 *prop;
-       void __iomem *regs;
-
-       dev_dbg(&op->dev, "probing psc i2s device\n");
-
-       /* Get the PSC ID */
-       prop = of_get_property(op->node, "cell-index", &size);
-       if (!prop || size < sizeof *prop)
-               return -ENODEV;
-       psc_id = be32_to_cpu(*prop);
-
-       /* Fetch the registers and IRQ of the PSC */
-       irq = irq_of_parse_and_map(op->node, 0);
-       if (of_address_to_resource(op->node, 0, &res)) {
-               dev_err(&op->dev, "Missing reg property\n");
-               return -ENODEV;
-       }
-       regs = ioremap(res.start, 1 + res.end - res.start);
-       if (!regs) {
-               dev_err(&op->dev, "Could not map registers\n");
-               return -ENODEV;
-       }
+       int rc;
+       struct psc_dma *psc_dma;
+       struct mpc52xx_psc __iomem *regs;
 
-       /* Allocate and initialize the driver private data */
-       psc_i2s = kzalloc(sizeof *psc_i2s, GFP_KERNEL);
-       if (!psc_i2s) {
-               iounmap(regs);
-               return -ENOMEM;
-       }
-       spin_lock_init(&psc_i2s->lock);
-       psc_i2s->irq = irq;
-       psc_i2s->psc_regs = regs;
-       psc_i2s->fifo_regs = regs + sizeof *psc_i2s->psc_regs;
-       psc_i2s->dev = &op->dev;
-       psc_i2s->playback.psc_i2s = psc_i2s;
-       psc_i2s->capture.psc_i2s = psc_i2s;
-       snprintf(psc_i2s->name, sizeof psc_i2s->name, "PSC%u", psc_id+1);
-
-       /* Fill out the CPU DAI structure */
-       memcpy(&psc_i2s->dai, &psc_i2s_dai_template, sizeof psc_i2s->dai);
-       psc_i2s->dai.private_data = psc_i2s;
-       psc_i2s->dai.name = psc_i2s->name;
-       psc_i2s->dai.id = psc_id;
-
-       /* Find the address of the fifo data registers and setup the
-        * DMA tasks */
-       fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32);
-       psc_i2s->capture.bcom_task =
-               bcom_psc_gen_bd_rx_init(psc_id, 10, fifo, 512);
-       psc_i2s->playback.bcom_task =
-               bcom_psc_gen_bd_tx_init(psc_id, 10, fifo);
-       if (!psc_i2s->capture.bcom_task ||
-           !psc_i2s->playback.bcom_task) {
-               dev_err(&op->dev, "Could not allocate bestcomm tasks\n");
-               iounmap(regs);
-               kfree(psc_i2s);
-               return -ENODEV;
+       rc = mpc5200_audio_dma_create(op);
+       if (rc != 0)
+               return rc;
+
+       rc = snd_soc_register_dais(psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai));
+       if (rc != 0) {
+               pr_err("Failed to register DAI\n");
+               return 0;
        }
 
-       /* Disable all interrupts and reset the PSC */
-       out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0);
-       out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset transmitter */
-       out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset receiver */
-       out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */
-       out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */
+       psc_dma = dev_get_drvdata(&op->dev);
+       regs = psc_dma->psc_regs;
 
        /* Configure the serial interface mode; defaulting to CODEC8 mode */
-       psc_i2s->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S |
+       psc_dma->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S |
                        MPC52xx_PSC_SICR_CLKPOL;
-       if (of_get_property(op->node, "fsl,cellslave", NULL))
-               psc_i2s->sicr |= MPC52xx_PSC_SICR_CELLSLAVE |
-                                MPC52xx_PSC_SICR_GENCLK;
-       out_be32(&psc_i2s->psc_regs->sicr,
-                psc_i2s->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8);
+       out_be32(&psc_dma->psc_regs->sicr,
+                psc_dma->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8);
 
        /* Check for the codec handle.  If it is not present then we
         * are done */
        if (!of_get_property(op->node, "codec-handle", NULL))
                return 0;
 
-       /* Set up mode register;
-        * First write: RxRdy (FIFO Alarm) generates rx FIFO irq
-        * Second write: register Normal mode for non loopback
-        */
-       out_8(&psc_i2s->psc_regs->mode, 0);
-       out_8(&psc_i2s->psc_regs->mode, 0);
-
-       /* Set the TX and RX fifo alarm thresholds */
-       out_be16(&psc_i2s->fifo_regs->rfalarm, 0x100);
-       out_8(&psc_i2s->fifo_regs->rfcntl, 0x4);
-       out_be16(&psc_i2s->fifo_regs->tfalarm, 0x100);
-       out_8(&psc_i2s->fifo_regs->tfcntl, 0x7);
-
-       /* Lookup the IRQ numbers */
-       psc_i2s->playback.irq =
-               bcom_get_task_irq(psc_i2s->playback.bcom_task);
-       psc_i2s->capture.irq =
-               bcom_get_task_irq(psc_i2s->capture.bcom_task);
-
-       /* Save what we've done so it can be found again later */
-       dev_set_drvdata(&op->dev, psc_i2s);
-
-       /* Register the SYSFS files */
-       rc = device_create_file(psc_i2s->dev, &dev_attr_status);
-       rc |= device_create_file(psc_i2s->dev, &dev_attr_capture_overrun);
-       rc |= device_create_file(psc_i2s->dev, &dev_attr_playback_underrun);
-       if (rc)
-               dev_info(psc_i2s->dev, "error creating sysfs files\n");
-
-       snd_soc_register_platform(&psc_i2s_pcm_soc_platform);
-
-       /* Tell the ASoC OF helpers about it */
-       of_snd_soc_register_platform(&psc_i2s_pcm_soc_platform, op->node,
-                                    &psc_i2s->dai);
+       /* Due to errata in the dma mode; need to line up enabling
+        * the transmitter with a transition on the frame sync
+        * line */
+
+       /* first make sure it is low */
+       while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) != 0)
+               ;
+       /* then wait for the transition to high */
+       while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) == 0)
+               ;
+       /* Finally, enable the PSC.
+        * Receiver must always be enabled; even when we only want
+        * transmit.  (see 15.3.2.3 of MPC5200B User's Guide) */
+
+       /* Go */
+       out_8(&psc_dma->psc_regs->command,
+                       MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE);
 
        return 0;
+
 }
 
 static int __devexit psc_i2s_of_remove(struct of_device *op)
 {
-       struct psc_i2s *psc_i2s = dev_get_drvdata(&op->dev);
-
-       dev_dbg(&op->dev, "psc_i2s_remove()\n");
-
-       snd_soc_unregister_platform(&psc_i2s_pcm_soc_platform);
-
-       bcom_gen_bd_rx_release(psc_i2s->capture.bcom_task);
-       bcom_gen_bd_tx_release(psc_i2s->playback.bcom_task);
-
-       iounmap(psc_i2s->psc_regs);
-       iounmap(psc_i2s->fifo_regs);
-       kfree(psc_i2s);
-       dev_set_drvdata(&op->dev, NULL);
-
-       return 0;
+       return mpc5200_audio_dma_destroy(op);
 }
 
 /* Match table for of_platform binding */
 static struct of_device_id psc_i2s_match[] __devinitdata = {
        { .compatible = "fsl,mpc5200-psc-i2s", },
+       { .compatible = "fsl,mpc5200b-psc-i2s", },
        {}
 };
 MODULE_DEVICE_TABLE(of, psc_i2s_match);
@@ -892,4 +245,7 @@ static void __exit psc_i2s_exit(void)
 }
 module_exit(psc_i2s_exit);
 
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver");
+MODULE_LICENSE("GPL");
 
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.h b/sound/soc/fsl/mpc5200_psc_i2s.h
new file mode 100644 (file)
index 0000000..ce55e07
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * Freescale MPC5200 PSC in I2S mode
+ * ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ */
+
+#ifndef __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__
+#define __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__
+
+extern struct snd_soc_dai psc_i2s_dai[];
+
+#endif /* __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__ */
diff --git a/sound/soc/fsl/pcm030-audio-fabric.c b/sound/soc/fsl/pcm030-audio-fabric.c
new file mode 100644 (file)
index 0000000..8766f7a
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Phytec pcm030 driver for the PSC of the Freescale MPC52xx
+ * configured as AC97 interface
+ *
+ * Copyright 2008 Jon Smirl, Digispeaker
+ * Author: Jon Smirl <jonsmirl@gmail.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/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/soc-of-simple.h>
+
+#include "mpc5200_dma.h"
+#include "mpc5200_psc_ac97.h"
+#include "../codecs/wm9712.h"
+
+static struct snd_soc_device device;
+static struct snd_soc_card card;
+
+static struct snd_soc_dai_link pcm030_fabric_dai[] = {
+{
+       .name = "AC97",
+       .stream_name = "AC97 Analog",
+       .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
+       .cpu_dai = &psc_ac97_dai[MPC5200_AC97_NORMAL],
+},
+{
+       .name = "AC97",
+       .stream_name = "AC97 IEC958",
+       .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
+       .cpu_dai = &psc_ac97_dai[MPC5200_AC97_SPDIF],
+},
+};
+
+static __init int pcm030_fabric_init(void)
+{
+       struct platform_device *pdev;
+       int rc;
+
+       if (!machine_is_compatible("phytec,pcm030"))
+               return -ENODEV;
+
+       card.platform = &mpc5200_audio_dma_platform;
+       card.name = "pcm030";
+       card.dai_link = pcm030_fabric_dai;
+       card.num_links = ARRAY_SIZE(pcm030_fabric_dai);
+
+       device.card = &card;
+       device.codec_dev = &soc_codec_dev_wm9712;
+
+       pdev = platform_device_alloc("soc-audio", 1);
+       if (!pdev) {
+               pr_err("pcm030_fabric_init: platform_device_alloc() failed\n");
+               return -ENODEV;
+       }
+
+       platform_set_drvdata(pdev, &device);
+       device.dev = &pdev->dev;
+
+       rc = platform_device_add(pdev);
+       if (rc) {
+               pr_err("pcm030_fabric_init: platform_device_add() failed\n");
+               return -ENODEV;
+       }
+       return 0;
+}
+
+module_init(pcm030_fabric_init);
+
+
+MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
+MODULE_DESCRIPTION(DRV_NAME ": mpc5200 pcm030 fabric driver");
+MODULE_LICENSE("GPL");
+
index 675732e724d5ccfa5a824deec7365fecbaf86f96..b771238662b6407fa8a10aa5e1050dbc633a1389 100644 (file)
@@ -39,6 +39,14 @@ config SND_OMAP_SOC_OMAP2EVM
        help
          Say Y if you want to add support for SoC audio on the omap2evm board.
 
+config SND_OMAP_SOC_OMAP3EVM
+       tristate "SoC Audio support for OMAP3EVM board"
+       depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP3EVM
+       select SND_OMAP_SOC_MCBSP
+       select SND_SOC_TWL4030
+       help
+         Say Y if you want to add support for SoC audio on the omap3evm board.
+
 config SND_OMAP_SOC_SDP3430
        tristate "SoC Audio support for Texas Instruments SDP3430"
        depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_3430SDP
index 0c9e4ac376601051a2136a861e68ae921ba439c0..a37f498623896426d0b50a6529834d1b9ee90888 100644 (file)
@@ -10,6 +10,7 @@ snd-soc-n810-objs := n810.o
 snd-soc-osk5912-objs := osk5912.o
 snd-soc-overo-objs := overo.o
 snd-soc-omap2evm-objs := omap2evm.o
+snd-soc-omap3evm-objs := omap3evm.o
 snd-soc-sdp3430-objs := sdp3430.o
 snd-soc-omap3pandora-objs := omap3pandora.o
 snd-soc-omap3beagle-objs := omap3beagle.o
@@ -18,6 +19,7 @@ obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
 obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
 obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o
 obj-$(CONFIG_MACH_OMAP2EVM) += snd-soc-omap2evm.o
+obj-$(CONFIG_MACH_OMAP3EVM) += snd-soc-omap3evm.o
 obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o
 obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
 obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o
index 91ef17992de52c68711da692679be075b7069aec..b60b1dfbc435f0036f9d18447b6e45c617e39439 100644 (file)
@@ -383,10 +383,9 @@ static int __init n810_soc_init(void)
        clk_set_parent(sys_clkout2_src, func96m_clk);
        clk_set_rate(sys_clkout2, 12000000);
 
-       if (gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0)
-               BUG();
-       if (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0)
-               BUG();
+       BUG_ON((gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0) ||
+              (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0));
+
        gpio_direction_output(N810_HEADSET_AMP_GPIO, 0);
        gpio_direction_output(N810_SPEAKER_AMP_GPIO, 0);
 
index 9126142838486926c00fbb3b6586c06f9b1c696b..a5d46a7b196a324cebce928cfaed329afaec8cf3 100644 (file)
@@ -215,8 +215,9 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
        struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
        struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
        int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id;
-       int wlen, channels;
+       int wlen, channels, wpf;
        unsigned long port;
+       unsigned int format;
 
        if (cpu_class_is_omap1()) {
                dma = omap1_dma_reqs[bus_id][substream->stream];
@@ -244,18 +245,24 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
                return 0;
        }
 
-       channels = params_channels(params);
+       format = mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+       wpf = channels = params_channels(params);
        switch (channels) {
        case 2:
-               /* Use dual-phase frames */
-               regs->rcr2      |= RPHASE;
-               regs->xcr2      |= XPHASE;
+               if (format == SND_SOC_DAIFMT_I2S) {
+                       /* Use dual-phase frames */
+                       regs->rcr2      |= RPHASE;
+                       regs->xcr2      |= XPHASE;
+                       /* Set 1 word per (McBSP) frame for phase1 and phase2 */
+                       wpf--;
+                       regs->rcr2      |= RFRLEN2(wpf - 1);
+                       regs->xcr2      |= XFRLEN2(wpf - 1);
+               }
        case 1:
-               /* Set 1 word per (McBSP) frame */
-               regs->rcr2      |= RFRLEN2(1 - 1);
-               regs->rcr1      |= RFRLEN1(1 - 1);
-               regs->xcr2      |= XFRLEN2(1 - 1);
-               regs->xcr1      |= XFRLEN1(1 - 1);
+       case 4:
+               /* Set word per (McBSP) frame for phase1 */
+               regs->rcr1      |= RFRLEN1(wpf - 1);
+               regs->xcr1      |= XFRLEN1(wpf - 1);
                break;
        default:
                /* Unsupported number of channels */
@@ -277,11 +284,12 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
        }
 
        /* Set FS period and length in terms of bit clock periods */
-       switch (mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       switch (format) {
        case SND_SOC_DAIFMT_I2S:
-               regs->srgr2     |= FPER(wlen * 2 - 1);
+               regs->srgr2     |= FPER(wlen * channels - 1);
                regs->srgr1     |= FWID(wlen - 1);
                break;
+       case SND_SOC_DAIFMT_DSP_A:
        case SND_SOC_DAIFMT_DSP_B:
                regs->srgr2     |= FPER(wlen * channels - 1);
                regs->srgr1     |= FWID(0);
@@ -326,6 +334,13 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                regs->rcr2      |= RDATDLY(1);
                regs->xcr2      |= XDATDLY(1);
                break;
+       case SND_SOC_DAIFMT_DSP_A:
+               /* 1-bit data delay */
+               regs->rcr2      |= RDATDLY(1);
+               regs->xcr2      |= XDATDLY(1);
+               /* Invert FS polarity configuration */
+               temp_fmt ^= SND_SOC_DAIFMT_NB_IF;
+               break;
        case SND_SOC_DAIFMT_DSP_B:
                /* 0-bit data delay */
                regs->rcr2      |= RDATDLY(0);
@@ -492,13 +507,13 @@ static struct snd_soc_dai_ops omap_mcbsp_dai_ops = {
        .id = (link_id),                                        \
        .playback = {                                           \
                .channels_min = 1,                              \
-               .channels_max = 2,                              \
+               .channels_max = 4,                              \
                .rates = OMAP_MCBSP_RATES,                      \
                .formats = SNDRV_PCM_FMTBIT_S16_LE,             \
        },                                                      \
        .capture = {                                            \
                .channels_min = 1,                              \
-               .channels_max = 2,                              \
+               .channels_max = 4,                              \
                .rates = OMAP_MCBSP_RATES,                      \
                .formats = SNDRV_PCM_FMTBIT_S16_LE,             \
        },                                                      \
index 07cf7f46b584f37f83404d59133731eaa965df9b..6454e15f7d28cb274bf3e21abe76b9b1ee6f06ee 100644 (file)
@@ -87,8 +87,10 @@ static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
        struct omap_pcm_dma_data *dma_data = rtd->dai->cpu_dai->dma_data;
        int err = 0;
 
+       /* return if this is a bufferless transfer e.g.
+        * codec <--> BT codec or GSM modem -- lg FIXME */
        if (!dma_data)
-               return -ENODEV;
+               return 0;
 
        snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
        runtime->dma_bytes = params_buffer_bytes(params);
@@ -134,6 +136,11 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream)
        struct omap_pcm_dma_data *dma_data = prtd->dma_data;
        struct omap_dma_channel_params dma_params;
 
+       /* return if this is a bufferless transfer e.g.
+        * codec <--> BT codec or GSM modem -- lg FIXME */
+       if (!prtd->dma_data)
+               return 0;
+
        memset(&dma_params, 0, sizeof(dma_params));
        /*
         * Note: Regardless of interface data formats supported by OMAP McBSP
index 0c2322dcf02a489c82653a257dcd8fe80aa4e33b..027e1a40f8a10d774b31b9aad597ada5655dc59a 100644 (file)
@@ -86,7 +86,7 @@ static struct snd_soc_dai_link omap2evm_dai = {
        .name = "TWL4030",
        .stream_name = "TWL4030",
        .cpu_dai = &omap_mcbsp_dai[0],
-       .codec_dai = &twl4030_dai,
+       .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
        .ops = &omap2evm_ops,
 };
 
index fd24a4acd2f5d622888b5f7dd8481a17361ac4b9..b0cff9f33b7eb30be7b2ce51376e643adc237d22 100644 (file)
@@ -41,23 +41,33 @@ static int omap3beagle_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       unsigned int fmt;
        int ret;
 
+       switch (params_channels(params)) {
+       case 2: /* Stereo I2S mode */
+               fmt =   SND_SOC_DAIFMT_I2S |
+                       SND_SOC_DAIFMT_NB_NF |
+                       SND_SOC_DAIFMT_CBM_CFM;
+               break;
+       case 4: /* Four channel TDM mode */
+               fmt =   SND_SOC_DAIFMT_DSP_A |
+                       SND_SOC_DAIFMT_IB_NF |
+                       SND_SOC_DAIFMT_CBM_CFM;
+               break;
+       default:
+               return -EINVAL;
+       }
+
        /* Set codec DAI configuration */
-       ret = snd_soc_dai_set_fmt(codec_dai,
-                                 SND_SOC_DAIFMT_I2S |
-                                 SND_SOC_DAIFMT_NB_NF |
-                                 SND_SOC_DAIFMT_CBM_CFM);
+       ret = snd_soc_dai_set_fmt(codec_dai, fmt);
        if (ret < 0) {
                printk(KERN_ERR "can't set codec DAI configuration\n");
                return ret;
        }
 
        /* Set cpu DAI configuration */
-       ret = snd_soc_dai_set_fmt(cpu_dai,
-                                 SND_SOC_DAIFMT_I2S |
-                                 SND_SOC_DAIFMT_NB_NF |
-                                 SND_SOC_DAIFMT_CBM_CFM);
+       ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
        if (ret < 0) {
                printk(KERN_ERR "can't set cpu DAI configuration\n");
                return ret;
@@ -83,7 +93,7 @@ static struct snd_soc_dai_link omap3beagle_dai = {
        .name = "TWL4030",
        .stream_name = "TWL4030",
        .cpu_dai = &omap_mcbsp_dai[0],
-       .codec_dai = &twl4030_dai,
+       .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
        .ops = &omap3beagle_ops,
 };
 
diff --git a/sound/soc/omap/omap3evm.c b/sound/soc/omap/omap3evm.c
new file mode 100644 (file)
index 0000000..9114c26
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * omap3evm.c  -- ALSA SoC support for OMAP3 EVM
+ *
+ * Author: Anuj Aggarwal <anuj.aggarwal@ti.com>
+ *
+ * Based on sound/soc/omap/beagle.c by Steve Sakoman
+ *
+ * Copyright (C) 2008 Texas Instruments, Incorporated
+ *
+ * This program is free software; 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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+static int omap3evm_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       int ret;
+
+       /* Set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "Can't set codec DAI configuration\n");
+               return ret;
+       }
+
+       /* Set cpu DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "Can't set cpu DAI configuration\n");
+               return ret;
+       }
+
+       /* Set the codec system clock for DAC and ADC */
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+                                    SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               printk(KERN_ERR "Can't set codec system clock\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops omap3evm_ops = {
+       .hw_params = omap3evm_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link omap3evm_dai = {
+       .name           = "TWL4030",
+       .stream_name    = "TWL4030",
+       .cpu_dai        = &omap_mcbsp_dai[0],
+       .codec_dai      = &twl4030_dai[TWL4030_DAI_HIFI],
+       .ops            = &omap3evm_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_omap3evm = {
+       .name = "omap3evm",
+       .platform = &omap_soc_platform,
+       .dai_link = &omap3evm_dai,
+       .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device omap3evm_snd_devdata = {
+       .card = &snd_soc_omap3evm,
+       .codec_dev = &soc_codec_dev_twl4030,
+};
+
+static struct platform_device *omap3evm_snd_device;
+
+static int __init omap3evm_soc_init(void)
+{
+       int ret;
+
+       if (!machine_is_omap3evm()) {
+               pr_err("Not OMAP3 EVM!\n");
+               return -ENODEV;
+       }
+       pr_info("OMAP3 EVM SoC init\n");
+
+       omap3evm_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!omap3evm_snd_device) {
+               printk(KERN_ERR "Platform device allocation failed\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(omap3evm_snd_device, &omap3evm_snd_devdata);
+       omap3evm_snd_devdata.dev = &omap3evm_snd_device->dev;
+       *(unsigned int *)omap3evm_dai.cpu_dai->private_data = 1;
+
+       ret = platform_device_add(omap3evm_snd_device);
+       if (ret)
+               goto err1;
+
+       return 0;
+
+err1:
+       printk(KERN_ERR "Unable to add platform device\n");
+       platform_device_put(omap3evm_snd_device);
+
+       return ret;
+}
+
+static void __exit omap3evm_soc_exit(void)
+{
+       platform_device_unregister(omap3evm_snd_device);
+}
+
+module_init(omap3evm_soc_init);
+module_exit(omap3evm_soc_exit);
+
+MODULE_AUTHOR("Anuj Aggarwal <anuj.aggarwal@ti.com>");
+MODULE_DESCRIPTION("ALSA SoC OMAP3 EVM");
+MODULE_LICENSE("GPLv2");
index fe282d4ef4222d559e51a8c8566bbccb813fb6fb..ad219aaf7cb80678ae80c1263f41df565803cfe0 100644 (file)
@@ -228,14 +228,14 @@ static struct snd_soc_dai_link omap3pandora_dai[] = {
                .name = "PCM1773",
                .stream_name = "HiFi Out",
                .cpu_dai = &omap_mcbsp_dai[0],
-               .codec_dai = &twl4030_dai,
+               .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
                .ops = &omap3pandora_out_ops,
                .init = omap3pandora_out_init,
        }, {
                .name = "TWL4030",
                .stream_name = "Line/Mic In",
                .cpu_dai = &omap_mcbsp_dai[1],
-               .codec_dai = &twl4030_dai,
+               .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
                .ops = &omap3pandora_in_ops,
                .init = omap3pandora_in_init,
        }
index a72dc4e159e5f813ff1369c93a788671ba5747c2..ec4f8fd8b3a2ae9a1e79a3cb91c033a2b1ae4cf6 100644 (file)
@@ -83,7 +83,7 @@ static struct snd_soc_dai_link overo_dai = {
        .name = "TWL4030",
        .stream_name = "TWL4030",
        .cpu_dai = &omap_mcbsp_dai[0],
-       .codec_dai = &twl4030_dai,
+       .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
        .ops = &overo_ops,
 };
 
index 10f1c867f11d672692aef958f64750f7f691d5c3..b719e5db4f574cbced41497987e7953591eca81e 100644 (file)
@@ -84,6 +84,49 @@ static struct snd_soc_ops sdp3430_ops = {
        .hw_params = sdp3430_hw_params,
 };
 
+static int sdp3430_hw_voice_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       int ret;
+
+       /* Set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai,
+                               SND_SOC_DAIFMT_DSP_A |
+                               SND_SOC_DAIFMT_IB_NF |
+                               SND_SOC_DAIFMT_CBS_CFM);
+       if (ret) {
+               printk(KERN_ERR "can't set codec DAI configuration\n");
+               return ret;
+       }
+
+       /* Set cpu DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai,
+                               SND_SOC_DAIFMT_DSP_A |
+                               SND_SOC_DAIFMT_IB_NF |
+                               SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set cpu DAI configuration\n");
+               return ret;
+       }
+
+       /* Set the codec system clock for DAC and ADC */
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+                                           SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec system clock\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops sdp3430_voice_ops = {
+       .hw_params = sdp3430_hw_voice_params,
+};
+
 /* Headset jack */
 static struct snd_soc_jack hs_jack;
 
@@ -192,28 +235,58 @@ static int sdp3430_twl4030_init(struct snd_soc_codec *codec)
        return ret;
 }
 
+static int sdp3430_twl4030_voice_init(struct snd_soc_codec *codec)
+{
+       unsigned short reg;
+
+       /* Enable voice interface */
+       reg = codec->read(codec, TWL4030_REG_VOICE_IF);
+       reg |= TWL4030_VIF_DIN_EN | TWL4030_VIF_DOUT_EN | TWL4030_VIF_EN;
+       codec->write(codec, TWL4030_REG_VOICE_IF, reg);
+
+       return 0;
+}
+
+
 /* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link sdp3430_dai = {
-       .name = "TWL4030",
-       .stream_name = "TWL4030",
-       .cpu_dai = &omap_mcbsp_dai[0],
-       .codec_dai = &twl4030_dai,
-       .init = sdp3430_twl4030_init,
-       .ops = &sdp3430_ops,
+static struct snd_soc_dai_link sdp3430_dai[] = {
+       {
+               .name = "TWL4030 I2S",
+               .stream_name = "TWL4030 Audio",
+               .cpu_dai = &omap_mcbsp_dai[0],
+               .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
+               .init = sdp3430_twl4030_init,
+               .ops = &sdp3430_ops,
+       },
+       {
+               .name = "TWL4030 PCM",
+               .stream_name = "TWL4030 Voice",
+               .cpu_dai = &omap_mcbsp_dai[1],
+               .codec_dai = &twl4030_dai[TWL4030_DAI_VOICE],
+               .init = sdp3430_twl4030_voice_init,
+               .ops = &sdp3430_voice_ops,
+       },
 };
 
 /* Audio machine driver */
 static struct snd_soc_card snd_soc_sdp3430 = {
        .name = "SDP3430",
        .platform = &omap_soc_platform,
-       .dai_link = &sdp3430_dai,
-       .num_links = 1,
+       .dai_link = sdp3430_dai,
+       .num_links = ARRAY_SIZE(sdp3430_dai),
+};
+
+/* twl4030 setup */
+static struct twl4030_setup_data twl4030_setup = {
+       .ramp_delay_value = 3,
+       .sysclk = 26000,
 };
 
 /* Audio subsystem */
 static struct snd_soc_device sdp3430_snd_devdata = {
        .card = &snd_soc_sdp3430,
        .codec_dev = &soc_codec_dev_twl4030,
+       .codec_data = &twl4030_setup,
 };
 
 static struct platform_device *sdp3430_snd_device;
@@ -236,7 +309,8 @@ static int __init sdp3430_soc_init(void)
 
        platform_set_drvdata(sdp3430_snd_device, &sdp3430_snd_devdata);
        sdp3430_snd_devdata.dev = &sdp3430_snd_device->dev;
-       *(unsigned int *)sdp3430_dai.cpu_dai->private_data = 1; /* McBSP2 */
+       *(unsigned int *)sdp3430_dai[0].cpu_dai->private_data = 1; /* McBSP2 */
+       *(unsigned int *)sdp3430_dai[1].cpu_dai->private_data = 2; /* McBSP3 */
 
        ret = platform_device_add(sdp3430_snd_device);
        if (ret)
index ad8a10fe629826d22b96fe7efbb52f8a885237e7..dcd163a4ee9ac02283ed1ff397822249f96c5e0d 100644 (file)
@@ -89,13 +89,13 @@ config SND_PXA2XX_SOC_E800
          Toshiba e800 PDA
 
 config SND_PXA2XX_SOC_EM_X270
-       tristate "SoC Audio support for CompuLab EM-x270"
+       tristate "SoC Audio support for CompuLab EM-x270, eXeda and CM-X300"
        depends on SND_PXA2XX_SOC && MACH_EM_X270
        select SND_PXA2XX_SOC_AC97
        select SND_SOC_WM9712
        help
          Say Y if you want to add support for SoC audio on
-         CompuLab EM-x270.
+         CompuLab EM-x270, eXeda and CM-X300 machines.
 
 config SND_PXA2XX_SOC_PALM27X
        bool "SoC Audio support for Palm T|X, T5 and LifeDrive"
@@ -134,3 +134,12 @@ config SND_PXA2XX_SOC_MIOA701
         help
           Say Y if you want to add support for SoC audio on the
           MIO A701.
+
+config SND_PXA2XX_SOC_IMOTE2
+       tristate "SoC Audio support for IMote 2"
+       depends on SND_PXA2XX_SOC && MACH_INTELMOTE2
+       select SND_PXA2XX_SOC_I2S
+       select SND_SOC_WM8940
+       help
+         Say Y if you want to add support for SoC audio on the
+        IMote 2.
index 4b90c3ccae4510209dc7f99f8b802ec299778278..6e096b48033550f4ad4305bb7ab08f7cff06efe4 100644 (file)
@@ -22,6 +22,7 @@ snd-soc-palm27x-objs := palm27x.o
 snd-soc-zylonite-objs := zylonite.o
 snd-soc-magician-objs := magician.o
 snd-soc-mioa701-objs := mioa701_wm9713.o
+snd-soc-imote2-objs := imote2.o
 
 obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
 obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
@@ -35,3 +36,4 @@ obj-$(CONFIG_SND_PXA2XX_SOC_PALM27X) += snd-soc-palm27x.o
 obj-$(CONFIG_SND_PXA2XX_SOC_MAGICIAN) += snd-soc-magician.o
 obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o
 obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o
+obj-$(CONFIG_SND_PXA2XX_SOC_IMOTE2) += snd-soc-imote2.o
index 949be9c2a01ba88bab7724d5d81c13c9c510c0d7..f4756e4025fd1dd0c06080d0e26a8c121dc52c2e 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * em-x270.c  --  SoC audio for EM-X270
+ * SoC audio driver for EM-X270, eXeda and CM-X300
  *
- * Copyright 2007 CompuLab, Ltd.
+ * Copyright 2007, 2009 CompuLab, Ltd.
  *
  * Author: Mike Rapoport <mike@compulab.co.il>
  *
@@ -68,7 +68,8 @@ static int __init em_x270_init(void)
 {
        int ret;
 
-       if (!machine_is_em_x270())
+       if (!(machine_is_em_x270() || machine_is_exeda()
+             || machine_is_cm_x300()))
                return -ENODEV;
 
        em_x270_snd_device = platform_device_alloc("soc-audio", -1);
@@ -95,5 +96,5 @@ module_exit(em_x270_exit);
 
 /* Module information */
 MODULE_AUTHOR("Mike Rapoport");
-MODULE_DESCRIPTION("ALSA SoC EM-X270");
+MODULE_DESCRIPTION("ALSA SoC EM-X270, eXeda and CM-X300");
 MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/imote2.c b/sound/soc/pxa/imote2.c
new file mode 100644 (file)
index 0000000..405587a
--- /dev/null
@@ -0,0 +1,114 @@
+
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include <asm/mach-types.h>
+
+#include "../codecs/wm8940.h"
+#include "pxa2xx-i2s.h"
+#include "pxa2xx-pcm.h"
+
+static int imote2_asoc_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       unsigned int clk = 0;
+       int ret;
+
+       switch (params_rate(params)) {
+       case 8000:
+       case 16000:
+       case 48000:
+       case 96000:
+               clk = 12288000;
+               break;
+       case 11025:
+       case 22050:
+       case 44100:
+               clk = 11289600;
+               break;
+       }
+
+       /* set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+                                 | SND_SOC_DAIFMT_NB_NF
+                                 | SND_SOC_DAIFMT_CBS_CFS);
+       if (ret < 0)
+               return ret;
+
+       /* CPU should be clock master */
+       ret = snd_soc_dai_set_fmt(cpu_dai,  SND_SOC_DAIFMT_I2S
+                                 | SND_SOC_DAIFMT_NB_NF
+                                 | SND_SOC_DAIFMT_CBS_CFS);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
+                                    SND_SOC_CLOCK_IN);
+       if (ret < 0)
+               return ret;
+
+       /* set the I2S system clock as input (unused) */
+       ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, clk,
+               SND_SOC_CLOCK_OUT);
+
+       return ret;
+}
+
+static struct snd_soc_ops imote2_asoc_ops = {
+       .hw_params = imote2_asoc_hw_params,
+};
+
+static struct snd_soc_dai_link imote2_dai = {
+       .name = "WM8940",
+       .stream_name = "WM8940",
+       .cpu_dai = &pxa_i2s_dai,
+       .codec_dai = &wm8940_dai,
+       .ops = &imote2_asoc_ops,
+};
+
+static struct snd_soc_card snd_soc_imote2 = {
+       .name = "Imote2",
+       .platform = &pxa2xx_soc_platform,
+       .dai_link = &imote2_dai,
+       .num_links = 1,
+};
+
+static struct snd_soc_device imote2_snd_devdata = {
+       .card = &snd_soc_imote2,
+       .codec_dev = &soc_codec_dev_wm8940,
+};
+
+static struct platform_device *imote2_snd_device;
+
+static int __init imote2_asoc_init(void)
+{
+       int ret;
+
+       if (!machine_is_intelmote2())
+               return -ENODEV;
+       imote2_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!imote2_snd_device)
+               return -ENOMEM;
+
+       platform_set_drvdata(imote2_snd_device, &imote2_snd_devdata);
+       imote2_snd_devdata.dev = &imote2_snd_device->dev;
+       ret = platform_device_add(imote2_snd_device);
+       if (ret)
+               platform_device_put(imote2_snd_device);
+
+       return ret;
+}
+module_init(imote2_asoc_init);
+
+static void __exit imote2_asoc_exit(void)
+{
+       platform_device_unregister(imote2_snd_device);
+}
+module_exit(imote2_asoc_exit);
+
+MODULE_AUTHOR("Jonathan Cameron");
+MODULE_DESCRIPTION("ALSA SoC Imote 2");
+MODULE_LICENSE("GPL");
index 0625c342a1c909b9f14f7d5f05fad3a8f1060886..c89a3cdf31e4aea8ad464baf720512deb0ccd319 100644 (file)
@@ -106,7 +106,7 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream,
                        /* 513156 Hz ~= _2_ * 8000 Hz * 32 (+0.23%) */
                        acds = PXA_SSP_CLK_AUDIO_DIV_16;
                        break;
-               case 32:
+               default: /* 32 */
                        /* 1026312 Hz ~= _2_ * 8000 Hz * 64 (+0.23%) */
                        acds = PXA_SSP_CLK_AUDIO_DIV_8;
                }
@@ -118,7 +118,7 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream,
                        /* 351375 Hz ~= 11025 Hz * 32 (-0.41%) */
                        acds = PXA_SSP_CLK_AUDIO_DIV_4;
                        break;
-               case 32:
+               default: /* 32 */
                        /* 702750 Hz ~= 11025 Hz * 64 (-0.41%) */
                        acds = PXA_SSP_CLK_AUDIO_DIV_2;
                }
@@ -130,7 +130,7 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream,
                        /* 702750 Hz ~= 22050 Hz * 32 (-0.41%) */
                        acds = PXA_SSP_CLK_AUDIO_DIV_2;
                        break;
-               case 32:
+               default: /* 32 */
                        /* 1405500 Hz ~= 22050 Hz * 64 (-0.41%) */
                        acds = PXA_SSP_CLK_AUDIO_DIV_1;
                }
@@ -142,7 +142,7 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream,
                        /* 1405500 Hz ~= 44100 Hz * 32 (-0.41%) */
                        acds = PXA_SSP_CLK_AUDIO_DIV_2;
                        break;
-               case 32:
+               default: /* 32 */
                        /* 2811000 Hz ~= 44100 Hz * 64 (-0.41%) */
                        acds = PXA_SSP_CLK_AUDIO_DIV_1;
                }
@@ -154,19 +154,20 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream,
                        /* 1529375 Hz ~= 48000 Hz * 32 (-0.44%) */
                        acds = PXA_SSP_CLK_AUDIO_DIV_2;
                        break;
-               case 32:
+               default: /* 32 */
                        /* 3058750 Hz ~= 48000 Hz * 64 (-0.44%) */
                        acds = PXA_SSP_CLK_AUDIO_DIV_1;
                }
                break;
        case 96000:
+       default:
                acps = 12235000;
                switch (width) {
                case 16:
                        /* 3058750 Hz ~= 96000 Hz * 32 (-0.44%) */
                        acds = PXA_SSP_CLK_AUDIO_DIV_1;
                        break;
-               case 32:
+               default: /* 32 */
                        /* 6117500 Hz ~= 96000 Hz * 64 (-0.44%) */
                        acds = PXA_SSP_CLK_AUDIO_DIV_2;
                        div4 = PXA_SSP_CLK_SCDB_1;
index 286be31545df7599fc7b50b6b27e1a43136830c5..19c45409d94c10257548fff8219395f833412fac 100644 (file)
@@ -50,139 +50,6 @@ struct ssp_priv {
 #endif
 };
 
-#define PXA2xx_SSP1_BASE       0x41000000
-#define PXA27x_SSP2_BASE       0x41700000
-#define PXA27x_SSP3_BASE       0x41900000
-#define PXA3xx_SSP4_BASE       0x41a00000
-
-static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_mono_out = {
-       .name                   = "SSP1 PCM Mono out",
-       .dev_addr               = PXA2xx_SSP1_BASE + SSDR,
-       .drcmr                  = &DRCMR(14),
-       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
-                                 DCMD_BURST16 | DCMD_WIDTH2,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_mono_in = {
-       .name                   = "SSP1 PCM Mono in",
-       .dev_addr               = PXA2xx_SSP1_BASE + SSDR,
-       .drcmr                  = &DRCMR(13),
-       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
-                                 DCMD_BURST16 | DCMD_WIDTH2,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_stereo_out = {
-       .name                   = "SSP1 PCM Stereo out",
-       .dev_addr               = PXA2xx_SSP1_BASE + SSDR,
-       .drcmr                  = &DRCMR(14),
-       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
-                                 DCMD_BURST16 | DCMD_WIDTH4,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_stereo_in = {
-       .name                   = "SSP1 PCM Stereo in",
-       .dev_addr               = PXA2xx_SSP1_BASE + SSDR,
-       .drcmr                  = &DRCMR(13),
-       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
-                                 DCMD_BURST16 | DCMD_WIDTH4,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_mono_out = {
-       .name                   = "SSP2 PCM Mono out",
-       .dev_addr               = PXA27x_SSP2_BASE + SSDR,
-       .drcmr                  = &DRCMR(16),
-       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
-                                 DCMD_BURST16 | DCMD_WIDTH2,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_mono_in = {
-       .name                   = "SSP2 PCM Mono in",
-       .dev_addr               = PXA27x_SSP2_BASE + SSDR,
-       .drcmr                  = &DRCMR(15),
-       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
-                                 DCMD_BURST16 | DCMD_WIDTH2,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_stereo_out = {
-       .name                   = "SSP2 PCM Stereo out",
-       .dev_addr               = PXA27x_SSP2_BASE + SSDR,
-       .drcmr                  = &DRCMR(16),
-       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
-                                 DCMD_BURST16 | DCMD_WIDTH4,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_stereo_in = {
-       .name                   = "SSP2 PCM Stereo in",
-       .dev_addr               = PXA27x_SSP2_BASE + SSDR,
-       .drcmr                  = &DRCMR(15),
-       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
-                                 DCMD_BURST16 | DCMD_WIDTH4,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_mono_out = {
-       .name                   = "SSP3 PCM Mono out",
-       .dev_addr               = PXA27x_SSP3_BASE + SSDR,
-       .drcmr                  = &DRCMR(67),
-       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
-                                 DCMD_BURST16 | DCMD_WIDTH2,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_mono_in = {
-       .name                   = "SSP3 PCM Mono in",
-       .dev_addr               = PXA27x_SSP3_BASE + SSDR,
-       .drcmr                  = &DRCMR(66),
-       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
-                                 DCMD_BURST16 | DCMD_WIDTH2,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_stereo_out = {
-       .name                   = "SSP3 PCM Stereo out",
-       .dev_addr               = PXA27x_SSP3_BASE + SSDR,
-       .drcmr                  = &DRCMR(67),
-       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
-                                 DCMD_BURST16 | DCMD_WIDTH4,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_stereo_in = {
-       .name                   = "SSP3 PCM Stereo in",
-       .dev_addr               = PXA27x_SSP3_BASE + SSDR,
-       .drcmr                  = &DRCMR(66),
-       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
-                                 DCMD_BURST16 | DCMD_WIDTH4,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_mono_out = {
-       .name                   = "SSP4 PCM Mono out",
-       .dev_addr               = PXA3xx_SSP4_BASE + SSDR,
-       .drcmr                  = &DRCMR(67),
-       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
-                                 DCMD_BURST16 | DCMD_WIDTH2,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_mono_in = {
-       .name                   = "SSP4 PCM Mono in",
-       .dev_addr               = PXA3xx_SSP4_BASE + SSDR,
-       .drcmr                  = &DRCMR(66),
-       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
-                                 DCMD_BURST16 | DCMD_WIDTH2,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_stereo_out = {
-       .name                   = "SSP4 PCM Stereo out",
-       .dev_addr               = PXA3xx_SSP4_BASE + SSDR,
-       .drcmr                  = &DRCMR(67),
-       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
-                                 DCMD_BURST16 | DCMD_WIDTH4,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_stereo_in = {
-       .name                   = "SSP4 PCM Stereo in",
-       .dev_addr               = PXA3xx_SSP4_BASE + SSDR,
-       .drcmr                  = &DRCMR(66),
-       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
-                                 DCMD_BURST16 | DCMD_WIDTH4,
-};
-
 static void dump_registers(struct ssp_device *ssp)
 {
        dev_dbg(&ssp->pdev->dev, "SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x\n",
@@ -194,25 +61,33 @@ static void dump_registers(struct ssp_device *ssp)
                 ssp_read_reg(ssp, SSACD));
 }
 
-static struct pxa2xx_pcm_dma_params *ssp_dma_params[4][4] = {
-       {
-               &pxa_ssp1_pcm_mono_out, &pxa_ssp1_pcm_mono_in,
-               &pxa_ssp1_pcm_stereo_out, &pxa_ssp1_pcm_stereo_in,
-       },
-       {
-               &pxa_ssp2_pcm_mono_out, &pxa_ssp2_pcm_mono_in,
-               &pxa_ssp2_pcm_stereo_out, &pxa_ssp2_pcm_stereo_in,
-       },
-       {
-               &pxa_ssp3_pcm_mono_out, &pxa_ssp3_pcm_mono_in,
-               &pxa_ssp3_pcm_stereo_out, &pxa_ssp3_pcm_stereo_in,
-       },
-       {
-               &pxa_ssp4_pcm_mono_out, &pxa_ssp4_pcm_mono_in,
-               &pxa_ssp4_pcm_stereo_out, &pxa_ssp4_pcm_stereo_in,
-       },
+struct pxa2xx_pcm_dma_data {
+       struct pxa2xx_pcm_dma_params params;
+       char name[20];
 };
 
+static struct pxa2xx_pcm_dma_params *
+ssp_get_dma_params(struct ssp_device *ssp, int width4, int out)
+{
+       struct pxa2xx_pcm_dma_data *dma;
+
+       dma = kzalloc(sizeof(struct pxa2xx_pcm_dma_data), GFP_KERNEL);
+       if (dma == NULL)
+               return NULL;
+
+       snprintf(dma->name, 20, "SSP%d PCM %s %s", ssp->port_id,
+                       width4 ? "32-bit" : "16-bit", out ? "out" : "in");
+
+       dma->params.name = dma->name;
+       dma->params.drcmr = &DRCMR(out ? ssp->drcmr_tx : ssp->drcmr_rx);
+       dma->params.dcmd = (out ? (DCMD_INCSRCADDR | DCMD_FLOWTRG) :
+                                 (DCMD_INCTRGADDR | DCMD_FLOWSRC)) |
+                       (width4 ? DCMD_WIDTH4 : DCMD_WIDTH2) | DCMD_BURST16;
+       dma->params.dev_addr = ssp->phys_base + SSDR;
+
+       return &dma->params;
+}
+
 static int pxa_ssp_startup(struct snd_pcm_substream *substream,
                           struct snd_soc_dai *dai)
 {
@@ -227,6 +102,11 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream,
                clk_enable(priv->dev.ssp->clk);
                ssp_disable(&priv->dev);
        }
+
+       if (cpu_dai->dma_data) {
+               kfree(cpu_dai->dma_data);
+               cpu_dai->dma_data = NULL;
+       }
        return ret;
 }
 
@@ -241,6 +121,11 @@ static void pxa_ssp_shutdown(struct snd_pcm_substream *substream,
                ssp_disable(&priv->dev);
                clk_disable(priv->dev.ssp->clk);
        }
+
+       if (cpu_dai->dma_data) {
+               kfree(cpu_dai->dma_data);
+               cpu_dai->dma_data = NULL;
+       }
 }
 
 #ifdef CONFIG_PM
@@ -323,7 +208,7 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
                ~(SSCR0_ECS |  SSCR0_NCS | SSCR0_MOD | SSCR0_ACS);
 
        dev_dbg(&ssp->pdev->dev,
-               "pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %d\n",
+               "pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %u\n",
                cpu_dai->id, clk_id, freq);
 
        switch (clk_id) {
@@ -472,7 +357,7 @@ static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai,
                        ssacd |= (0x6 << 4);
 
                        dev_dbg(&ssp->pdev->dev,
-                               "Using SSACDD %x to supply %dHz\n",
+                               "Using SSACDD %x to supply %uHz\n",
                                val, freq_out);
                        break;
                }
@@ -589,7 +474,10 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                case SND_SOC_DAIFMT_NB_IF:
                        break;
                case SND_SOC_DAIFMT_IB_IF:
-                       sspsp |= SSPSP_SCMODE(3);
+                       sspsp |= SSPSP_SCMODE(2);
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP;
                        break;
                default:
                        return -EINVAL;
@@ -606,7 +494,13 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                case SND_SOC_DAIFMT_NB_NF:
                        sspsp |= SSPSP_SFRMP;
                        break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       break;
                case SND_SOC_DAIFMT_IB_IF:
+                       sspsp |= SSPSP_SCMODE(2);
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP;
                        break;
                default:
                        return -EINVAL;
@@ -644,25 +538,23 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
        struct ssp_priv *priv = cpu_dai->private_data;
        struct ssp_device *ssp = priv->dev.ssp;
-       int dma = 0, chn = params_channels(params);
+       int chn = params_channels(params);
        u32 sscr0;
        u32 sspsp;
        int width = snd_pcm_format_physical_width(params_format(params));
        int ttsa = ssp_read_reg(ssp, SSTSA) & 0xf;
 
-       /* select correct DMA params */
-       if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
-               dma = 1; /* capture DMA offset is 1,3 */
+       /* generate correct DMA params */
+       if (cpu_dai->dma_data)
+               kfree(cpu_dai->dma_data);
+
        /* Network mode with one active slot (ttsa == 1) can be used
         * to force 16-bit frame width on the wire (for S16_LE), even
         * with two channels. Use 16-bit DMA transfers for this case.
         */
-       if (((chn == 2) && (ttsa != 1)) || (width == 32))
-               dma += 2; /* 32-bit DMA offset is 2, 16-bit is 0 */
-
-       cpu_dai->dma_data = ssp_dma_params[cpu_dai->id][dma];
-
-       dev_dbg(&ssp->pdev->dev, "pxa_ssp_hw_params: dma %d\n", dma);
+       cpu_dai->dma_data = ssp_get_dma_params(ssp,
+                       ((chn == 2) && (ttsa != 1)) || (width == 32),
+                       substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
 
        /* we can only change the settings if the port is not in use */
        if (ssp_read_reg(ssp, SSCR0) & SSCR0_SSE)
index 2f4b6e489b7849a68eae4a76e4bb011c92bef129..4743e262895d9f1068260762ca6c4f64df5dd17d 100644 (file)
@@ -106,10 +106,8 @@ static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream,
        if (IS_ERR(clk_i2s))
                return PTR_ERR(clk_i2s);
 
-       if (!cpu_dai->active) {
-               SACR0 |= SACR0_RST;
+       if (!cpu_dai->active)
                SACR0 = 0;
-       }
 
        return 0;
 }
@@ -178,9 +176,7 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
 
        /* is port used by another stream */
        if (!(SACR0 & SACR0_ENB)) {
-
                SACR0 = 0;
-               SACR1 = 0;
                if (pxa_i2s.master)
                        SACR0 |= SACR0_BCKD;
 
@@ -226,6 +222,10 @@ static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       SACR1 &= ~SACR1_DRPL;
+               else
+                       SACR1 &= ~SACR1_DREC;
                SACR0 |= SACR0_ENB;
                break;
        case SNDRV_PCM_TRIGGER_RESUME:
@@ -252,21 +252,16 @@ static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream,
                SAIMR &= ~SAIMR_RFS;
        }
 
-       if (SACR1 & (SACR1_DREC | SACR1_DRPL)) {
+       if ((SACR1 & (SACR1_DREC | SACR1_DRPL)) == (SACR1_DREC | SACR1_DRPL)) {
                SACR0 &= ~SACR0_ENB;
                pxa_i2s_wait();
                clk_disable(clk_i2s);
        }
-
-       clk_put(clk_i2s);
 }
 
 #ifdef CONFIG_PM
 static int pxa2xx_i2s_suspend(struct snd_soc_dai *dai)
 {
-       if (!dai->active)
-               return 0;
-
        /* store registers */
        pxa_i2s.sacr0 = SACR0;
        pxa_i2s.sacr1 = SACR1;
@@ -281,16 +276,14 @@ static int pxa2xx_i2s_suspend(struct snd_soc_dai *dai)
 
 static int pxa2xx_i2s_resume(struct snd_soc_dai *dai)
 {
-       if (!dai->active)
-               return 0;
-
        pxa_i2s_wait();
 
-       SACR0 = pxa_i2s.sacr0 &= ~SACR0_ENB;
+       SACR0 = pxa_i2s.sacr0 & ~SACR0_ENB;
        SACR1 = pxa_i2s.sacr1;
        SAIMR = pxa_i2s.saimr;
        SADIV = pxa_i2s.sadiv;
-       SACR0 |= SACR0_ENB;
+
+       SACR0 = pxa_i2s.sacr0;
 
        return 0;
 }
@@ -329,6 +322,7 @@ struct snd_soc_dai pxa_i2s_dai = {
                .rates = PXA2XX_I2S_RATES,
                .formats = SNDRV_PCM_FMTBIT_S16_LE,},
        .ops = &pxa_i2s_dai_ops,
+       .symmetric_rates = 1,
 };
 
 EXPORT_SYMBOL_GPL(pxa_i2s_dai);
@@ -346,6 +340,19 @@ static int pxa2xx_i2s_probe(struct platform_device *dev)
        if (ret != 0)
                clk_put(clk_i2s);
 
+       /*
+        * PXA Developer's Manual:
+        * If SACR0[ENB] is toggled in the middle of a normal operation,
+        * the SACR0[RST] bit must also be set and cleared to reset all
+        * I2S controller registers.
+        */
+       SACR0 = SACR0_RST;
+       SACR0 = 0;
+       /* Make sure RPL and REC are disabled */
+       SACR1 = SACR1_DRPL | SACR1_DREC;
+       /* Along with FIFO servicing */
+       SAIMR &= ~(SAIMR_RFS | SAIMR_TFS);
+
        return ret;
 }
 
index 289fadf60b1038a24585f3c4bf7e65f8046a8b46..906709e6dd5f36182d2ec3ef7db497747d05c627 100644 (file)
@@ -345,9 +345,11 @@ static void lm4857_write_regs(void)
 static int lm4857_get_reg(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       int reg = kcontrol->private_value & 0xFF;
-       int shift = (kcontrol->private_value >> 8) & 0x0F;
-       int mask = (kcontrol->private_value >> 16) & 0xFF;
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       int reg = mc->reg;
+       int shift = mc->shift;
+       int mask = mc->max;
 
        pr_debug("Entered %s\n", __func__);
 
@@ -358,9 +360,11 @@ static int lm4857_get_reg(struct snd_kcontrol *kcontrol,
 static int lm4857_set_reg(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       int reg = kcontrol->private_value & 0xFF;
-       int shift = (kcontrol->private_value >> 8) & 0x0F;
-       int mask = (kcontrol->private_value >> 16) & 0xFF;
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       int reg = mc->reg;
+       int shift = mc->shift;
+       int mask = mc->max;
 
        if (((lm4857_regs[reg] >> shift) & mask) ==
                ucontrol->value.integer.value[0])
index ab680aac3fcb0edb064e6bad6ee2f03c7b03f707..1a283170ca92d80e00394f3cbc7cb6d01ebafc98 100644 (file)
 
 #include "s3c-i2s-v2.h"
 
+#undef S3C_IIS_V2_SUPPORTED
+
+#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413)
+#define S3C_IIS_V2_SUPPORTED
+#endif
+
+#ifdef CONFIG_PLAT_S3C64XX
+#define S3C_IIS_V2_SUPPORTED
+#endif
+
+#ifndef S3C_IIS_V2_SUPPORTED
+#error Unsupported CPU model
+#endif
+
 #define S3C2412_I2S_DEBUG_CON 0
 
 static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai)
@@ -75,7 +89,7 @@ static inline void dbg_showcon(const char *fn, u32 con)
 
 
 /* Turn on or off the transmission path. */
-void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on)
+static void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on)
 {
        void __iomem *regs = i2s->regs;
        u32 fic, con, mod;
@@ -105,7 +119,9 @@ void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on)
                        break;
 
                default:
-                       dev_err(i2s->dev, "TXEN: Invalid MODE in IISMOD\n");
+                       dev_err(i2s->dev, "TXEN: Invalid MODE %x in IISMOD\n",
+                               mod & S3C2412_IISMOD_MODE_MASK);
+                       break;
                }
 
                writel(con, regs + S3C2412_IISCON);
@@ -132,7 +148,9 @@ void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on)
                        break;
 
                default:
-                       dev_err(i2s->dev, "TXDIS: Invalid MODE in IISMOD\n");
+                       dev_err(i2s->dev, "TXDIS: Invalid MODE %x in IISMOD\n",
+                               mod & S3C2412_IISMOD_MODE_MASK);
+                       break;
                }
 
                writel(mod, regs + S3C2412_IISMOD);
@@ -143,9 +161,8 @@ void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on)
        dbg_showcon(__func__, con);
        pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
 }
-EXPORT_SYMBOL_GPL(s3c2412_snd_txctrl);
 
-void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on)
+static void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on)
 {
        void __iomem *regs = i2s->regs;
        u32 fic, con, mod;
@@ -175,7 +192,8 @@ void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on)
                        break;
 
                default:
-                       dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n");
+                       dev_err(i2s->dev, "RXEN: Invalid MODE %x in IISMOD\n",
+                               mod & S3C2412_IISMOD_MODE_MASK);
                }
 
                writel(mod, regs + S3C2412_IISMOD);
@@ -199,7 +217,8 @@ void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on)
                        break;
 
                default:
-                       dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n");
+                       dev_err(i2s->dev, "RXDIS: Invalid MODE %x in IISMOD\n",
+                               mod & S3C2412_IISMOD_MODE_MASK);
                }
 
                writel(con, regs + S3C2412_IISCON);
@@ -209,7 +228,6 @@ void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on)
        fic = readl(regs + S3C2412_IISFIC);
        pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
 }
-EXPORT_SYMBOL_GPL(s3c2412_snd_rxctrl);
 
 /*
  * Wait for the LR signal to allow synchronisation to the L/R clock
@@ -266,7 +284,7 @@ static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
  */
 #define IISMOD_MASTER_MASK (1 << 11)
 #define IISMOD_SLAVE (1 << 11)
-#define IISMOD_MASTER (0x0)
+#define IISMOD_MASTER (0 << 11)
 #endif
 
        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -281,7 +299,7 @@ static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
                iismod |= IISMOD_MASTER;
                break;
        default:
-               pr_debug("unknwon master/slave format\n");
+               pr_err("unknwon master/slave format\n");
                return -EINVAL;
        }
 
@@ -298,7 +316,7 @@ static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
                iismod |= S3C2412_IISMOD_SDF_IIS;
                break;
        default:
-               pr_debug("Unknown data format\n");
+               pr_err("Unknown data format\n");
                return -EINVAL;
        }
 
@@ -327,6 +345,7 @@ static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
        iismod = readl(i2s->regs + S3C2412_IISMOD);
        pr_debug("%s: r: IISMOD: %x\n", __func__, iismod);
 
+#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413)
        switch (params_format(params)) {
        case SNDRV_PCM_FORMAT_S8:
                iismod |= S3C2412_IISMOD_8BIT;
@@ -335,6 +354,25 @@ static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
                iismod &= ~S3C2412_IISMOD_8BIT;
                break;
        }
+#endif
+
+#ifdef CONFIG_PLAT_S3C64XX
+       iismod &= ~0x606;
+       /* Sample size */
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S8:
+               /* 8 bit sample, 16fs BCLK */
+               iismod |= 0x2004;
+               break;
+       case SNDRV_PCM_FORMAT_S16_LE:
+               /* 16 bit sample, 32fs BCLK */
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               /* 24 bit sample, 48fs BCLK */
+               iismod |= 0x4002;
+               break;
+       }
+#endif
 
        writel(iismod, i2s->regs + S3C2412_IISMOD);
        pr_debug("%s: w: IISMOD: %x\n", __func__, iismod);
@@ -489,6 +527,8 @@ int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info,
        unsigned int best_rate = 0;
        unsigned int best_deviation = INT_MAX;
 
+       pr_debug("Input clock rate %ldHz\n", clkrate);
+
        if (fstab == NULL)
                fstab = iis_fs_tab;
 
@@ -507,7 +547,7 @@ int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info,
                actual = clkrate / (fsdiv * div);
                deviation = actual - rate;
 
-               printk(KERN_DEBUG "%dfs: div %d => result %d, deviation %d\n",
+               printk(KERN_DEBUG "%ufs: div %u => result %u, deviation %d\n",
                       fsdiv, div, actual, deviation);
 
                deviation = abs(deviation);
@@ -523,7 +563,7 @@ int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info,
                        break;
        }
 
-       printk(KERN_DEBUG "best: fs=%d, div=%d, rate=%d\n",
+       printk(KERN_DEBUG "best: fs=%u, div=%u, rate=%u\n",
               best_fs, best_div, best_rate);
 
        info->fs_div = best_fs;
@@ -539,12 +579,31 @@ int s3c_i2sv2_probe(struct platform_device *pdev,
                    unsigned long base)
 {
        struct device *dev = &pdev->dev;
+       unsigned int iismod;
 
        i2s->dev = dev;
 
        /* record our i2s structure for later use in the callbacks */
        dai->private_data = i2s;
 
+       if (!base) {
+               struct resource *res = platform_get_resource(pdev,
+                                                            IORESOURCE_MEM,
+                                                            0);
+               if (!res) {
+                       dev_err(dev, "Unable to get register resource\n");
+                       return -ENXIO;
+               }
+
+               if (!request_mem_region(res->start, resource_size(res),
+                                       "s3c64xx-i2s-v4")) {
+                       dev_err(dev, "Unable to request register region\n");
+                       return -EBUSY;
+               }
+
+               base = res->start;
+       }
+
        i2s->regs = ioremap(base, 0x100);
        if (i2s->regs == NULL) {
                dev_err(dev, "cannot ioremap registers\n");
@@ -560,12 +619,16 @@ int s3c_i2sv2_probe(struct platform_device *pdev,
 
        clk_enable(i2s->iis_pclk);
 
+       /* Mark ourselves as in TXRX mode so we can run through our cleanup
+        * process without warnings. */
+       iismod = readl(i2s->regs + S3C2412_IISMOD);
+       iismod |= S3C2412_IISMOD_MODE_TXRX;
+       writel(iismod, i2s->regs + S3C2412_IISMOD);
        s3c2412_snd_txctrl(i2s, 0);
        s3c2412_snd_rxctrl(i2s, 0);
 
        return 0;
 }
-
 EXPORT_SYMBOL_GPL(s3c_i2sv2_probe);
 
 #ifdef CONFIG_PM
index b7e0b3f0bfc8f5c6dda3cb418742cf035f1a319c..168a088ba761f02c73bd93aaa859b9fefa5a44fe 100644 (file)
@@ -120,7 +120,7 @@ static int s3c2412_i2s_probe(struct platform_device *pdev,
 
        s3c2412_i2s.iis_cclk = clk_get(&pdev->dev, "i2sclk");
        if (s3c2412_i2s.iis_cclk == NULL) {
-               pr_debug("failed to get i2sclk clock\n");
+               pr_err("failed to get i2sclk clock\n");
                iounmap(s3c2412_i2s.regs);
                return -ENODEV;
        }
index 33c5de7e255f3769d00b6de48425085abc48dc56..3c06c401d0fb86ce6a4b98cb55264af36c807b4c 100644 (file)
@@ -108,48 +108,19 @@ static int s3c64xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
        return 0;
 }
 
-
-unsigned long s3c64xx_i2s_get_clockrate(struct snd_soc_dai *dai)
+struct clk *s3c64xx_i2s_get_clock(struct snd_soc_dai *dai)
 {
        struct s3c_i2sv2_info *i2s = to_info(dai);
 
-       return clk_get_rate(i2s->iis_cclk);
+       return i2s->iis_cclk;
 }
-EXPORT_SYMBOL_GPL(s3c64xx_i2s_get_clockrate);
+EXPORT_SYMBOL_GPL(s3c64xx_i2s_get_clock);
 
 static int s3c64xx_i2s_probe(struct platform_device *pdev,
                             struct snd_soc_dai *dai)
 {
-       struct device *dev = &pdev->dev;
-       struct s3c_i2sv2_info *i2s;
-       int ret;
-
-       dev_dbg(dev, "%s: probing dai %d\n", __func__, pdev->id);
-
-       if (pdev->id < 0 || pdev->id > ARRAY_SIZE(s3c64xx_i2s)) {
-               dev_err(dev, "id %d out of range\n", pdev->id);
-               return -EINVAL;
-       }
-
-       i2s = &s3c64xx_i2s[pdev->id];
-
-       ret = s3c_i2sv2_probe(pdev, dai, i2s,
-                             pdev->id ? S3C64XX_PA_IIS1 : S3C64XX_PA_IIS0);
-       if (ret)
-               return ret;
-
-       i2s->dma_capture = &s3c64xx_i2s_pcm_stereo_in[pdev->id];
-       i2s->dma_playback = &s3c64xx_i2s_pcm_stereo_out[pdev->id];
-
-       i2s->iis_cclk = clk_get(dev, "audio-bus");
-       if (IS_ERR(i2s->iis_cclk)) {
-               dev_err(dev, "failed to get audio-bus");
-               iounmap(i2s->regs);
-               return -ENODEV;
-       }
-
        /* configure GPIO for i2s port */
-       switch (pdev->id) {
+       switch (dai->id) {
        case 0:
                s3c_gpio_cfgpin(S3C64XX_GPD(0), S3C64XX_GPD0_I2S0_CLK);
                s3c_gpio_cfgpin(S3C64XX_GPD(1), S3C64XX_GPD1_I2S0_CDCLK);
@@ -175,41 +146,122 @@ static int s3c64xx_i2s_probe(struct platform_device *pdev,
        SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
 
 #define S3C64XX_I2S_FMTS \
-       (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE)
+       (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
+        SNDRV_PCM_FMTBIT_S24_LE)
 
 static struct snd_soc_dai_ops s3c64xx_i2s_dai_ops = {
        .set_sysclk     = s3c64xx_i2s_set_sysclk,       
 };
 
-struct snd_soc_dai s3c64xx_i2s_dai = {
-       .name           = "s3c64xx-i2s",
-       .id             = 0,
-       .probe          = s3c64xx_i2s_probe,
-       .playback = {
-               .channels_min   = 2,
-               .channels_max   = 2,
-               .rates          = S3C64XX_I2S_RATES,
-               .formats        = S3C64XX_I2S_FMTS,
+struct snd_soc_dai s3c64xx_i2s_dai[] = {
+       {
+               .name           = "s3c64xx-i2s",
+               .id             = 0,
+               .probe          = s3c64xx_i2s_probe,
+               .playback = {
+                       .channels_min   = 2,
+                       .channels_max   = 2,
+                       .rates          = S3C64XX_I2S_RATES,
+                       .formats        = S3C64XX_I2S_FMTS,
+               },
+               .capture = {
+                        .channels_min  = 2,
+                        .channels_max  = 2,
+                        .rates         = S3C64XX_I2S_RATES,
+                        .formats       = S3C64XX_I2S_FMTS,
+                },
+               .ops = &s3c64xx_i2s_dai_ops,
+               .symmetric_rates = 1,
        },
-       .capture = {
-               .channels_min   = 2,
-               .channels_max   = 2,
-               .rates          = S3C64XX_I2S_RATES,
-               .formats        = S3C64XX_I2S_FMTS,
+       {
+               .name           = "s3c64xx-i2s",
+               .id             = 1,
+               .probe          = s3c64xx_i2s_probe,
+               .playback = {
+                       .channels_min   = 2,
+                       .channels_max   = 2,
+                       .rates          = S3C64XX_I2S_RATES,
+                       .formats        = S3C64XX_I2S_FMTS,
+               },
+               .capture = {
+                        .channels_min  = 2,
+                        .channels_max  = 2,
+                        .rates         = S3C64XX_I2S_RATES,
+                        .formats       = S3C64XX_I2S_FMTS,
+                },
+               .ops = &s3c64xx_i2s_dai_ops,
+               .symmetric_rates = 1,
        },
-       .ops = &s3c64xx_i2s_dai_ops,
 };
 EXPORT_SYMBOL_GPL(s3c64xx_i2s_dai);
 
+static __devinit int s3c64xx_iis_dev_probe(struct platform_device *pdev)
+{
+       struct s3c_i2sv2_info *i2s;
+       struct snd_soc_dai *dai;
+       int ret;
+
+       if (pdev->id >= ARRAY_SIZE(s3c64xx_i2s)) {
+               dev_err(&pdev->dev, "id %d out of range\n", pdev->id);
+               return -EINVAL;
+       }
+
+       i2s = &s3c64xx_i2s[pdev->id];
+       dai = &s3c64xx_i2s_dai[pdev->id];
+       dai->dev = &pdev->dev;
+
+       i2s->dma_capture = &s3c64xx_i2s_pcm_stereo_in[pdev->id];
+       i2s->dma_playback = &s3c64xx_i2s_pcm_stereo_out[pdev->id];
+
+       i2s->iis_cclk = clk_get(&pdev->dev, "audio-bus");
+       if (IS_ERR(i2s->iis_cclk)) {
+               dev_err(&pdev->dev, "failed to get audio-bus\n");
+               ret = PTR_ERR(i2s->iis_cclk);
+               goto err;
+       }
+
+       ret = s3c_i2sv2_probe(pdev, dai, i2s, 0);
+       if (ret)
+               goto err_clk;
+
+       ret = s3c_i2sv2_register_dai(dai);
+       if (ret != 0)
+               goto err_i2sv2;
+
+       return 0;
+
+err_i2sv2:
+       /* Not implemented for I2Sv2 core yet */
+err_clk:
+       clk_put(i2s->iis_cclk);
+err:
+       return ret;
+}
+
+static __devexit int s3c64xx_iis_dev_remove(struct platform_device *pdev)
+{
+       dev_err(&pdev->dev, "Device removal not yet supported\n");
+       return 0;
+}
+
+static struct platform_driver s3c64xx_iis_driver = {
+       .probe  = s3c64xx_iis_dev_probe,
+       .remove = s3c64xx_iis_dev_remove,
+       .driver = {
+               .name = "s3c64xx-iis",
+               .owner = THIS_MODULE,
+       },
+};
+
 static int __init s3c64xx_i2s_init(void)
 {
-       return  s3c_i2sv2_register_dai(&s3c64xx_i2s_dai);
+       return platform_driver_register(&s3c64xx_iis_driver);
 }
 module_init(s3c64xx_i2s_init);
 
 static void __exit s3c64xx_i2s_exit(void)
 {
-       snd_soc_unregister_dai(&s3c64xx_i2s_dai);
+       platform_driver_unregister(&s3c64xx_iis_driver);
 }
 module_exit(s3c64xx_i2s_exit);
 
@@ -217,6 +269,3 @@ module_exit(s3c64xx_i2s_exit);
 MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
 MODULE_DESCRIPTION("S3C64XX I2S SoC Interface");
 MODULE_LICENSE("GPL");
-
-
-
index b7ffe3c38b664794b0f7f97906950d081c46cc3d..02148cee26139fe1ca4df20df734d9314ca43486 100644 (file)
@@ -15,6 +15,8 @@
 #ifndef __SND_SOC_S3C24XX_S3C64XX_I2S_H
 #define __SND_SOC_S3C24XX_S3C64XX_I2S_H __FILE__
 
+struct clk;
+
 #include "s3c-i2s-v2.h"
 
 #define S3C64XX_DIV_BCLK       S3C_I2SV2_DIV_BCLK
@@ -24,8 +26,8 @@
 #define S3C64XX_CLKSRC_PCLK    (0)
 #define S3C64XX_CLKSRC_MUX     (1)
 
-extern struct snd_soc_dai s3c64xx_i2s_dai;
+extern struct snd_soc_dai s3c64xx_i2s_dai[];
 
-extern unsigned long s3c64xx_i2s_get_clockrate(struct snd_soc_dai *cpu_dai);
+extern struct clk *s3c64xx_i2s_get_clock(struct snd_soc_dai *dai);
 
 #endif /* __SND_SOC_S3C24XX_S3C64XX_I2S_H */
diff --git a/sound/soc/s6000/Kconfig b/sound/soc/s6000/Kconfig
new file mode 100644 (file)
index 0000000..c74eb3d
--- /dev/null
@@ -0,0 +1,19 @@
+config SND_S6000_SOC
+       tristate "SoC Audio for the Stretch s6000 family"
+       depends on XTENSA_VARIANT_S6000
+       help
+         Say Y or M if you want to add support for codecs attached to
+         s6000 family chips. You will also need to select the platform
+         to support below.
+
+config SND_S6000_SOC_I2S
+       tristate
+
+config SND_S6000_SOC_S6IPCAM
+       tristate "SoC Audio support for Stretch 6105 IP Camera"
+       depends on SND_S6000_SOC && XTENSA_PLATFORM_S6105
+       select SND_S6000_SOC_I2S
+       select SND_SOC_TLV320AIC3X
+       help
+         Say Y if you want to add support for SoC audio on the
+         Stretch s6105 IP Camera Reference Design.
diff --git a/sound/soc/s6000/Makefile b/sound/soc/s6000/Makefile
new file mode 100644 (file)
index 0000000..7a61361
--- /dev/null
@@ -0,0 +1,11 @@
+# s6000 Platform Support
+snd-soc-s6000-objs := s6000-pcm.o
+snd-soc-s6000-i2s-objs := s6000-i2s.o
+
+obj-$(CONFIG_SND_S6000_SOC) += snd-soc-s6000.o
+obj-$(CONFIG_SND_S6000_SOC_I2S) += snd-soc-s6000-i2s.o
+
+# s6105 Machine Support
+snd-soc-s6ipcam-objs := s6105-ipcam.o
+
+obj-$(CONFIG_SND_S6000_SOC_S6IPCAM) += snd-soc-s6ipcam.o
diff --git a/sound/soc/s6000/s6000-i2s.c b/sound/soc/s6000/s6000-i2s.c
new file mode 100644 (file)
index 0000000..c5cda18
--- /dev/null
@@ -0,0 +1,629 @@
+/*
+ * ALSA SoC I2S Audio Layer for the Stretch S6000 family
+ *
+ * Author:      Daniel Gloeckner, <dg@emlix.com>
+ * Copyright:   (C) 2009 emlix GmbH <info@emlix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "s6000-i2s.h"
+#include "s6000-pcm.h"
+
+struct s6000_i2s_dev {
+       dma_addr_t sifbase;
+       u8 __iomem *scbbase;
+       unsigned int wide;
+       unsigned int channel_in;
+       unsigned int channel_out;
+       unsigned int lines_in;
+       unsigned int lines_out;
+       struct s6000_pcm_dma_params dma_params;
+};
+
+#define S6_I2S_INTERRUPT_STATUS        0x00
+#define   S6_I2S_INT_OVERRUN   1
+#define   S6_I2S_INT_UNDERRUN  2
+#define   S6_I2S_INT_ALIGNMENT 4
+#define S6_I2S_INTERRUPT_ENABLE        0x04
+#define S6_I2S_INTERRUPT_RAW   0x08
+#define S6_I2S_INTERRUPT_CLEAR 0x0C
+#define S6_I2S_INTERRUPT_SET   0x10
+#define S6_I2S_MODE            0x20
+#define   S6_I2S_DUAL          0
+#define   S6_I2S_WIDE          1
+#define S6_I2S_TX_DEFAULT      0x24
+#define S6_I2S_DATA_CFG(c)     (0x40 + 0x10 * (c))
+#define   S6_I2S_IN            0
+#define   S6_I2S_OUT           1
+#define   S6_I2S_UNUSED                2
+#define S6_I2S_INTERFACE_CFG(c)        (0x44 + 0x10 * (c))
+#define   S6_I2S_DIV_MASK      0x001fff
+#define   S6_I2S_16BIT         0x000000
+#define   S6_I2S_20BIT         0x002000
+#define   S6_I2S_24BIT         0x004000
+#define   S6_I2S_32BIT         0x006000
+#define   S6_I2S_BITS_MASK     0x006000
+#define   S6_I2S_MEM_16BIT     0x000000
+#define   S6_I2S_MEM_32BIT     0x008000
+#define   S6_I2S_MEM_MASK      0x008000
+#define   S6_I2S_CHANNELS_SHIFT        16
+#define   S6_I2S_CHANNELS_MASK 0x030000
+#define   S6_I2S_SCK_IN                0x000000
+#define   S6_I2S_SCK_OUT       0x040000
+#define   S6_I2S_SCK_DIR       0x040000
+#define   S6_I2S_WS_IN         0x000000
+#define   S6_I2S_WS_OUT                0x080000
+#define   S6_I2S_WS_DIR                0x080000
+#define   S6_I2S_LEFT_FIRST    0x000000
+#define   S6_I2S_RIGHT_FIRST   0x100000
+#define   S6_I2S_FIRST         0x100000
+#define   S6_I2S_CUR_SCK       0x200000
+#define   S6_I2S_CUR_WS                0x400000
+#define S6_I2S_ENABLE(c)       (0x48 + 0x10 * (c))
+#define   S6_I2S_DISABLE_IF    0x02
+#define   S6_I2S_ENABLE_IF     0x03
+#define   S6_I2S_IS_BUSY       0x04
+#define   S6_I2S_DMA_ACTIVE    0x08
+#define   S6_I2S_IS_ENABLED    0x10
+
+#define S6_I2S_NUM_LINES       4
+
+#define S6_I2S_SIF_PORT0       0x0000000
+#define S6_I2S_SIF_PORT1       0x0000080 /* docs say 0x0000010 */
+
+static inline void s6_i2s_write_reg(struct s6000_i2s_dev *dev, int reg, u32 val)
+{
+       writel(val, dev->scbbase + reg);
+}
+
+static inline u32 s6_i2s_read_reg(struct s6000_i2s_dev *dev, int reg)
+{
+       return readl(dev->scbbase + reg);
+}
+
+static inline void s6_i2s_mod_reg(struct s6000_i2s_dev *dev, int reg,
+                                 u32 mask, u32 val)
+{
+       val ^= s6_i2s_read_reg(dev, reg) & ~mask;
+       s6_i2s_write_reg(dev, reg, val);
+}
+
+static void s6000_i2s_start_channel(struct s6000_i2s_dev *dev, int channel)
+{
+       int i, j, cur, prev;
+
+       /*
+        * Wait for WCLK to toggle 5 times before enabling the channel
+        * s6000 Family Datasheet 3.6.4:
+        *   "At least two cycles of WS must occur between commands
+        *    to disable or enable the interface"
+        */
+       j = 0;
+       prev = ~S6_I2S_CUR_WS;
+       for (i = 1000000; --i && j < 6; ) {
+               cur = s6_i2s_read_reg(dev, S6_I2S_INTERFACE_CFG(channel))
+                      & S6_I2S_CUR_WS;
+               if (prev != cur) {
+                       prev = cur;
+                       j++;
+               }
+       }
+       if (j < 6)
+               printk(KERN_WARNING "s6000-i2s: timeout waiting for WCLK\n");
+
+       s6_i2s_write_reg(dev, S6_I2S_ENABLE(channel), S6_I2S_ENABLE_IF);
+}
+
+static void s6000_i2s_stop_channel(struct s6000_i2s_dev *dev, int channel)
+{
+       s6_i2s_write_reg(dev, S6_I2S_ENABLE(channel), S6_I2S_DISABLE_IF);
+}
+
+static void s6000_i2s_start(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct s6000_i2s_dev *dev = rtd->dai->cpu_dai->private_data;
+       int channel;
+
+       channel = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+                       dev->channel_out : dev->channel_in;
+
+       s6000_i2s_start_channel(dev, channel);
+}
+
+static void s6000_i2s_stop(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct s6000_i2s_dev *dev = rtd->dai->cpu_dai->private_data;
+       int channel;
+
+       channel = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+                       dev->channel_out : dev->channel_in;
+
+       s6000_i2s_stop_channel(dev, channel);
+}
+
+static int s6000_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+                            int after)
+{
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) ^ !after)
+                       s6000_i2s_start(substream);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (!after)
+                       s6000_i2s_stop(substream);
+       }
+       return 0;
+}
+
+static unsigned int s6000_i2s_int_sources(struct s6000_i2s_dev *dev)
+{
+       unsigned int pending;
+       pending = s6_i2s_read_reg(dev, S6_I2S_INTERRUPT_RAW);
+       pending &= S6_I2S_INT_ALIGNMENT |
+                  S6_I2S_INT_UNDERRUN |
+                  S6_I2S_INT_OVERRUN;
+       s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_CLEAR, pending);
+
+       return pending;
+}
+
+static unsigned int s6000_i2s_check_xrun(struct snd_soc_dai *cpu_dai)
+{
+       struct s6000_i2s_dev *dev = cpu_dai->private_data;
+       unsigned int errors;
+       unsigned int ret;
+
+       errors = s6000_i2s_int_sources(dev);
+       if (likely(!errors))
+               return 0;
+
+       ret = 0;
+       if (errors & S6_I2S_INT_ALIGNMENT)
+               printk(KERN_ERR "s6000-i2s: WCLK misaligned\n");
+       if (errors & S6_I2S_INT_UNDERRUN)
+               ret |= 1 << SNDRV_PCM_STREAM_PLAYBACK;
+       if (errors & S6_I2S_INT_OVERRUN)
+               ret |= 1 << SNDRV_PCM_STREAM_CAPTURE;
+       return ret;
+}
+
+static void s6000_i2s_wait_disabled(struct s6000_i2s_dev *dev)
+{
+       int channel;
+       int n = 50;
+       for (channel = 0; channel < 2; channel++) {
+               while (--n >= 0) {
+                       int v = s6_i2s_read_reg(dev, S6_I2S_ENABLE(channel));
+                       if ((v & S6_I2S_IS_ENABLED)
+                           || !(v & (S6_I2S_DMA_ACTIVE | S6_I2S_IS_BUSY)))
+                               break;
+                       udelay(20);
+               }
+       }
+       if (n < 0)
+               printk(KERN_WARNING "s6000-i2s: timeout disabling interfaces");
+}
+
+static int s6000_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+                                  unsigned int fmt)
+{
+       struct s6000_i2s_dev *dev = cpu_dai->private_data;
+       u32 w;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               w = S6_I2S_SCK_IN | S6_I2S_WS_IN;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFM:
+               w = S6_I2S_SCK_OUT | S6_I2S_WS_IN;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFS:
+               w = S6_I2S_SCK_IN | S6_I2S_WS_OUT;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               w = S6_I2S_SCK_OUT | S6_I2S_WS_OUT;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               w |= S6_I2S_LEFT_FIRST;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               w |= S6_I2S_RIGHT_FIRST;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(0),
+                      S6_I2S_FIRST | S6_I2S_WS_DIR | S6_I2S_SCK_DIR, w);
+       s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(1),
+                      S6_I2S_FIRST | S6_I2S_WS_DIR | S6_I2S_SCK_DIR, w);
+
+       return 0;
+}
+
+static int s6000_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
+{
+       struct s6000_i2s_dev *dev = dai->private_data;
+
+       if (!div || (div & 1) || div > (S6_I2S_DIV_MASK + 1) * 2)
+               return -EINVAL;
+
+       s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(div_id),
+                      S6_I2S_DIV_MASK, div / 2 - 1);
+       return 0;
+}
+
+static int s6000_i2s_hw_params(struct snd_pcm_substream *substream,
+                              struct snd_pcm_hw_params *params,
+                              struct snd_soc_dai *dai)
+{
+       struct s6000_i2s_dev *dev = dai->private_data;
+       int interf;
+       u32 w = 0;
+
+       if (dev->wide)
+               interf = 0;
+       else {
+               w |= (((params_channels(params) - 2) / 2)
+                     << S6_I2S_CHANNELS_SHIFT) & S6_I2S_CHANNELS_MASK;
+               interf = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                               ? dev->channel_out : dev->channel_in;
+       }
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               w |= S6_I2S_16BIT | S6_I2S_MEM_16BIT;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               w |= S6_I2S_32BIT | S6_I2S_MEM_32BIT;
+               break;
+       default:
+               printk(KERN_WARNING "s6000-i2s: unsupported PCM format %x\n",
+                      params_format(params));
+               return -EINVAL;
+       }
+
+       if (s6_i2s_read_reg(dev, S6_I2S_INTERFACE_CFG(interf))
+            & S6_I2S_IS_ENABLED) {
+               printk(KERN_ERR "s6000-i2s: interface already enabled\n");
+               return -EBUSY;
+       }
+
+       s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(interf),
+                      S6_I2S_CHANNELS_MASK|S6_I2S_MEM_MASK|S6_I2S_BITS_MASK,
+                      w);
+
+       return 0;
+}
+
+static int s6000_i2s_dai_probe(struct platform_device *pdev,
+                              struct snd_soc_dai *dai)
+{
+       struct s6000_i2s_dev *dev = dai->private_data;
+       struct s6000_snd_platform_data *pdata = pdev->dev.platform_data;
+
+       if (!pdata)
+               return -EINVAL;
+
+       dev->wide = pdata->wide;
+       dev->channel_in = pdata->channel_in;
+       dev->channel_out = pdata->channel_out;
+       dev->lines_in = pdata->lines_in;
+       dev->lines_out = pdata->lines_out;
+
+       s6_i2s_write_reg(dev, S6_I2S_MODE,
+                        dev->wide ? S6_I2S_WIDE : S6_I2S_DUAL);
+
+       if (dev->wide) {
+               int i;
+
+               if (dev->lines_in + dev->lines_out > S6_I2S_NUM_LINES)
+                       return -EINVAL;
+
+               dev->channel_in = 0;
+               dev->channel_out = 1;
+               dai->capture.channels_min = 2 * dev->lines_in;
+               dai->capture.channels_max = dai->capture.channels_min;
+               dai->playback.channels_min = 2 * dev->lines_out;
+               dai->playback.channels_max = dai->playback.channels_min;
+
+               for (i = 0; i < dev->lines_out; i++)
+                       s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), S6_I2S_OUT);
+
+               for (; i < S6_I2S_NUM_LINES - dev->lines_in; i++)
+                       s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i),
+                                        S6_I2S_UNUSED);
+
+               for (; i < S6_I2S_NUM_LINES; i++)
+                       s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), S6_I2S_IN);
+       } else {
+               unsigned int cfg[2] = {S6_I2S_UNUSED, S6_I2S_UNUSED};
+
+               if (dev->lines_in > 1 || dev->lines_out > 1)
+                       return -EINVAL;
+
+               dai->capture.channels_min = 2 * dev->lines_in;
+               dai->capture.channels_max = 8 * dev->lines_in;
+               dai->playback.channels_min = 2 * dev->lines_out;
+               dai->playback.channels_max = 8 * dev->lines_out;
+
+               if (dev->lines_in)
+                       cfg[dev->channel_in] = S6_I2S_IN;
+               if (dev->lines_out)
+                       cfg[dev->channel_out] = S6_I2S_OUT;
+
+               s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(0), cfg[0]);
+               s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(1), cfg[1]);
+       }
+
+       if (dev->lines_out) {
+               if (dev->lines_in) {
+                       if (!dev->dma_params.dma_out)
+                               return -ENODEV;
+               } else {
+                       dev->dma_params.dma_out = dev->dma_params.dma_in;
+                       dev->dma_params.dma_in = 0;
+               }
+       }
+       dev->dma_params.sif_in = dev->sifbase + (dev->channel_in ?
+                                       S6_I2S_SIF_PORT1 : S6_I2S_SIF_PORT0);
+       dev->dma_params.sif_out = dev->sifbase + (dev->channel_out ?
+                                       S6_I2S_SIF_PORT1 : S6_I2S_SIF_PORT0);
+       dev->dma_params.same_rate = pdata->same_rate | pdata->wide;
+       return 0;
+}
+
+#define S6000_I2S_RATES        (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_5512 | \
+                        SNDRV_PCM_RATE_8000_192000)
+#define S6000_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops s6000_i2s_dai_ops = {
+       .set_fmt = s6000_i2s_set_dai_fmt,
+       .set_clkdiv = s6000_i2s_set_clkdiv,
+       .hw_params = s6000_i2s_hw_params,
+};
+
+struct snd_soc_dai s6000_i2s_dai = {
+       .name = "s6000-i2s",
+       .id = 0,
+       .probe = s6000_i2s_dai_probe,
+       .playback = {
+               .channels_min = 2,
+               .channels_max = 8,
+               .formats = S6000_I2S_FORMATS,
+               .rates = S6000_I2S_RATES,
+               .rate_min = 0,
+               .rate_max = 1562500,
+       },
+       .capture = {
+               .channels_min = 2,
+               .channels_max = 8,
+               .formats = S6000_I2S_FORMATS,
+               .rates = S6000_I2S_RATES,
+               .rate_min = 0,
+               .rate_max = 1562500,
+       },
+       .ops = &s6000_i2s_dai_ops,
+}
+EXPORT_SYMBOL_GPL(s6000_i2s_dai);
+
+static int __devinit s6000_i2s_probe(struct platform_device *pdev)
+{
+       struct s6000_i2s_dev *dev;
+       struct resource *scbmem, *sifmem, *region, *dma1, *dma2;
+       u8 __iomem *mmio;
+       int ret;
+
+       scbmem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!scbmem) {
+               dev_err(&pdev->dev, "no mem resource?\n");
+               ret = -ENODEV;
+               goto err_release_none;
+       }
+
+       region = request_mem_region(scbmem->start,
+                                   scbmem->end - scbmem->start + 1,
+                                   pdev->name);
+       if (!region) {
+               dev_err(&pdev->dev, "I2S SCB region already claimed\n");
+               ret = -EBUSY;
+               goto err_release_none;
+       }
+
+       mmio = ioremap(scbmem->start, scbmem->end - scbmem->start + 1);
+       if (!mmio) {
+               dev_err(&pdev->dev, "can't ioremap SCB region\n");
+               ret = -ENOMEM;
+               goto err_release_scb;
+       }
+
+       sifmem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (!sifmem) {
+               dev_err(&pdev->dev, "no second mem resource?\n");
+               ret = -ENODEV;
+               goto err_release_map;
+       }
+
+       region = request_mem_region(sifmem->start,
+                                   sifmem->end - sifmem->start + 1,
+                                   pdev->name);
+       if (!region) {
+               dev_err(&pdev->dev, "I2S SIF region already claimed\n");
+               ret = -EBUSY;
+               goto err_release_map;
+       }
+
+       dma1 = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+       if (!dma1) {
+               dev_err(&pdev->dev, "no dma resource?\n");
+               ret = -ENODEV;
+               goto err_release_sif;
+       }
+
+       region = request_mem_region(dma1->start, dma1->end - dma1->start + 1,
+                                   pdev->name);
+       if (!region) {
+               dev_err(&pdev->dev, "I2S DMA region already claimed\n");
+               ret = -EBUSY;
+               goto err_release_sif;
+       }
+
+       dma2 = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+       if (dma2) {
+               region = request_mem_region(dma2->start,
+                                           dma2->end - dma2->start + 1,
+                                           pdev->name);
+               if (!region) {
+                       dev_err(&pdev->dev,
+                               "I2S DMA region already claimed\n");
+                       ret = -EBUSY;
+                       goto err_release_dma1;
+               }
+       }
+
+       dev = kzalloc(sizeof(struct s6000_i2s_dev), GFP_KERNEL);
+       if (!dev) {
+               ret = -ENOMEM;
+               goto err_release_dma2;
+       }
+
+       s6000_i2s_dai.dev = &pdev->dev;
+       s6000_i2s_dai.private_data = dev;
+       s6000_i2s_dai.dma_data = &dev->dma_params;
+
+       dev->sifbase = sifmem->start;
+       dev->scbbase = mmio;
+
+       s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE, 0);
+       s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_CLEAR,
+                        S6_I2S_INT_ALIGNMENT |
+                        S6_I2S_INT_UNDERRUN |
+                        S6_I2S_INT_OVERRUN);
+
+       s6000_i2s_stop_channel(dev, 0);
+       s6000_i2s_stop_channel(dev, 1);
+       s6000_i2s_wait_disabled(dev);
+
+       dev->dma_params.check_xrun = s6000_i2s_check_xrun;
+       dev->dma_params.trigger = s6000_i2s_trigger;
+       dev->dma_params.dma_in = dma1->start;
+       dev->dma_params.dma_out = dma2 ? dma2->start : 0;
+       dev->dma_params.irq = platform_get_irq(pdev, 0);
+       if (dev->dma_params.irq < 0) {
+               dev_err(&pdev->dev, "no irq resource?\n");
+               ret = -ENODEV;
+               goto err_release_dev;
+       }
+
+       s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE,
+                        S6_I2S_INT_ALIGNMENT |
+                        S6_I2S_INT_UNDERRUN |
+                        S6_I2S_INT_OVERRUN);
+
+       ret = snd_soc_register_dai(&s6000_i2s_dai);
+       if (ret)
+               goto err_release_dev;
+
+       return 0;
+
+err_release_dev:
+       kfree(dev);
+err_release_dma2:
+       if (dma2)
+               release_mem_region(dma2->start, dma2->end - dma2->start + 1);
+err_release_dma1:
+       release_mem_region(dma1->start, dma1->end - dma1->start + 1);
+err_release_sif:
+       release_mem_region(sifmem->start, (sifmem->end - sifmem->start) + 1);
+err_release_map:
+       iounmap(mmio);
+err_release_scb:
+       release_mem_region(scbmem->start, (scbmem->end - scbmem->start) + 1);
+err_release_none:
+       return ret;
+}
+
+static void __devexit s6000_i2s_remove(struct platform_device *pdev)
+{
+       struct s6000_i2s_dev *dev = s6000_i2s_dai.private_data;
+       struct resource *region;
+       void __iomem *mmio = dev->scbbase;
+
+       snd_soc_unregister_dai(&s6000_i2s_dai);
+
+       s6000_i2s_stop_channel(dev, 0);
+       s6000_i2s_stop_channel(dev, 1);
+
+       s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE, 0);
+       s6000_i2s_dai.private_data = 0;
+       kfree(dev);
+
+       region = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+       release_mem_region(region->start, region->end - region->start + 1);
+
+       region = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+       if (region)
+               release_mem_region(region->start,
+                                  region->end - region->start + 1);
+
+       region = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(region->start, (region->end - region->start) + 1);
+
+       iounmap(mmio);
+       region = platform_get_resource(pdev, IORESOURCE_IO, 0);
+       release_mem_region(region->start, (region->end - region->start) + 1);
+}
+
+static struct platform_driver s6000_i2s_driver = {
+       .probe  = s6000_i2s_probe,
+       .remove = __devexit_p(s6000_i2s_remove),
+       .driver = {
+               .name   = "s6000-i2s",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init s6000_i2s_init(void)
+{
+       return platform_driver_register(&s6000_i2s_driver);
+}
+module_init(s6000_i2s_init);
+
+static void __exit s6000_i2s_exit(void)
+{
+       platform_driver_unregister(&s6000_i2s_driver);
+}
+module_exit(s6000_i2s_exit);
+
+MODULE_AUTHOR("Daniel Gloeckner");
+MODULE_DESCRIPTION("Stretch s6000 family I2S SoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s6000/s6000-i2s.h b/sound/soc/s6000/s6000-i2s.h
new file mode 100644 (file)
index 0000000..2375fdf
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * ALSA SoC I2S Audio Layer for the Stretch s6000 family
+ *
+ * Author:      Daniel Gloeckner, <dg@emlix.com>
+ * Copyright:   (C) 2009 emlix GmbH <info@emlix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _S6000_I2S_H
+#define _S6000_I2S_H
+
+extern struct snd_soc_dai s6000_i2s_dai;
+
+struct s6000_snd_platform_data {
+       int lines_in;
+       int lines_out;
+       int channel_in;
+       int channel_out;
+       int wide;
+       int same_rate;
+};
+#endif
diff --git a/sound/soc/s6000/s6000-pcm.c b/sound/soc/s6000/s6000-pcm.c
new file mode 100644 (file)
index 0000000..83b8028
--- /dev/null
@@ -0,0 +1,497 @@
+/*
+ * ALSA PCM interface for the Stetch s6000 family
+ *
+ * Author:      Daniel Gloeckner, <dg@emlix.com>
+ * Copyright:   (C) 2009 emlix GmbH <info@emlix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/dma.h>
+#include <variant/dmac.h>
+
+#include "s6000-pcm.h"
+
+#define S6_PCM_PREALLOCATE_SIZE (96 * 1024)
+#define S6_PCM_PREALLOCATE_MAX  (2048 * 1024)
+
+static struct snd_pcm_hardware s6000_pcm_hardware = {
+       .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+                SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_JOINT_DUPLEX),
+       .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE),
+       .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_5512 | \
+                 SNDRV_PCM_RATE_8000_192000),
+       .rate_min = 0,
+       .rate_max = 1562500,
+       .channels_min = 2,
+       .channels_max = 8,
+       .buffer_bytes_max = 0x7ffffff0,
+       .period_bytes_min = 16,
+       .period_bytes_max = 0xfffff0,
+       .periods_min = 2,
+       .periods_max = 1024, /* no limit */
+       .fifo_size = 0,
+};
+
+struct s6000_runtime_data {
+       spinlock_t lock;
+       int period;             /* current DMA period */
+};
+
+static void s6000_pcm_enqueue_dma(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct s6000_runtime_data *prtd = runtime->private_data;
+       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+       struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
+       int channel;
+       unsigned int period_size;
+       unsigned int dma_offset;
+       dma_addr_t dma_pos;
+       dma_addr_t src, dst;
+
+       period_size = snd_pcm_lib_period_bytes(substream);
+       dma_offset = prtd->period * period_size;
+       dma_pos = runtime->dma_addr + dma_offset;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               src = dma_pos;
+               dst = par->sif_out;
+               channel = par->dma_out;
+       } else {
+               src = par->sif_in;
+               dst = dma_pos;
+               channel = par->dma_in;
+       }
+
+       if (!s6dmac_channel_enabled(DMA_MASK_DMAC(channel),
+                                   DMA_INDEX_CHNL(channel)))
+               return;
+
+       if (s6dmac_fifo_full(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel))) {
+               printk(KERN_ERR "s6000-pcm: fifo full\n");
+               return;
+       }
+
+       BUG_ON(period_size & 15);
+       s6dmac_put_fifo(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel),
+                       src, dst, period_size);
+
+       prtd->period++;
+       if (unlikely(prtd->period >= runtime->periods))
+               prtd->period = 0;
+}
+
+static irqreturn_t s6000_pcm_irq(int irq, void *data)
+{
+       struct snd_pcm *pcm = data;
+       struct snd_soc_pcm_runtime *runtime = pcm->private_data;
+       struct s6000_pcm_dma_params *params = runtime->dai->cpu_dai->dma_data;
+       struct s6000_runtime_data *prtd;
+       unsigned int has_xrun;
+       int i, ret = IRQ_NONE;
+       u32 channel[2] = {
+               [SNDRV_PCM_STREAM_PLAYBACK] = params->dma_out,
+               [SNDRV_PCM_STREAM_CAPTURE] = params->dma_in
+       };
+
+       has_xrun = params->check_xrun(runtime->dai->cpu_dai);
+
+       for (i = 0; i < ARRAY_SIZE(channel); ++i) {
+               struct snd_pcm_substream *substream = pcm->streams[i].substream;
+               unsigned int pending;
+
+               if (!channel[i])
+                       continue;
+
+               if (unlikely(has_xrun & (1 << i)) &&
+                   substream->runtime &&
+                   snd_pcm_running(substream)) {
+                       dev_dbg(pcm->dev, "xrun\n");
+                       snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+                       ret = IRQ_HANDLED;
+               }
+
+               pending = s6dmac_int_sources(DMA_MASK_DMAC(channel[i]),
+                                            DMA_INDEX_CHNL(channel[i]));
+
+               if (pending & 1) {
+                       ret = IRQ_HANDLED;
+                       if (likely(substream->runtime &&
+                                  snd_pcm_running(substream))) {
+                               snd_pcm_period_elapsed(substream);
+                               dev_dbg(pcm->dev, "period elapsed %x %x\n",
+                                      s6dmac_cur_src(DMA_MASK_DMAC(channel[i]),
+                                                  DMA_INDEX_CHNL(channel[i])),
+                                      s6dmac_cur_dst(DMA_MASK_DMAC(channel[i]),
+                                                  DMA_INDEX_CHNL(channel[i])));
+                               prtd = substream->runtime->private_data;
+                               spin_lock(&prtd->lock);
+                               s6000_pcm_enqueue_dma(substream);
+                               spin_unlock(&prtd->lock);
+                       }
+               }
+
+               if (unlikely(pending & ~7)) {
+                       if (pending & (1 << 3))
+                               printk(KERN_WARNING
+                                      "s6000-pcm: DMA %x Underflow\n",
+                                      channel[i]);
+                       if (pending & (1 << 4))
+                               printk(KERN_WARNING
+                                      "s6000-pcm: DMA %x Overflow\n",
+                                      channel[i]);
+                       if (pending & 0x1e0)
+                               printk(KERN_WARNING
+                                      "s6000-pcm: DMA %x Master Error "
+                                      "(mask %x)\n",
+                                      channel[i], pending >> 5);
+
+               }
+       }
+
+       return ret;
+}
+
+static int s6000_pcm_start(struct snd_pcm_substream *substream)
+{
+       struct s6000_runtime_data *prtd = substream->runtime->private_data;
+       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+       struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
+       unsigned long flags;
+       int srcinc;
+       u32 dma;
+
+       spin_lock_irqsave(&prtd->lock, flags);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               srcinc = 1;
+               dma = par->dma_out;
+       } else {
+               srcinc = 0;
+               dma = par->dma_in;
+       }
+       s6dmac_enable_chan(DMA_MASK_DMAC(dma), DMA_INDEX_CHNL(dma),
+                          1 /* priority 1 (0 is max) */,
+                          0 /* peripheral requests w/o xfer length mode */,
+                          srcinc /* source address increment */,
+                          srcinc^1 /* destination address increment */,
+                          0 /* chunksize 0 (skip impossible on this dma) */,
+                          0 /* source skip after chunk (impossible) */,
+                          0 /* destination skip after chunk (impossible) */,
+                          4 /* 16 byte burst size */,
+                          -1 /* don't conserve bandwidth */,
+                          0 /* low watermark irq descriptor theshold */,
+                          0 /* disable hardware timestamps */,
+                          1 /* enable channel */);
+
+       s6000_pcm_enqueue_dma(substream);
+       s6000_pcm_enqueue_dma(substream);
+
+       spin_unlock_irqrestore(&prtd->lock, flags);
+
+       return 0;
+}
+
+static int s6000_pcm_stop(struct snd_pcm_substream *substream)
+{
+       struct s6000_runtime_data *prtd = substream->runtime->private_data;
+       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+       struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
+       unsigned long flags;
+       u32 channel;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               channel = par->dma_out;
+       else
+               channel = par->dma_in;
+
+       s6dmac_set_terminal_count(DMA_MASK_DMAC(channel),
+                                 DMA_INDEX_CHNL(channel), 0);
+
+       spin_lock_irqsave(&prtd->lock, flags);
+
+       s6dmac_disable_chan(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel));
+
+       spin_unlock_irqrestore(&prtd->lock, flags);
+
+       return 0;
+}
+
+static int s6000_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+       struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
+       int ret;
+
+       ret = par->trigger(substream, cmd, 0);
+       if (ret < 0)
+               return ret;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               ret = s6000_pcm_start(substream);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               ret = s6000_pcm_stop(substream);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       if (ret < 0)
+               return ret;
+
+       return par->trigger(substream, cmd, 1);
+}
+
+static int s6000_pcm_prepare(struct snd_pcm_substream *substream)
+{
+       struct s6000_runtime_data *prtd = substream->runtime->private_data;
+
+       prtd->period = 0;
+
+       return 0;
+}
+
+static snd_pcm_uframes_t s6000_pcm_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+       struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct s6000_runtime_data *prtd = runtime->private_data;
+       unsigned long flags;
+       unsigned int offset;
+       dma_addr_t count;
+
+       spin_lock_irqsave(&prtd->lock, flags);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               count = s6dmac_cur_src(DMA_MASK_DMAC(par->dma_out),
+                                      DMA_INDEX_CHNL(par->dma_out));
+       else
+               count = s6dmac_cur_dst(DMA_MASK_DMAC(par->dma_in),
+                                      DMA_INDEX_CHNL(par->dma_in));
+
+       count -= runtime->dma_addr;
+
+       spin_unlock_irqrestore(&prtd->lock, flags);
+
+       offset = bytes_to_frames(runtime, count);
+       if (unlikely(offset >= runtime->buffer_size))
+               offset = 0;
+
+       return offset;
+}
+
+static int s6000_pcm_open(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+       struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct s6000_runtime_data *prtd;
+       int ret;
+
+       snd_soc_set_runtime_hwparams(substream, &s6000_pcm_hardware);
+
+       ret = snd_pcm_hw_constraint_step(runtime, 0,
+                                        SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 16);
+       if (ret < 0)
+               return ret;
+       ret = snd_pcm_hw_constraint_step(runtime, 0,
+                                        SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
+       if (ret < 0)
+               return ret;
+       ret = snd_pcm_hw_constraint_integer(runtime,
+                                           SNDRV_PCM_HW_PARAM_PERIODS);
+       if (ret < 0)
+               return ret;
+
+       if (par->same_rate) {
+               int rate;
+               spin_lock(&par->lock); /* needed? */
+               rate = par->rate;
+               spin_unlock(&par->lock);
+               if (rate != -1) {
+                       ret = snd_pcm_hw_constraint_minmax(runtime,
+                                                       SNDRV_PCM_HW_PARAM_RATE,
+                                                       rate, rate);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+
+       prtd = kzalloc(sizeof(struct s6000_runtime_data), GFP_KERNEL);
+       if (prtd == NULL)
+               return -ENOMEM;
+
+       spin_lock_init(&prtd->lock);
+
+       runtime->private_data = prtd;
+
+       return 0;
+}
+
+static int s6000_pcm_close(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct s6000_runtime_data *prtd = runtime->private_data;
+
+       kfree(prtd);
+
+       return 0;
+}
+
+static int s6000_pcm_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+       struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
+       int ret;
+       ret = snd_pcm_lib_malloc_pages(substream,
+                                      params_buffer_bytes(hw_params));
+       if (ret < 0) {
+               printk(KERN_WARNING "s6000-pcm: allocation of memory failed\n");
+               return ret;
+       }
+
+       if (par->same_rate) {
+               spin_lock(&par->lock);
+               if (par->rate == -1 ||
+                   !(par->in_use & ~(1 << substream->stream))) {
+                       par->rate = params_rate(hw_params);
+                       par->in_use |= 1 << substream->stream;
+               } else if (params_rate(hw_params) != par->rate) {
+                       snd_pcm_lib_free_pages(substream);
+                       par->in_use &= ~(1 << substream->stream);
+                       ret = -EBUSY;
+               }
+               spin_unlock(&par->lock);
+       }
+       return ret;
+}
+
+static int s6000_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+       struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
+
+       spin_lock(&par->lock);
+       par->in_use &= ~(1 << substream->stream);
+       if (!par->in_use)
+               par->rate = -1;
+       spin_unlock(&par->lock);
+
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static struct snd_pcm_ops s6000_pcm_ops = {
+       .open =         s6000_pcm_open,
+       .close =        s6000_pcm_close,
+       .ioctl =        snd_pcm_lib_ioctl,
+       .hw_params =    s6000_pcm_hw_params,
+       .hw_free =      s6000_pcm_hw_free,
+       .trigger =      s6000_pcm_trigger,
+       .prepare =      s6000_pcm_prepare,
+       .pointer =      s6000_pcm_pointer,
+};
+
+static void s6000_pcm_free(struct snd_pcm *pcm)
+{
+       struct snd_soc_pcm_runtime *runtime = pcm->private_data;
+       struct s6000_pcm_dma_params *params = runtime->dai->cpu_dai->dma_data;
+
+       free_irq(params->irq, pcm);
+       snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static u64 s6000_pcm_dmamask = DMA_32BIT_MASK;
+
+static int s6000_pcm_new(struct snd_card *card,
+                        struct snd_soc_dai *dai, struct snd_pcm *pcm)
+{
+       struct snd_soc_pcm_runtime *runtime = pcm->private_data;
+       struct s6000_pcm_dma_params *params = runtime->dai->cpu_dai->dma_data;
+       int res;
+
+       if (!card->dev->dma_mask)
+               card->dev->dma_mask = &s6000_pcm_dmamask;
+       if (!card->dev->coherent_dma_mask)
+               card->dev->coherent_dma_mask = DMA_32BIT_MASK;
+
+       if (params->dma_in) {
+               s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_in),
+                                   DMA_INDEX_CHNL(params->dma_in));
+               s6dmac_int_sources(DMA_MASK_DMAC(params->dma_in),
+                                  DMA_INDEX_CHNL(params->dma_in));
+       }
+
+       if (params->dma_out) {
+               s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_out),
+                                   DMA_INDEX_CHNL(params->dma_out));
+               s6dmac_int_sources(DMA_MASK_DMAC(params->dma_out),
+                                  DMA_INDEX_CHNL(params->dma_out));
+       }
+
+       res = request_irq(params->irq, s6000_pcm_irq, IRQF_SHARED,
+                         s6000_soc_platform.name, pcm);
+       if (res) {
+               printk(KERN_ERR "s6000-pcm couldn't get IRQ\n");
+               return res;
+       }
+
+       res = snd_pcm_lib_preallocate_pages_for_all(pcm,
+                                                   SNDRV_DMA_TYPE_DEV,
+                                                   card->dev,
+                                                   S6_PCM_PREALLOCATE_SIZE,
+                                                   S6_PCM_PREALLOCATE_MAX);
+       if (res)
+               printk(KERN_WARNING "s6000-pcm: preallocation failed\n");
+
+       spin_lock_init(&params->lock);
+       params->in_use = 0;
+       params->rate = -1;
+       return 0;
+}
+
+struct snd_soc_platform s6000_soc_platform = {
+       .name =         "s6000-audio",
+       .pcm_ops =      &s6000_pcm_ops,
+       .pcm_new =      s6000_pcm_new,
+       .pcm_free =     s6000_pcm_free,
+};
+EXPORT_SYMBOL_GPL(s6000_soc_platform);
+
+static int __init s6000_pcm_init(void)
+{
+       return snd_soc_register_platform(&s6000_soc_platform);
+}
+module_init(s6000_pcm_init);
+
+static void __exit s6000_pcm_exit(void)
+{
+       snd_soc_unregister_platform(&s6000_soc_platform);
+}
+module_exit(s6000_pcm_exit);
+
+MODULE_AUTHOR("Daniel Gloeckner");
+MODULE_DESCRIPTION("Stretch s6000 family PCM DMA module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s6000/s6000-pcm.h b/sound/soc/s6000/s6000-pcm.h
new file mode 100644 (file)
index 0000000..96f23f6
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * ALSA PCM interface for the Stretch s6000 family
+ *
+ * Author:      Daniel Gloeckner, <dg@emlix.com>
+ * Copyright:   (C) 2009 emlix GmbH <info@emlix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _S6000_PCM_H
+#define _S6000_PCM_H
+
+struct snd_soc_dai;
+struct snd_pcm_substream;
+
+struct s6000_pcm_dma_params {
+       unsigned int (*check_xrun)(struct snd_soc_dai *cpu_dai);
+       int (*trigger)(struct snd_pcm_substream *substream, int cmd, int after);
+       dma_addr_t sif_in;
+       dma_addr_t sif_out;
+       u32 dma_in;
+       u32 dma_out;
+       int irq;
+       int same_rate;
+
+       spinlock_t lock;
+       int in_use;
+       int rate;
+};
+
+extern struct snd_soc_platform s6000_soc_platform;
+
+#endif
diff --git a/sound/soc/s6000/s6105-ipcam.c b/sound/soc/s6000/s6105-ipcam.c
new file mode 100644 (file)
index 0000000..b5f95f9
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * ASoC driver for Stretch s6105 IP camera platform
+ *
+ * Author:      Daniel Gloeckner, <dg@emlix.com>
+ * Copyright:   (C) 2009 emlix GmbH <info@emlix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <variant/dmac.h>
+
+#include "../codecs/tlv320aic3x.h"
+#include "s6000-pcm.h"
+#include "s6000-i2s.h"
+
+#define S6105_CAM_CODEC_CLOCK 12288000
+
+static int s6105_hw_params(struct snd_pcm_substream *substream,
+                          struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       int ret = 0;
+
+       /* set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+                                            SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0)
+               return ret;
+
+       /* set cpu DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM |
+                                          SND_SOC_DAIFMT_NB_NF);
+       if (ret < 0)
+               return ret;
+
+       /* set the codec system clock */
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, S6105_CAM_CODEC_CLOCK,
+                                           SND_SOC_CLOCK_OUT);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static struct snd_soc_ops s6105_ops = {
+       .hw_params = s6105_hw_params,
+};
+
+/* s6105 machine dapm widgets */
+static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
+       SND_SOC_DAPM_LINE("Audio Out Differential", NULL),
+       SND_SOC_DAPM_LINE("Audio Out Stereo", NULL),
+       SND_SOC_DAPM_LINE("Audio In", NULL),
+};
+
+/* s6105 machine audio_mapnections to the codec pins */
+static const struct snd_soc_dapm_route audio_map[] = {
+       /* Audio Out connected to HPLOUT, HPLCOM, HPROUT */
+       {"Audio Out Differential", NULL, "HPLOUT"},
+       {"Audio Out Differential", NULL, "HPLCOM"},
+       {"Audio Out Stereo", NULL, "HPLOUT"},
+       {"Audio Out Stereo", NULL, "HPROUT"},
+
+       /* Audio In connected to LINE1L, LINE1R */
+       {"LINE1L", NULL, "Audio In"},
+       {"LINE1R", NULL, "Audio In"},
+};
+
+static int output_type_info(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 2;
+       if (uinfo->value.enumerated.item) {
+               uinfo->value.enumerated.item = 1;
+               strcpy(uinfo->value.enumerated.name, "HPLOUT/HPROUT");
+       } else {
+               strcpy(uinfo->value.enumerated.name, "HPLOUT/HPLCOM");
+       }
+       return 0;
+}
+
+static int output_type_get(struct snd_kcontrol *kcontrol,
+                          struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.enumerated.item[0] = kcontrol->private_value;
+       return 0;
+}
+
+static int output_type_put(struct snd_kcontrol *kcontrol,
+                          struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = kcontrol->private_data;
+       unsigned int val = (ucontrol->value.enumerated.item[0] != 0);
+       char *differential = "Audio Out Differential";
+       char *stereo = "Audio Out Stereo";
+
+       if (kcontrol->private_value == val)
+               return 0;
+       kcontrol->private_value = val;
+       snd_soc_dapm_disable_pin(codec, val ? differential : stereo);
+       snd_soc_dapm_sync(codec);
+       snd_soc_dapm_enable_pin(codec, val ? stereo : differential);
+       snd_soc_dapm_sync(codec);
+
+       return 1;
+}
+
+static const struct snd_kcontrol_new audio_out_mux = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Master Output Mux",
+       .index = 0,
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .info = output_type_info,
+       .get = output_type_get,
+       .put = output_type_put,
+       .private_value = 1 /* default to stereo */
+};
+
+/* Logic for a aic3x as connected on the s6105 ip camera ref design */
+static int s6105_aic3x_init(struct snd_soc_codec *codec)
+{
+       /* Add s6105 specific widgets */
+       snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets,
+                                 ARRAY_SIZE(aic3x_dapm_widgets));
+
+       /* Set up s6105 specific audio path audio_map */
+       snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+       /* not present */
+       snd_soc_dapm_nc_pin(codec, "MONO_LOUT");
+       snd_soc_dapm_nc_pin(codec, "LINE2L");
+       snd_soc_dapm_nc_pin(codec, "LINE2R");
+
+       /* not connected */
+       snd_soc_dapm_nc_pin(codec, "MIC3L"); /* LINE2L on this chip */
+       snd_soc_dapm_nc_pin(codec, "MIC3R"); /* LINE2R on this chip */
+       snd_soc_dapm_nc_pin(codec, "LLOUT");
+       snd_soc_dapm_nc_pin(codec, "RLOUT");
+       snd_soc_dapm_nc_pin(codec, "HPRCOM");
+
+       /* always connected */
+       snd_soc_dapm_enable_pin(codec, "Audio In");
+
+       /* must correspond to audio_out_mux.private_value initializer */
+       snd_soc_dapm_disable_pin(codec, "Audio Out Differential");
+       snd_soc_dapm_sync(codec);
+       snd_soc_dapm_enable_pin(codec, "Audio Out Stereo");
+
+       snd_soc_dapm_sync(codec);
+
+       snd_ctl_add(codec->card, snd_ctl_new1(&audio_out_mux, codec));
+
+       return 0;
+}
+
+/* s6105 digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link s6105_dai = {
+       .name = "TLV320AIC31",
+       .stream_name = "AIC31",
+       .cpu_dai = &s6000_i2s_dai,
+       .codec_dai = &aic3x_dai,
+       .init = s6105_aic3x_init,
+       .ops = &s6105_ops,
+};
+
+/* s6105 audio machine driver */
+static struct snd_soc_card snd_soc_card_s6105 = {
+       .name = "Stretch IP Camera",
+       .platform = &s6000_soc_platform,
+       .dai_link = &s6105_dai,
+       .num_links = 1,
+};
+
+/* s6105 audio private data */
+static struct aic3x_setup_data s6105_aic3x_setup = {
+       .i2c_bus = 0,
+       .i2c_address = 0x18,
+};
+
+/* s6105 audio subsystem */
+static struct snd_soc_device s6105_snd_devdata = {
+       .card = &snd_soc_card_s6105,
+       .codec_dev = &soc_codec_dev_aic3x,
+       .codec_data = &s6105_aic3x_setup,
+};
+
+static struct s6000_snd_platform_data __initdata s6105_snd_data = {
+       .wide           = 0,
+       .channel_in     = 0,
+       .channel_out    = 1,
+       .lines_in       = 1,
+       .lines_out      = 1,
+       .same_rate      = 1,
+};
+
+static struct platform_device *s6105_snd_device;
+
+static int __init s6105_init(void)
+{
+       int ret;
+
+       s6105_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!s6105_snd_device)
+               return -ENOMEM;
+
+       platform_set_drvdata(s6105_snd_device, &s6105_snd_devdata);
+       s6105_snd_devdata.dev = &s6105_snd_device->dev;
+       platform_device_add_data(s6105_snd_device, &s6105_snd_data,
+                                sizeof(s6105_snd_data));
+
+       ret = platform_device_add(s6105_snd_device);
+       if (ret)
+               platform_device_put(s6105_snd_device);
+
+       return ret;
+}
+
+static void __exit s6105_exit(void)
+{
+       platform_device_unregister(s6105_snd_device);
+}
+
+module_init(s6105_init);
+module_exit(s6105_exit);
+
+MODULE_AUTHOR("Daniel Gloeckner");
+MODULE_DESCRIPTION("Stretch s6105 IP camera ASoC driver");
+MODULE_LICENSE("GPL");
index 56fa0872abbbb54a2bfd8cfcbb6a579cbd1bc8be..b378096cadb1566e4e6dad55ae94cf7c0f6e9e25 100644 (file)
@@ -145,7 +145,7 @@ static int ssi_hw_params(struct snd_pcm_substream *substream,
        recv = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1;
 
        pr_debug("ssi_hw_params() enter\nssicr was    %08lx\n", ssicr);
-       pr_debug("bits: %d channels: %d\n", bits, channels);
+       pr_debug("bits: %u channels: %u\n", bits, channels);
 
        ssicr &= ~(CR_TRMD | CR_CHNL_MASK | CR_DWL_MASK | CR_PDTA |
                   CR_SWL_MASK);
index 1cd149b9ce69e4060c13c1cae1e75115e24c948b..3f44150d8e30b6a9d8f5920e497627b397c0f049 100644 (file)
@@ -113,6 +113,35 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)
 }
 #endif
 
+static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_card *card = socdev->card;
+       struct snd_soc_dai_link *machine = rtd->dai;
+       struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+       struct snd_soc_dai *codec_dai = machine->codec_dai;
+       int ret;
+
+       if (codec_dai->symmetric_rates || cpu_dai->symmetric_rates ||
+           machine->symmetric_rates) {
+               dev_dbg(card->dev, "Symmetry forces %dHz rate\n", 
+                       machine->rate);
+
+               ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+                                                  SNDRV_PCM_HW_PARAM_RATE,
+                                                  machine->rate,
+                                                  machine->rate);
+               if (ret < 0) {
+                       dev_err(card->dev,
+                               "Unable to apply rate symmetry constraint: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
 /*
  * Called by ALSA when a PCM substream is opened, the runtime->hw record is
  * then initialized and any private data can be allocated. This also calls
@@ -221,6 +250,13 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
                goto machine_err;
        }
 
+       /* Symmetry only applies if we've already got an active stream. */
+       if (cpu_dai->active || codec_dai->active) {
+               ret = soc_pcm_apply_symmetry(substream);
+               if (ret != 0)
+                       goto machine_err;
+       }
+
        pr_debug("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name);
        pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates);
        pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
@@ -263,7 +299,6 @@ static void close_delayed_work(struct work_struct *work)
 {
        struct snd_soc_card *card = container_of(work, struct snd_soc_card,
                                                 delayed_work.work);
-       struct snd_soc_device *socdev = card->socdev;
        struct snd_soc_codec *codec = card->codec;
        struct snd_soc_dai *codec_dai;
        int i;
@@ -279,27 +314,10 @@ static void close_delayed_work(struct work_struct *work)
 
                /* are we waiting on this codec DAI stream */
                if (codec_dai->pop_wait == 1) {
-
-                       /* Reduce power if no longer active */
-                       if (codec->active == 0) {
-                               pr_debug("pop wq D1 %s %s\n", codec->name,
-                                        codec_dai->playback.stream_name);
-                               snd_soc_dapm_set_bias_level(socdev,
-                                       SND_SOC_BIAS_PREPARE);
-                       }
-
                        codec_dai->pop_wait = 0;
                        snd_soc_dapm_stream_event(codec,
                                codec_dai->playback.stream_name,
                                SND_SOC_DAPM_STREAM_STOP);
-
-                       /* Fall into standby if no longer active */
-                       if (codec->active == 0) {
-                               pr_debug("pop wq D3 %s %s\n", codec->name,
-                                        codec_dai->playback.stream_name);
-                               snd_soc_dapm_set_bias_level(socdev,
-                                       SND_SOC_BIAS_STANDBY);
-                       }
                }
        }
        mutex_unlock(&pcm_mutex);
@@ -363,10 +381,6 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
                snd_soc_dapm_stream_event(codec,
                        codec_dai->capture.stream_name,
                        SND_SOC_DAPM_STREAM_STOP);
-
-               if (codec->active == 0 && codec_dai->pop_wait == 0)
-                       snd_soc_dapm_set_bias_level(socdev,
-                                               SND_SOC_BIAS_STANDBY);
        }
 
        mutex_unlock(&pcm_mutex);
@@ -431,36 +445,16 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
                cancel_delayed_work(&card->delayed_work);
        }
 
-       /* do we need to power up codec */
-       if (codec->bias_level != SND_SOC_BIAS_ON) {
-               snd_soc_dapm_set_bias_level(socdev,
-                                           SND_SOC_BIAS_PREPARE);
-
-               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-                       snd_soc_dapm_stream_event(codec,
-                                       codec_dai->playback.stream_name,
-                                       SND_SOC_DAPM_STREAM_START);
-               else
-                       snd_soc_dapm_stream_event(codec,
-                                       codec_dai->capture.stream_name,
-                                       SND_SOC_DAPM_STREAM_START);
-
-               snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_ON);
-               snd_soc_dai_digital_mute(codec_dai, 0);
-
-       } else {
-               /* codec already powered - power on widgets */
-               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-                       snd_soc_dapm_stream_event(codec,
-                                       codec_dai->playback.stream_name,
-                                       SND_SOC_DAPM_STREAM_START);
-               else
-                       snd_soc_dapm_stream_event(codec,
-                                       codec_dai->capture.stream_name,
-                                       SND_SOC_DAPM_STREAM_START);
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               snd_soc_dapm_stream_event(codec,
+                                         codec_dai->playback.stream_name,
+                                         SND_SOC_DAPM_STREAM_START);
+       else
+               snd_soc_dapm_stream_event(codec,
+                                         codec_dai->capture.stream_name,
+                                         SND_SOC_DAPM_STREAM_START);
 
-               snd_soc_dai_digital_mute(codec_dai, 0);
-       }
+       snd_soc_dai_digital_mute(codec_dai, 0);
 
 out:
        mutex_unlock(&pcm_mutex);
@@ -521,6 +515,8 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
                }
        }
 
+       machine->rate = params_rate(params);
+
 out:
        mutex_unlock(&pcm_mutex);
        return ret;
@@ -632,6 +628,12 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
        struct snd_soc_codec *codec = card->codec;
        int i;
 
+       /* If the initialization of this soc device failed, there is no codec
+        * associated with it. Just bail out in this case.
+        */
+       if (!codec)
+               return 0;
+
        /* Due to the resume being scheduled into a workqueue we could
        * suspend before that's finished - wait for it to complete.
         */
@@ -1334,6 +1336,7 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
                return ret;
        }
 
+       codec->socdev = socdev;
        codec->card->dev = socdev->dev;
        codec->card->private_data = codec;
        strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver));
@@ -1744,7 +1747,7 @@ int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol,
 {
        int max = kcontrol->private_value;
 
-       if (max == 1)
+       if (max == 1 && !strstr(kcontrol->id.name, " Volume"))
                uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
        else
                uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -1774,7 +1777,7 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
        unsigned int shift = mc->shift;
        unsigned int rshift = mc->rshift;
 
-       if (max == 1)
+       if (max == 1 && !strstr(kcontrol->id.name, " Volume"))
                uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
        else
                uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -1881,7 +1884,7 @@ int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol,
                (struct soc_mixer_control *)kcontrol->private_value;
        int max = mc->max;
 
-       if (max == 1)
+       if (max == 1 && !strstr(kcontrol->id.name, " Volume"))
                uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
        else
                uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -2065,7 +2068,7 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
 int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
        unsigned int freq, int dir)
 {
-       if (dai->ops->set_sysclk)
+       if (dai->ops && dai->ops->set_sysclk)
                return dai->ops->set_sysclk(dai, clk_id, freq, dir);
        else
                return -EINVAL;
@@ -2085,7 +2088,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
 int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
        int div_id, int div)
 {
-       if (dai->ops->set_clkdiv)
+       if (dai->ops && dai->ops->set_clkdiv)
                return dai->ops->set_clkdiv(dai, div_id, div);
        else
                return -EINVAL;
@@ -2104,7 +2107,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
 int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
        int pll_id, unsigned int freq_in, unsigned int freq_out)
 {
-       if (dai->ops->set_pll)
+       if (dai->ops && dai->ops->set_pll)
                return dai->ops->set_pll(dai, pll_id, freq_in, freq_out);
        else
                return -EINVAL;
@@ -2120,7 +2123,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll);
  */
 int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 {
-       if (dai->ops->set_fmt)
+       if (dai->ops && dai->ops->set_fmt)
                return dai->ops->set_fmt(dai, fmt);
        else
                return -EINVAL;
@@ -2139,7 +2142,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
 int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
        unsigned int mask, int slots)
 {
-       if (dai->ops->set_sysclk)
+       if (dai->ops && dai->ops->set_tdm_slot)
                return dai->ops->set_tdm_slot(dai, mask, slots);
        else
                return -EINVAL;
@@ -2155,7 +2158,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
  */
 int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate)
 {
-       if (dai->ops->set_sysclk)
+       if (dai->ops && dai->ops->set_tristate)
                return dai->ops->set_tristate(dai, tristate);
        else
                return -EINVAL;
@@ -2171,7 +2174,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate);
  */
 int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute)
 {
-       if (dai->ops->digital_mute)
+       if (dai->ops && dai->ops->digital_mute)
                return dai->ops->digital_mute(dai, mute);
        else
                return -EINVAL;
@@ -2352,6 +2355,39 @@ void snd_soc_unregister_platform(struct snd_soc_platform *platform)
 }
 EXPORT_SYMBOL_GPL(snd_soc_unregister_platform);
 
+static u64 codec_format_map[] = {
+       SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE,
+       SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE,
+       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE,
+       SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE,
+       SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE,
+       SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE,
+       SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3BE,
+       SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_U24_3BE,
+       SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE,
+       SNDRV_PCM_FMTBIT_U20_3LE | SNDRV_PCM_FMTBIT_U20_3BE,
+       SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE,
+       SNDRV_PCM_FMTBIT_U18_3LE | SNDRV_PCM_FMTBIT_U18_3BE,
+       SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE,
+       SNDRV_PCM_FMTBIT_FLOAT64_LE | SNDRV_PCM_FMTBIT_FLOAT64_BE,
+       SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE
+       | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE,
+};
+
+/* Fix up the DAI formats for endianness: codecs don't actually see
+ * the endianness of the data but we're using the CPU format
+ * definitions which do need to include endianness so we ensure that
+ * codec DAIs always have both big and little endian variants set.
+ */
+static void fixup_codec_formats(struct snd_soc_pcm_stream *stream)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(codec_format_map); i++)
+               if (stream->formats & codec_format_map[i])
+                       stream->formats |= codec_format_map[i];
+}
+
 /**
  * snd_soc_register_codec - Register a codec with the ASoC core
  *
@@ -2359,6 +2395,8 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_platform);
  */
 int snd_soc_register_codec(struct snd_soc_codec *codec)
 {
+       int i;
+
        if (!codec->name)
                return -EINVAL;
 
@@ -2368,6 +2406,11 @@ int snd_soc_register_codec(struct snd_soc_codec *codec)
 
        INIT_LIST_HEAD(&codec->list);
 
+       for (i = 0; i < codec->num_dai; i++) {
+               fixup_codec_formats(&codec->dai[i].playback);
+               fixup_codec_formats(&codec->dai[i].capture);
+       }
+
        mutex_lock(&client_mutex);
        list_add(&codec->list, &codec_list);
        snd_soc_instantiate_cards();
index 735903a7467500f4d93d750e527a693d67eabaa2..21c69074aa17a1819f7bb4b95255cafa7c57a433 100644 (file)
@@ -12,7 +12,7 @@
  *  Features:
  *    o Changes power status of internal codec blocks depending on the
  *      dynamic configuration of codec internal audio paths and active
- *      DAC's/ADC's.
+ *      DACs/ADCs.
  *    o Platform power domain - can support external components i.e. amps and
  *      mic/meadphone insertion events.
  *    o Automatic Mic Bias support
 
 /* dapm power sequences - make this per codec in the future */
 static int dapm_up_seq[] = {
-       snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic,
-       snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_dac,
-       snd_soc_dapm_mixer, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_pga,
-       snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_post
+       snd_soc_dapm_pre, snd_soc_dapm_supply, snd_soc_dapm_micbias,
+       snd_soc_dapm_mic, snd_soc_dapm_mux, snd_soc_dapm_value_mux,
+       snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_mixer_named_ctl,
+       snd_soc_dapm_pga, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
+       snd_soc_dapm_post
 };
 
 static int dapm_down_seq[] = {
        snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
        snd_soc_dapm_pga, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_mixer,
        snd_soc_dapm_dac, snd_soc_dapm_mic, snd_soc_dapm_micbias,
-       snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_post
+       snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_supply,
+       snd_soc_dapm_post
 };
 
-static int dapm_status = 1;
-module_param(dapm_status, int, 0);
-MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries");
-
 static void pop_wait(u32 pop_time)
 {
        if (pop_time)
@@ -96,6 +94,48 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
        return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
 }
 
+/**
+ * snd_soc_dapm_set_bias_level - set the bias level for the system
+ * @socdev: audio device
+ * @level: level to configure
+ *
+ * Configure the bias (power) levels for the SoC audio device.
+ *
+ * Returns 0 for success else error.
+ */
+static int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
+                                      enum snd_soc_bias_level level)
+{
+       struct snd_soc_card *card = socdev->card;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       int ret = 0;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               dev_dbg(socdev->dev, "Setting full bias\n");
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               dev_dbg(socdev->dev, "Setting bias prepare\n");
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               dev_dbg(socdev->dev, "Setting standby bias\n");
+               break;
+       case SND_SOC_BIAS_OFF:
+               dev_dbg(socdev->dev, "Setting bias off\n");
+               break;
+       default:
+               dev_err(socdev->dev, "Setting invalid bias %d\n", level);
+               return -EINVAL;
+       }
+
+       if (card->set_bias_level)
+               ret = card->set_bias_level(card, level);
+       if (ret == 0 && codec->set_bias_level)
+               ret = codec->set_bias_level(codec, level);
+
+       return ret;
+}
+
 /* set up initial codec paths */
 static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
        struct snd_soc_dapm_path *p, int i)
@@ -165,6 +205,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
        case snd_soc_dapm_dac:
        case snd_soc_dapm_micbias:
        case snd_soc_dapm_vmid:
+       case snd_soc_dapm_supply:
                p->connect = 1;
        break;
        /* does effect routing - dynamically connected */
@@ -179,7 +220,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
        }
 }
 
-/* connect mux widget to it's interconnecting audio paths */
+/* connect mux widget to its interconnecting audio paths */
 static int dapm_connect_mux(struct snd_soc_codec *codec,
        struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
        struct snd_soc_dapm_path *path, const char *control_name,
@@ -202,7 +243,7 @@ static int dapm_connect_mux(struct snd_soc_codec *codec,
        return -ENODEV;
 }
 
-/* connect mixer widget to it's interconnecting audio paths */
+/* connect mixer widget to its interconnecting audio paths */
 static int dapm_connect_mixer(struct snd_soc_codec *codec,
        struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
        struct snd_soc_dapm_path *path, const char *control_name)
@@ -357,8 +398,9 @@ static int dapm_new_mixer(struct snd_soc_codec *codec,
                                path->long_name);
                        ret = snd_ctl_add(codec->card, path->kcontrol);
                        if (ret < 0) {
-                               printk(KERN_ERR "asoc: failed to add dapm kcontrol %s\n",
-                                               path->long_name);
+                               printk(KERN_ERR "asoc: failed to add dapm kcontrol %s: %d\n",
+                                      path->long_name,
+                                      ret);
                                kfree(path->long_name);
                                path->long_name = NULL;
                                return ret;
@@ -434,6 +476,9 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
        struct snd_soc_dapm_path *path;
        int con = 0;
 
+       if (widget->id == snd_soc_dapm_supply)
+               return 0;
+
        if (widget->id == snd_soc_dapm_adc && widget->active)
                return 1;
 
@@ -470,6 +515,9 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
        struct snd_soc_dapm_path *path;
        int con = 0;
 
+       if (widget->id == snd_soc_dapm_supply)
+               return 0;
+
        /* active stream ? */
        if (widget->id == snd_soc_dapm_dac && widget->active)
                return 1;
@@ -521,84 +569,12 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w,
 }
 EXPORT_SYMBOL_GPL(dapm_reg_event);
 
-/*
- * Scan a single DAPM widget for a complete audio path and update the
- * power status appropriately.
+/* Standard power change method, used to apply power changes to most
+ * widgets.
  */
-static int dapm_power_widget(struct snd_soc_codec *codec, int event,
-                            struct snd_soc_dapm_widget *w)
+static int dapm_generic_apply_power(struct snd_soc_dapm_widget *w)
 {
-       int in, out, power_change, power, ret;
-
-       /* vmid - no action */
-       if (w->id == snd_soc_dapm_vmid)
-               return 0;
-
-       /* active ADC */
-       if (w->id == snd_soc_dapm_adc && w->active) {
-               in = is_connected_input_ep(w);
-               dapm_clear_walk(w->codec);
-               w->power = (in != 0) ? 1 : 0;
-               dapm_update_bits(w);
-               return 0;
-       }
-
-       /* active DAC */
-       if (w->id == snd_soc_dapm_dac && w->active) {
-               out = is_connected_output_ep(w);
-               dapm_clear_walk(w->codec);
-               w->power = (out != 0) ? 1 : 0;
-               dapm_update_bits(w);
-               return 0;
-       }
-
-       /* pre and post event widgets */
-       if (w->id == snd_soc_dapm_pre) {
-               if (!w->event)
-                       return 0;
-
-               if (event == SND_SOC_DAPM_STREAM_START) {
-                       ret = w->event(w,
-                                      NULL, SND_SOC_DAPM_PRE_PMU);
-                       if (ret < 0)
-                               return ret;
-               } else if (event == SND_SOC_DAPM_STREAM_STOP) {
-                       ret = w->event(w,
-                                      NULL, SND_SOC_DAPM_PRE_PMD);
-                       if (ret < 0)
-                               return ret;
-               }
-               return 0;
-       }
-       if (w->id == snd_soc_dapm_post) {
-               if (!w->event)
-                       return 0;
-
-               if (event == SND_SOC_DAPM_STREAM_START) {
-                       ret = w->event(w,
-                                      NULL, SND_SOC_DAPM_POST_PMU);
-                       if (ret < 0)
-                               return ret;
-               } else if (event == SND_SOC_DAPM_STREAM_STOP) {
-                       ret = w->event(w,
-                                      NULL, SND_SOC_DAPM_POST_PMD);
-                       if (ret < 0)
-                               return ret;
-               }
-               return 0;
-       }
-
-       /* all other widgets */
-       in = is_connected_input_ep(w);
-       dapm_clear_walk(w->codec);
-       out = is_connected_output_ep(w);
-       dapm_clear_walk(w->codec);
-       power = (out != 0 && in != 0) ? 1 : 0;
-       power_change = (w->power == power) ? 0 : 1;
-       w->power = power;
-
-       if (!power_change)
-               return 0;
+       int ret;
 
        /* call any power change event handlers */
        if (w->event)
@@ -607,7 +583,7 @@ static int dapm_power_widget(struct snd_soc_codec *codec, int event,
                         w->name, w->event_flags);
 
        /* power up pre event */
-       if (power && w->event &&
+       if (w->power && w->event &&
            (w->event_flags & SND_SOC_DAPM_PRE_PMU)) {
                ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU);
                if (ret < 0)
@@ -615,7 +591,7 @@ static int dapm_power_widget(struct snd_soc_codec *codec, int event,
        }
 
        /* power down pre event */
-       if (!power && w->event &&
+       if (!w->power && w->event &&
            (w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
                ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD);
                if (ret < 0)
@@ -623,17 +599,17 @@ static int dapm_power_widget(struct snd_soc_codec *codec, int event,
        }
 
        /* Lower PGA volume to reduce pops */
-       if (w->id == snd_soc_dapm_pga && !power)
-               dapm_set_pga(w, power);
+       if (w->id == snd_soc_dapm_pga && !w->power)
+               dapm_set_pga(w, w->power);
 
        dapm_update_bits(w);
 
        /* Raise PGA volume to reduce pops */
-       if (w->id == snd_soc_dapm_pga && power)
-               dapm_set_pga(w, power);
+       if (w->id == snd_soc_dapm_pga && w->power)
+               dapm_set_pga(w, w->power);
 
        /* power up post event */
-       if (power && w->event &&
+       if (w->power && w->event &&
            (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
                ret = w->event(w,
                               NULL, SND_SOC_DAPM_POST_PMU);
@@ -642,7 +618,7 @@ static int dapm_power_widget(struct snd_soc_codec *codec, int event,
        }
 
        /* power down post event */
-       if (!power && w->event &&
+       if (!w->power && w->event &&
            (w->event_flags & SND_SOC_DAPM_POST_PMD)) {
                ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD);
                if (ret < 0)
@@ -652,6 +628,116 @@ static int dapm_power_widget(struct snd_soc_codec *codec, int event,
        return 0;
 }
 
+/* Generic check to see if a widget should be powered.
+ */
+static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
+{
+       int in, out;
+
+       in = is_connected_input_ep(w);
+       dapm_clear_walk(w->codec);
+       out = is_connected_output_ep(w);
+       dapm_clear_walk(w->codec);
+       return out != 0 && in != 0;
+}
+
+/* Check to see if an ADC has power */
+static int dapm_adc_check_power(struct snd_soc_dapm_widget *w)
+{
+       int in;
+
+       if (w->active) {
+               in = is_connected_input_ep(w);
+               dapm_clear_walk(w->codec);
+               return in != 0;
+       } else {
+               return dapm_generic_check_power(w);
+       }
+}
+
+/* Check to see if a DAC has power */
+static int dapm_dac_check_power(struct snd_soc_dapm_widget *w)
+{
+       int out;
+
+       if (w->active) {
+               out = is_connected_output_ep(w);
+               dapm_clear_walk(w->codec);
+               return out != 0;
+       } else {
+               return dapm_generic_check_power(w);
+       }
+}
+
+/* Check to see if a power supply is needed */
+static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
+{
+       struct snd_soc_dapm_path *path;
+       int power = 0;
+
+       /* Check if one of our outputs is connected */
+       list_for_each_entry(path, &w->sinks, list_source) {
+               if (path->sink && path->sink->power_check &&
+                   path->sink->power_check(path->sink)) {
+                       power = 1;
+                       break;
+               }
+       }
+
+       dapm_clear_walk(w->codec);
+
+       return power;
+}
+
+/*
+ * Scan a single DAPM widget for a complete audio path and update the
+ * power status appropriately.
+ */
+static int dapm_power_widget(struct snd_soc_codec *codec, int event,
+                            struct snd_soc_dapm_widget *w)
+{
+       int ret;
+
+       switch (w->id) {
+       case snd_soc_dapm_pre:
+               if (!w->event)
+                       return 0;
+
+               if (event == SND_SOC_DAPM_STREAM_START) {
+                       ret = w->event(w,
+                                      NULL, SND_SOC_DAPM_PRE_PMU);
+                       if (ret < 0)
+                               return ret;
+               } else if (event == SND_SOC_DAPM_STREAM_STOP) {
+                       ret = w->event(w,
+                                      NULL, SND_SOC_DAPM_PRE_PMD);
+                       if (ret < 0)
+                               return ret;
+               }
+               return 0;
+
+       case snd_soc_dapm_post:
+               if (!w->event)
+                       return 0;
+
+               if (event == SND_SOC_DAPM_STREAM_START) {
+                       ret = w->event(w,
+                                      NULL, SND_SOC_DAPM_POST_PMU);
+                       if (ret < 0)
+                               return ret;
+               } else if (event == SND_SOC_DAPM_STREAM_STOP) {
+                       ret = w->event(w,
+                                      NULL, SND_SOC_DAPM_POST_PMD);
+                       if (ret < 0)
+                               return ret;
+               }
+               return 0;
+
+       default:
+               return dapm_generic_apply_power(w);
+       }
+}
+
 /*
  * Scan each dapm widget for complete audio path.
  * A complete path is a route that has valid endpoints i.e.:-
@@ -663,31 +749,102 @@ static int dapm_power_widget(struct snd_soc_codec *codec, int event,
  */
 static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
 {
+       struct snd_soc_device *socdev = codec->socdev;
        struct snd_soc_dapm_widget *w;
-       int i, c = 1, *seq = NULL, ret = 0;
-
-       /* do we have a sequenced stream event */
-       if (event == SND_SOC_DAPM_STREAM_START) {
-               c = ARRAY_SIZE(dapm_up_seq);
-               seq = dapm_up_seq;
-       } else if (event == SND_SOC_DAPM_STREAM_STOP) {
-               c = ARRAY_SIZE(dapm_down_seq);
-               seq = dapm_down_seq;
+       int ret = 0;
+       int i, power;
+       int sys_power = 0;
+
+       INIT_LIST_HEAD(&codec->up_list);
+       INIT_LIST_HEAD(&codec->down_list);
+
+       /* Check which widgets we need to power and store them in
+        * lists indicating if they should be powered up or down.
+        */
+       list_for_each_entry(w, &codec->dapm_widgets, list) {
+               switch (w->id) {
+               case snd_soc_dapm_pre:
+                       list_add_tail(&codec->down_list, &w->power_list);
+                       break;
+               case snd_soc_dapm_post:
+                       list_add_tail(&codec->up_list, &w->power_list);
+                       break;
+
+               default:
+                       if (!w->power_check)
+                               continue;
+
+                       power = w->power_check(w);
+                       if (power)
+                               sys_power = 1;
+
+                       if (w->power == power)
+                               continue;
+
+                       if (power)
+                               list_add_tail(&w->power_list, &codec->up_list);
+                       else
+                               list_add_tail(&w->power_list,
+                                             &codec->down_list);
+
+                       w->power = power;
+                       break;
+               }
        }
 
-       for (i = 0; i < c; i++) {
-               list_for_each_entry(w, &codec->dapm_widgets, list) {
+       /* If we're changing to all on or all off then prepare */
+       if ((sys_power && codec->bias_level == SND_SOC_BIAS_STANDBY) ||
+           (!sys_power && codec->bias_level == SND_SOC_BIAS_ON)) {
+               ret = snd_soc_dapm_set_bias_level(socdev,
+                                                 SND_SOC_BIAS_PREPARE);
+               if (ret != 0)
+                       pr_err("Failed to prepare bias: %d\n", ret);
+       }
 
+       /* Power down widgets first; try to avoid amplifying pops. */
+       for (i = 0; i < ARRAY_SIZE(dapm_down_seq); i++) {
+               list_for_each_entry(w, &codec->down_list, power_list) {
                        /* is widget in stream order */
-                       if (seq && seq[i] && w->id != seq[i])
+                       if (w->id != dapm_down_seq[i])
                                continue;
 
                        ret = dapm_power_widget(codec, event, w);
                        if (ret != 0)
-                               return ret;
+                               pr_err("Failed to power down %s: %d\n",
+                                      w->name, ret);
                }
        }
 
+       /* Now power up. */
+       for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) {
+               list_for_each_entry(w, &codec->up_list, power_list) {
+                       /* is widget in stream order */
+                       if (w->id != dapm_up_seq[i])
+                               continue;
+
+                       ret = dapm_power_widget(codec, event, w);
+                       if (ret != 0)
+                               pr_err("Failed to power up %s: %d\n",
+                                      w->name, ret);
+               }
+       }
+
+       /* If we just powered the last thing off drop to standby bias */
+       if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) {
+               ret = snd_soc_dapm_set_bias_level(socdev,
+                                                 SND_SOC_BIAS_STANDBY);
+               if (ret != 0)
+                       pr_err("Failed to apply standby bias: %d\n", ret);
+       }
+
+       /* If we just powered up then move to active bias */
+       if (codec->bias_level == SND_SOC_BIAS_PREPARE && sys_power) {
+               ret = snd_soc_dapm_set_bias_level(socdev,
+                                                 SND_SOC_BIAS_ON);
+               if (ret != 0)
+                       pr_err("Failed to apply active bias: %d\n", ret);
+       }
+
        return 0;
 }
 
@@ -723,6 +880,7 @@ static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
                case snd_soc_dapm_pga:
                case snd_soc_dapm_mixer:
                case snd_soc_dapm_mixer_named_ctl:
+               case snd_soc_dapm_supply:
                        if (w->name) {
                                in = is_connected_input_ep(w);
                                dapm_clear_walk(w->codec);
@@ -851,6 +1009,7 @@ static ssize_t dapm_widget_show(struct device *dev,
                case snd_soc_dapm_pga:
                case snd_soc_dapm_mixer:
                case snd_soc_dapm_mixer_named_ctl:
+               case snd_soc_dapm_supply:
                        if (w->name)
                                count += sprintf(buf + count, "%s: %s\n",
                                        w->name, w->power ? "On":"Off");
@@ -883,16 +1042,12 @@ static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL);
 
 int snd_soc_dapm_sys_add(struct device *dev)
 {
-       if (!dapm_status)
-               return 0;
        return device_create_file(dev, &dev_attr_dapm_widget);
 }
 
 static void snd_soc_dapm_sys_remove(struct device *dev)
 {
-       if (dapm_status) {
-               device_remove_file(dev, &dev_attr_dapm_widget);
-       }
+       device_remove_file(dev, &dev_attr_dapm_widget);
 }
 
 /* free all dapm widgets and resources */
@@ -1015,6 +1170,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
        case snd_soc_dapm_vmid:
        case snd_soc_dapm_pre:
        case snd_soc_dapm_post:
+       case snd_soc_dapm_supply:
                list_add(&path->list, &codec->dapm_paths);
                list_add(&path->list_sink, &wsink->sources);
                list_add(&path->list_source, &wsource->sinks);
@@ -1108,15 +1264,22 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
                case snd_soc_dapm_switch:
                case snd_soc_dapm_mixer:
                case snd_soc_dapm_mixer_named_ctl:
+                       w->power_check = dapm_generic_check_power;
                        dapm_new_mixer(codec, w);
                        break;
                case snd_soc_dapm_mux:
                case snd_soc_dapm_value_mux:
+                       w->power_check = dapm_generic_check_power;
                        dapm_new_mux(codec, w);
                        break;
                case snd_soc_dapm_adc:
+                       w->power_check = dapm_adc_check_power;
+                       break;
                case snd_soc_dapm_dac:
+                       w->power_check = dapm_dac_check_power;
+                       break;
                case snd_soc_dapm_pga:
+                       w->power_check = dapm_generic_check_power;
                        dapm_new_pga(codec, w);
                        break;
                case snd_soc_dapm_input:
@@ -1126,6 +1289,10 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
                case snd_soc_dapm_hp:
                case snd_soc_dapm_mic:
                case snd_soc_dapm_line:
+                       w->power_check = dapm_generic_check_power;
+                       break;
+               case snd_soc_dapm_supply:
+                       w->power_check = dapm_supply_check_power;
                case snd_soc_dapm_vmid:
                case snd_soc_dapm_pre:
                case snd_soc_dapm_post:
@@ -1625,36 +1792,12 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec,
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
 
-/**
- * snd_soc_dapm_set_bias_level - set the bias level for the system
- * @socdev: audio device
- * @level: level to configure
- *
- * Configure the bias (power) levels for the SoC audio device.
- *
- * Returns 0 for success else error.
- */
-int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
-                               enum snd_soc_bias_level level)
-{
-       struct snd_soc_card *card = socdev->card;
-       struct snd_soc_codec *codec = socdev->card->codec;
-       int ret = 0;
-
-       if (card->set_bias_level)
-               ret = card->set_bias_level(card, level);
-       if (ret == 0 && codec->set_bias_level)
-               ret = codec->set_bias_level(codec, level);
-
-       return ret;
-}
-
 /**
  * snd_soc_dapm_enable_pin - enable pin.
  * @codec: SoC codec
  * @pin: pin name
  *
- * Enables input/output pin and it's parents or children widgets iff there is
+ * Enables input/output pin and its parents or children widgets iff there is
  * a valid audio route and active audio stream.
  * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
  * do any widget power switching.
@@ -1670,7 +1813,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin);
  * @codec: SoC codec
  * @pin: pin name
  *
- * Disables input/output pin and it's parents or children widgets.
+ * Disables input/output pin and its parents or children widgets.
  * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
  * do any widget power switching.
  */
diff --git a/sound/soc/txx9/Kconfig b/sound/soc/txx9/Kconfig
new file mode 100644 (file)
index 0000000..ebc9327
--- /dev/null
@@ -0,0 +1,29 @@
+##
+## TXx9 ACLC
+##
+config SND_SOC_TXX9ACLC
+       tristate "SoC Audio for TXx9"
+       depends on HAS_TXX9_ACLC && TXX9_DMAC
+       help
+         This option enables support for the AC Link Controllers in TXx9 SoC.
+
+config HAS_TXX9_ACLC
+       bool
+
+config SND_SOC_TXX9ACLC_AC97
+       tristate
+       select AC97_BUS
+       select SND_AC97_CODEC
+       select SND_SOC_AC97_BUS
+
+
+##
+## Boards
+##
+config SND_SOC_TXX9ACLC_GENERIC
+       tristate "Generic TXx9 ACLC sound machine"
+       depends on SND_SOC_TXX9ACLC
+       select SND_SOC_TXX9ACLC_AC97
+       select SND_SOC_AC97_CODEC
+       help
+         This is a generic AC97 sound machine for use in TXx9 based systems.
diff --git a/sound/soc/txx9/Makefile b/sound/soc/txx9/Makefile
new file mode 100644 (file)
index 0000000..551f16c
--- /dev/null
@@ -0,0 +1,11 @@
+# Platform
+snd-soc-txx9aclc-objs := txx9aclc.o
+snd-soc-txx9aclc-ac97-objs := txx9aclc-ac97.o
+
+obj-$(CONFIG_SND_SOC_TXX9ACLC) += snd-soc-txx9aclc.o
+obj-$(CONFIG_SND_SOC_TXX9ACLC_AC97) += snd-soc-txx9aclc-ac97.o
+
+# Machine
+snd-soc-txx9aclc-generic-objs := txx9aclc-generic.o
+
+obj-$(CONFIG_SND_SOC_TXX9ACLC_GENERIC) += snd-soc-txx9aclc-generic.o
diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c
new file mode 100644 (file)
index 0000000..0f83bdb
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * TXx9 ACLC AC97 driver
+ *
+ * Copyright (C) 2009 Atsushi Nemoto
+ *
+ * Based on RBTX49xx patch from CELF patch archive.
+ * (C) Copyright TOSHIBA CORPORATION 2004-2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include "txx9aclc.h"
+
+#define AC97_DIR       \
+       (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
+
+#define AC97_RATES     \
+       SNDRV_PCM_RATE_8000_48000
+
+#ifdef __BIG_ENDIAN
+#define AC97_FMTS      SNDRV_PCM_FMTBIT_S16_BE
+#else
+#define AC97_FMTS      SNDRV_PCM_FMTBIT_S16_LE
+#endif
+
+static DECLARE_WAIT_QUEUE_HEAD(ac97_waitq);
+
+/* REVISIT: How to find txx9aclc_soc_device from snd_ac97? */
+static struct txx9aclc_soc_device *txx9aclc_soc_dev;
+
+static int txx9aclc_regready(struct txx9aclc_soc_device *dev)
+{
+       struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev);
+
+       return __raw_readl(drvdata->base + ACINTSTS) & ACINT_REGACCRDY;
+}
+
+/* AC97 controller reads codec register */
+static unsigned short txx9aclc_ac97_read(struct snd_ac97 *ac97,
+                                        unsigned short reg)
+{
+       struct txx9aclc_soc_device *dev = txx9aclc_soc_dev;
+       struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev);
+       void __iomem *base = drvdata->base;
+       u32 dat;
+
+       if (!(__raw_readl(base + ACINTSTS) & ACINT_CODECRDY(ac97->num)))
+               return 0xffff;
+       reg |= ac97->num << 7;
+       dat = (reg << ACREGACC_REG_SHIFT) | ACREGACC_READ;
+       __raw_writel(dat, base + ACREGACC);
+       __raw_writel(ACINT_REGACCRDY, base + ACINTEN);
+       if (!wait_event_timeout(ac97_waitq, txx9aclc_regready(dev), HZ)) {
+               __raw_writel(ACINT_REGACCRDY, base + ACINTDIS);
+               dev_err(dev->soc_dev.dev, "ac97 read timeout (reg %#x)\n", reg);
+               dat = 0xffff;
+               goto done;
+       }
+       dat = __raw_readl(base + ACREGACC);
+       if (((dat >> ACREGACC_REG_SHIFT) & 0xff) != reg) {
+               dev_err(dev->soc_dev.dev, "reg mismatch %x with %x\n",
+                       dat, reg);
+               dat = 0xffff;
+               goto done;
+       }
+       dat = (dat >> ACREGACC_DAT_SHIFT) & 0xffff;
+done:
+       __raw_writel(ACINT_REGACCRDY, base + ACINTDIS);
+       return dat;
+}
+
+/* AC97 controller writes to codec register */
+static void txx9aclc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+                               unsigned short val)
+{
+       struct txx9aclc_soc_device *dev = txx9aclc_soc_dev;
+       struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev);
+       void __iomem *base = drvdata->base;
+
+       __raw_writel(((reg | (ac97->num << 7)) << ACREGACC_REG_SHIFT) |
+                    (val << ACREGACC_DAT_SHIFT),
+                    base + ACREGACC);
+       __raw_writel(ACINT_REGACCRDY, base + ACINTEN);
+       if (!wait_event_timeout(ac97_waitq, txx9aclc_regready(dev), HZ)) {
+               dev_err(dev->soc_dev.dev,
+                       "ac97 write timeout (reg %#x)\n", reg);
+       }
+       __raw_writel(ACINT_REGACCRDY, base + ACINTDIS);
+}
+
+static void txx9aclc_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+       struct txx9aclc_soc_device *dev = txx9aclc_soc_dev;
+       struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev);
+       void __iomem *base = drvdata->base;
+       u32 ready = ACINT_CODECRDY(ac97->num) | ACINT_REGACCRDY;
+
+       __raw_writel(ACCTL_ENLINK, base + ACCTLDIS);
+       mmiowb();
+       udelay(1);
+       __raw_writel(ACCTL_ENLINK, base + ACCTLEN);
+       /* wait for primary codec ready status */
+       __raw_writel(ready, base + ACINTEN);
+       if (!wait_event_timeout(ac97_waitq,
+                               (__raw_readl(base + ACINTSTS) & ready) == ready,
+                               HZ)) {
+               dev_err(&ac97->dev, "primary codec is not ready "
+                       "(status %#x)\n",
+                       __raw_readl(base + ACINTSTS));
+       }
+       __raw_writel(ACINT_REGACCRDY, base + ACINTSTS);
+       __raw_writel(ready, base + ACINTDIS);
+}
+
+/* AC97 controller operations */
+struct snd_ac97_bus_ops soc_ac97_ops = {
+       .read           = txx9aclc_ac97_read,
+       .write          = txx9aclc_ac97_write,
+       .reset          = txx9aclc_ac97_cold_reset,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+static irqreturn_t txx9aclc_ac97_irq(int irq, void *dev_id)
+{
+       struct txx9aclc_plat_drvdata *drvdata = dev_id;
+       void __iomem *base = drvdata->base;
+
+       __raw_writel(__raw_readl(base + ACINTMSTS), base + ACINTDIS);
+       wake_up(&ac97_waitq);
+       return IRQ_HANDLED;
+}
+
+static int txx9aclc_ac97_probe(struct platform_device *pdev,
+                              struct snd_soc_dai *dai)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct txx9aclc_soc_device *dev =
+               container_of(socdev, struct txx9aclc_soc_device, soc_dev);
+
+       dev->aclc_pdev = to_platform_device(dai->dev);
+       txx9aclc_soc_dev = dev;
+       return 0;
+}
+
+static void txx9aclc_ac97_remove(struct platform_device *pdev,
+                                struct snd_soc_dai *dai)
+{
+       struct platform_device *aclc_pdev = to_platform_device(dai->dev);
+       struct txx9aclc_plat_drvdata *drvdata = platform_get_drvdata(aclc_pdev);
+
+       /* disable AC-link */
+       __raw_writel(ACCTL_ENLINK, drvdata->base + ACCTLDIS);
+       txx9aclc_soc_dev = NULL;
+}
+
+struct snd_soc_dai txx9aclc_ac97_dai = {
+       .name                   = "txx9aclc_ac97",
+       .ac97_control           = 1,
+       .probe                  = txx9aclc_ac97_probe,
+       .remove                 = txx9aclc_ac97_remove,
+       .playback = {
+               .rates          = AC97_RATES,
+               .formats        = AC97_FMTS,
+               .channels_min   = 2,
+               .channels_max   = 2,
+       },
+       .capture = {
+               .rates          = AC97_RATES,
+               .formats        = AC97_FMTS,
+               .channels_min   = 2,
+               .channels_max   = 2,
+       },
+};
+EXPORT_SYMBOL_GPL(txx9aclc_ac97_dai);
+
+static int __devinit txx9aclc_ac97_dev_probe(struct platform_device *pdev)
+{
+       struct txx9aclc_plat_drvdata *drvdata;
+       struct resource *r;
+       int err;
+       int irq;
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!r)
+               return -EBUSY;
+
+       if (!devm_request_mem_region(&pdev->dev, r->start, resource_size(r),
+                                    dev_name(&pdev->dev)))
+               return -EBUSY;
+
+       drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
+       if (!drvdata)
+               return -ENOMEM;
+       platform_set_drvdata(pdev, drvdata);
+       drvdata->physbase = r->start;
+       if (sizeof(drvdata->physbase) > sizeof(r->start) &&
+           r->start >= TXX9_DIRECTMAP_BASE &&
+           r->start < TXX9_DIRECTMAP_BASE + 0x400000)
+               drvdata->physbase |= 0xf00000000ull;
+       drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
+       if (!drvdata->base)
+               return -EBUSY;
+       err = devm_request_irq(&pdev->dev, irq, txx9aclc_ac97_irq,
+                              IRQF_DISABLED, dev_name(&pdev->dev), drvdata);
+       if (err < 0)
+               return err;
+
+       txx9aclc_ac97_dai.dev = &pdev->dev;
+       return snd_soc_register_dai(&txx9aclc_ac97_dai);
+}
+
+static int __devexit txx9aclc_ac97_dev_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_dai(&txx9aclc_ac97_dai);
+       return 0;
+}
+
+static struct platform_driver txx9aclc_ac97_driver = {
+       .probe          = txx9aclc_ac97_dev_probe,
+       .remove         = __devexit_p(txx9aclc_ac97_dev_remove),
+       .driver         = {
+               .name   = "txx9aclc-ac97",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init txx9aclc_ac97_init(void)
+{
+       return platform_driver_register(&txx9aclc_ac97_driver);
+}
+
+static void __exit txx9aclc_ac97_exit(void)
+{
+       platform_driver_unregister(&txx9aclc_ac97_driver);
+}
+
+module_init(txx9aclc_ac97_init);
+module_exit(txx9aclc_ac97_exit);
+
+MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
+MODULE_DESCRIPTION("TXx9 ACLC AC97 driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/txx9/txx9aclc-generic.c b/sound/soc/txx9/txx9aclc-generic.c
new file mode 100644 (file)
index 0000000..3175de9
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Generic TXx9 ACLC machine driver
+ *
+ * Copyright (C) 2009 Atsushi Nemoto
+ *
+ * Based on RBTX49xx patch from CELF patch archive.
+ * (C) Copyright TOSHIBA CORPORATION 2004-2006
+ *
+ * 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 is a very generic AC97 sound machine driver for boards which
+ * have (AC97) audio at ACLC (e.g. RBTX49XX boards).
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include "../codecs/ac97.h"
+#include "txx9aclc.h"
+
+static struct snd_soc_dai_link txx9aclc_generic_dai = {
+       .name = "AC97",
+       .stream_name = "AC97 HiFi",
+       .cpu_dai = &txx9aclc_ac97_dai,
+       .codec_dai = &ac97_dai,
+};
+
+static struct snd_soc_card txx9aclc_generic_card = {
+       .name           = "Generic TXx9 ACLC Audio",
+       .platform       = &txx9aclc_soc_platform,
+       .dai_link       = &txx9aclc_generic_dai,
+       .num_links      = 1,
+};
+
+static struct txx9aclc_soc_device txx9aclc_generic_soc_device = {
+       .soc_dev = {
+               .card           = &txx9aclc_generic_card,
+               .codec_dev      = &soc_codec_dev_ac97,
+       },
+};
+
+static int __init txx9aclc_generic_probe(struct platform_device *pdev)
+{
+       struct txx9aclc_soc_device *dev = &txx9aclc_generic_soc_device;
+       struct platform_device *soc_pdev;
+       int ret;
+
+       soc_pdev = platform_device_alloc("soc-audio", -1);
+       if (!soc_pdev)
+               return -ENOMEM;
+       platform_set_drvdata(soc_pdev, &dev->soc_dev);
+       dev->soc_dev.dev = &soc_pdev->dev;
+       ret = platform_device_add(soc_pdev);
+       if (ret) {
+               platform_device_put(soc_pdev);
+               return ret;
+       }
+       platform_set_drvdata(pdev, soc_pdev);
+       return 0;
+}
+
+static int __exit txx9aclc_generic_remove(struct platform_device *pdev)
+{
+       struct platform_device *soc_pdev = platform_get_drvdata(pdev);
+
+       platform_device_unregister(soc_pdev);
+       return 0;
+}
+
+static struct platform_driver txx9aclc_generic_driver = {
+       .remove = txx9aclc_generic_remove,
+       .driver = {
+               .name = "txx9aclc-generic",
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init txx9aclc_generic_init(void)
+{
+       return platform_driver_probe(&txx9aclc_generic_driver,
+                                    txx9aclc_generic_probe);
+}
+
+static void __exit txx9aclc_generic_exit(void)
+{
+       platform_driver_unregister(&txx9aclc_generic_driver);
+}
+
+module_init(txx9aclc_generic_init);
+module_exit(txx9aclc_generic_exit);
+
+MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
+MODULE_DESCRIPTION("Generic TXx9 ACLC ALSA SoC audio driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c
new file mode 100644 (file)
index 0000000..fa33661
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+ * Generic TXx9 ACLC platform driver
+ *
+ * Copyright (C) 2009 Atsushi Nemoto
+ *
+ * Based on RBTX49xx patch from CELF patch archive.
+ * (C) Copyright TOSHIBA CORPORATION 2004-2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "txx9aclc.h"
+
+static const struct snd_pcm_hardware txx9aclc_pcm_hardware = {
+       /*
+        * REVISIT: SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID
+        * needs more works for noncoherent MIPS.
+        */
+       .info             = SNDRV_PCM_INFO_INTERLEAVED |
+                           SNDRV_PCM_INFO_BATCH |
+                           SNDRV_PCM_INFO_PAUSE,
+#ifdef __BIG_ENDIAN
+       .formats          = SNDRV_PCM_FMTBIT_S16_BE,
+#else
+       .formats          = SNDRV_PCM_FMTBIT_S16_LE,
+#endif
+       .period_bytes_min = 1024,
+       .period_bytes_max = 8 * 1024,
+       .periods_min      = 2,
+       .periods_max      = 4096,
+       .buffer_bytes_max = 32 * 1024,
+};
+
+static int txx9aclc_pcm_hw_params(struct snd_pcm_substream *substream,
+                                 struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct txx9aclc_dmadata *dmadata = runtime->private_data;
+       int ret;
+
+       ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+       if (ret < 0)
+               return ret;
+
+       dev_dbg(socdev->dev,
+               "runtime->dma_area = %#lx dma_addr = %#lx dma_bytes = %zd "
+               "runtime->min_align %ld\n",
+               (unsigned long)runtime->dma_area,
+               (unsigned long)runtime->dma_addr, runtime->dma_bytes,
+               runtime->min_align);
+       dev_dbg(socdev->dev,
+               "periods %d period_bytes %d stream %d\n",
+               params_periods(params), params_period_bytes(params),
+               substream->stream);
+
+       dmadata->substream = substream;
+       dmadata->pos = 0;
+       return 0;
+}
+
+static int txx9aclc_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static int txx9aclc_pcm_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct txx9aclc_dmadata *dmadata = runtime->private_data;
+
+       dmadata->dma_addr = runtime->dma_addr;
+       dmadata->buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
+       dmadata->period_bytes = snd_pcm_lib_period_bytes(substream);
+
+       if (dmadata->buffer_bytes == dmadata->period_bytes) {
+               dmadata->frag_bytes = dmadata->period_bytes >> 1;
+               dmadata->frags = 2;
+       } else {
+               dmadata->frag_bytes = dmadata->period_bytes;
+               dmadata->frags = dmadata->buffer_bytes / dmadata->period_bytes;
+       }
+       dmadata->frag_count = 0;
+       dmadata->pos = 0;
+       return 0;
+}
+
+static void txx9aclc_dma_complete(void *arg)
+{
+       struct txx9aclc_dmadata *dmadata = arg;
+       unsigned long flags;
+
+       /* dma completion handler cannot submit new operations */
+       spin_lock_irqsave(&dmadata->dma_lock, flags);
+       if (dmadata->frag_count >= 0) {
+               dmadata->dmacount--;
+               BUG_ON(dmadata->dmacount < 0);
+               tasklet_schedule(&dmadata->tasklet);
+       }
+       spin_unlock_irqrestore(&dmadata->dma_lock, flags);
+}
+
+static struct dma_async_tx_descriptor *
+txx9aclc_dma_submit(struct txx9aclc_dmadata *dmadata, dma_addr_t buf_dma_addr)
+{
+       struct dma_chan *chan = dmadata->dma_chan;
+       struct dma_async_tx_descriptor *desc;
+       struct scatterlist sg;
+
+       sg_init_table(&sg, 1);
+       sg_set_page(&sg, pfn_to_page(PFN_DOWN(buf_dma_addr)),
+                   dmadata->frag_bytes, buf_dma_addr & (PAGE_SIZE - 1));
+       sg_dma_address(&sg) = buf_dma_addr;
+       desc = chan->device->device_prep_slave_sg(chan, &sg, 1,
+               dmadata->substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+               DMA_TO_DEVICE : DMA_FROM_DEVICE,
+               DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+       if (!desc) {
+               dev_err(&chan->dev->device, "cannot prepare slave dma\n");
+               return NULL;
+       }
+       desc->callback = txx9aclc_dma_complete;
+       desc->callback_param = dmadata;
+       desc->tx_submit(desc);
+       return desc;
+}
+
+#define NR_DMA_CHAIN           2
+
+static void txx9aclc_dma_tasklet(unsigned long data)
+{
+       struct txx9aclc_dmadata *dmadata = (struct txx9aclc_dmadata *)data;
+       struct dma_chan *chan = dmadata->dma_chan;
+       struct dma_async_tx_descriptor *desc;
+       struct snd_pcm_substream *substream = dmadata->substream;
+       u32 ctlbit = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+               ACCTL_AUDODMA : ACCTL_AUDIDMA;
+       int i;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dmadata->dma_lock, flags);
+       if (dmadata->frag_count < 0) {
+               struct txx9aclc_soc_device *dev =
+                       container_of(dmadata, struct txx9aclc_soc_device,
+                                    dmadata[substream->stream]);
+               struct txx9aclc_plat_drvdata *drvdata =
+                       txx9aclc_get_plat_drvdata(dev);
+               void __iomem *base = drvdata->base;
+
+               spin_unlock_irqrestore(&dmadata->dma_lock, flags);
+               chan->device->device_terminate_all(chan);
+               /* first time */
+               for (i = 0; i < NR_DMA_CHAIN; i++) {
+                       desc = txx9aclc_dma_submit(dmadata,
+                               dmadata->dma_addr + i * dmadata->frag_bytes);
+                       if (!desc)
+                               return;
+               }
+               dmadata->dmacount = NR_DMA_CHAIN;
+               chan->device->device_issue_pending(chan);
+               spin_lock_irqsave(&dmadata->dma_lock, flags);
+               __raw_writel(ctlbit, base + ACCTLEN);
+               dmadata->frag_count = NR_DMA_CHAIN % dmadata->frags;
+               spin_unlock_irqrestore(&dmadata->dma_lock, flags);
+               return;
+       }
+       BUG_ON(dmadata->dmacount >= NR_DMA_CHAIN);
+       while (dmadata->dmacount < NR_DMA_CHAIN) {
+               dmadata->dmacount++;
+               spin_unlock_irqrestore(&dmadata->dma_lock, flags);
+               desc = txx9aclc_dma_submit(dmadata,
+                       dmadata->dma_addr +
+                       dmadata->frag_count * dmadata->frag_bytes);
+               if (!desc)
+                       return;
+               chan->device->device_issue_pending(chan);
+
+               spin_lock_irqsave(&dmadata->dma_lock, flags);
+               dmadata->frag_count++;
+               dmadata->frag_count %= dmadata->frags;
+               dmadata->pos += dmadata->frag_bytes;
+               dmadata->pos %= dmadata->buffer_bytes;
+               if ((dmadata->frag_count * dmadata->frag_bytes) %
+                   dmadata->period_bytes == 0)
+                       snd_pcm_period_elapsed(substream);
+       }
+       spin_unlock_irqrestore(&dmadata->dma_lock, flags);
+}
+
+static int txx9aclc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct txx9aclc_dmadata *dmadata = substream->runtime->private_data;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct txx9aclc_soc_device *dev =
+               container_of(rtd->socdev, struct txx9aclc_soc_device, soc_dev);
+       struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev);
+       void __iomem *base = drvdata->base;
+       unsigned long flags;
+       int ret = 0;
+       u32 ctlbit = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+               ACCTL_AUDODMA : ACCTL_AUDIDMA;
+
+       spin_lock_irqsave(&dmadata->dma_lock, flags);
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               dmadata->frag_count = -1;
+               tasklet_schedule(&dmadata->tasklet);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               __raw_writel(ctlbit, base + ACCTLDIS);
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+       case SNDRV_PCM_TRIGGER_RESUME:
+               __raw_writel(ctlbit, base + ACCTLEN);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       spin_unlock_irqrestore(&dmadata->dma_lock, flags);
+       return ret;
+}
+
+static snd_pcm_uframes_t
+txx9aclc_pcm_pointer(struct snd_pcm_substream *substream)
+{
+       struct txx9aclc_dmadata *dmadata = substream->runtime->private_data;
+
+       return bytes_to_frames(substream->runtime, dmadata->pos);
+}
+
+static int txx9aclc_pcm_open(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct txx9aclc_soc_device *dev =
+               container_of(rtd->socdev, struct txx9aclc_soc_device, soc_dev);
+       struct txx9aclc_dmadata *dmadata = &dev->dmadata[substream->stream];
+       int ret;
+
+       ret = snd_soc_set_runtime_hwparams(substream, &txx9aclc_pcm_hardware);
+       if (ret)
+               return ret;
+       /* ensure that buffer size is a multiple of period size */
+       ret = snd_pcm_hw_constraint_integer(substream->runtime,
+                                           SNDRV_PCM_HW_PARAM_PERIODS);
+       if (ret < 0)
+               return ret;
+       substream->runtime->private_data = dmadata;
+       return 0;
+}
+
+static int txx9aclc_pcm_close(struct snd_pcm_substream *substream)
+{
+       struct txx9aclc_dmadata *dmadata = substream->runtime->private_data;
+       struct dma_chan *chan = dmadata->dma_chan;
+
+       dmadata->frag_count = -1;
+       chan->device->device_terminate_all(chan);
+       return 0;
+}
+
+static struct snd_pcm_ops txx9aclc_pcm_ops = {
+       .open           = txx9aclc_pcm_open,
+       .close          = txx9aclc_pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = txx9aclc_pcm_hw_params,
+       .hw_free        = txx9aclc_pcm_hw_free,
+       .prepare        = txx9aclc_pcm_prepare,
+       .trigger        = txx9aclc_pcm_trigger,
+       .pointer        = txx9aclc_pcm_pointer,
+};
+
+static void txx9aclc_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+       snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int txx9aclc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+                           struct snd_pcm *pcm)
+{
+       return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+               card->dev, 64 * 1024, 4 * 1024 * 1024);
+}
+
+static bool filter(struct dma_chan *chan, void *param)
+{
+       struct txx9aclc_dmadata *dmadata = param;
+       char devname[BUS_ID_SIZE + 2];
+
+       sprintf(devname, "%s.%d", dmadata->dma_res->name,
+               (int)dmadata->dma_res->start);
+       if (strcmp(dev_name(chan->device->dev), devname) == 0) {
+               chan->private = &dmadata->dma_slave;
+               return true;
+       }
+       return false;
+}
+
+static int txx9aclc_dma_init(struct txx9aclc_soc_device *dev,
+                            struct txx9aclc_dmadata *dmadata)
+{
+       struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev);
+       struct txx9dmac_slave *ds = &dmadata->dma_slave;
+       dma_cap_mask_t mask;
+
+       spin_lock_init(&dmadata->dma_lock);
+
+       ds->reg_width = sizeof(u32);
+       if (dmadata->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               ds->tx_reg = drvdata->physbase + ACAUDODAT;
+               ds->rx_reg = 0;
+       } else {
+               ds->tx_reg = 0;
+               ds->rx_reg = drvdata->physbase + ACAUDIDAT;
+       }
+
+       /* Try to grab a DMA channel */
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+       dmadata->dma_chan = dma_request_channel(mask, filter, dmadata);
+       if (!dmadata->dma_chan) {
+               dev_err(dev->soc_dev.dev,
+                       "DMA channel for %s is not available\n",
+                       dmadata->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+                       "playback" : "capture");
+               return -EBUSY;
+       }
+       tasklet_init(&dmadata->tasklet, txx9aclc_dma_tasklet,
+                    (unsigned long)dmadata);
+       return 0;
+}
+
+static int txx9aclc_pcm_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct txx9aclc_soc_device *dev =
+               container_of(socdev, struct txx9aclc_soc_device, soc_dev);
+       struct resource *r;
+       int i;
+       int ret;
+
+       dev->dmadata[0].stream = SNDRV_PCM_STREAM_PLAYBACK;
+       dev->dmadata[1].stream = SNDRV_PCM_STREAM_CAPTURE;
+       for (i = 0; i < 2; i++) {
+               r = platform_get_resource(dev->aclc_pdev, IORESOURCE_DMA, i);
+               if (!r) {
+                       ret = -EBUSY;
+                       goto exit;
+               }
+               dev->dmadata[i].dma_res = r;
+               ret = txx9aclc_dma_init(dev, &dev->dmadata[i]);
+               if (ret)
+                       goto exit;
+       }
+       return 0;
+
+exit:
+       for (i = 0; i < 2; i++) {
+               if (dev->dmadata[i].dma_chan)
+                       dma_release_channel(dev->dmadata[i].dma_chan);
+               dev->dmadata[i].dma_chan = NULL;
+       }
+       return ret;
+}
+
+static int txx9aclc_pcm_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct txx9aclc_soc_device *dev =
+               container_of(socdev, struct txx9aclc_soc_device, soc_dev);
+       struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev);
+       void __iomem *base = drvdata->base;
+       int i;
+
+       /* disable all FIFO DMAs */
+       __raw_writel(ACCTL_AUDODMA | ACCTL_AUDIDMA, base + ACCTLDIS);
+       /* dummy R/W to clear pending DMAREQ if any */
+       __raw_writel(__raw_readl(base + ACAUDIDAT), base + ACAUDODAT);
+
+       for (i = 0; i < 2; i++) {
+               struct txx9aclc_dmadata *dmadata = &dev->dmadata[i];
+               struct dma_chan *chan = dmadata->dma_chan;
+               if (chan) {
+                       dmadata->frag_count = -1;
+                       chan->device->device_terminate_all(chan);
+                       dma_release_channel(chan);
+               }
+               dev->dmadata[i].dma_chan = NULL;
+       }
+       return 0;
+}
+
+struct snd_soc_platform txx9aclc_soc_platform = {
+       .name           = "txx9aclc-audio",
+       .probe          = txx9aclc_pcm_probe,
+       .remove         = txx9aclc_pcm_remove,
+       .pcm_ops        = &txx9aclc_pcm_ops,
+       .pcm_new        = txx9aclc_pcm_new,
+       .pcm_free       = txx9aclc_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(txx9aclc_soc_platform);
+
+static int __init txx9aclc_soc_platform_init(void)
+{
+       return snd_soc_register_platform(&txx9aclc_soc_platform);
+}
+
+static void __exit txx9aclc_soc_platform_exit(void)
+{
+       snd_soc_unregister_platform(&txx9aclc_soc_platform);
+}
+
+module_init(txx9aclc_soc_platform_init);
+module_exit(txx9aclc_soc_platform_exit);
+
+MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
+MODULE_DESCRIPTION("TXx9 ACLC Audio DMA driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/txx9/txx9aclc.h b/sound/soc/txx9/txx9aclc.h
new file mode 100644 (file)
index 0000000..6769aab
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * TXx9 SoC AC Link Controller
+ *
+ * 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 __TXX9ACLC_H
+#define __TXX9ACLC_H
+
+#include <linux/interrupt.h>
+#include <asm/txx9/dmac.h>
+
+#define ACCTLEN                        0x00    /* control enable */
+#define ACCTLDIS               0x04    /* control disable */
+#define   ACCTL_ENLINK         0x00000001      /* enable/disable AC-link */
+#define   ACCTL_AUDODMA                0x00000100      /* AUDODMA enable/disable */
+#define   ACCTL_AUDIDMA                0x00001000      /* AUDIDMA enable/disable */
+#define   ACCTL_AUDOEHLT       0x00010000      /* AUDO error halt
+                                                  enable/disable */
+#define   ACCTL_AUDIEHLT       0x00100000      /* AUDI error halt
+                                                  enable/disable */
+#define ACREGACC               0x08    /* codec register access */
+#define   ACREGACC_DAT_SHIFT   0       /* data field */
+#define   ACREGACC_REG_SHIFT   16      /* address field */
+#define   ACREGACC_CODECID_SHIFT       24      /* CODEC ID field */
+#define   ACREGACC_READ                0x80000000      /* CODEC read */
+#define   ACREGACC_WRITE       0x00000000      /* CODEC write */
+#define ACINTSTS               0x10    /* interrupt status */
+#define ACINTMSTS              0x14    /* interrupt masked status */
+#define ACINTEN                        0x18    /* interrupt enable */
+#define ACINTDIS               0x1c    /* interrupt disable */
+#define   ACINT_CODECRDY(n)    (0x00000001 << (n))     /* CODECn ready */
+#define   ACINT_REGACCRDY      0x00000010      /* ACREGACC ready */
+#define   ACINT_AUDOERR                0x00000100      /* AUDO underrun error */
+#define   ACINT_AUDIERR                0x00001000      /* AUDI overrun error */
+#define ACDMASTS               0x80    /* DMA request status */
+#define   ACDMA_AUDO           0x00000001      /* AUDODMA pending */
+#define   ACDMA_AUDI           0x00000010      /* AUDIDMA pending */
+#define ACAUDODAT              0xa0    /* audio out data */
+#define ACAUDIDAT              0xb0    /* audio in data */
+#define ACREVID                        0xfc    /* revision ID */
+
+struct txx9aclc_dmadata {
+       struct resource *dma_res;
+       struct txx9dmac_slave dma_slave;
+       struct dma_chan *dma_chan;
+       struct tasklet_struct tasklet;
+       spinlock_t dma_lock;
+       int stream; /* SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE */
+       struct snd_pcm_substream *substream;
+       unsigned long pos;
+       dma_addr_t dma_addr;
+       unsigned long buffer_bytes;
+       unsigned long period_bytes;
+       unsigned long frag_bytes;
+       int frags;
+       int frag_count;
+       int dmacount;
+};
+
+struct txx9aclc_plat_drvdata {
+       void __iomem *base;
+       u64 physbase;
+};
+
+struct txx9aclc_soc_device {
+       struct snd_soc_device soc_dev;
+       struct platform_device *aclc_pdev;      /* for ioresources, drvdata */
+       struct txx9aclc_dmadata dmadata[2];
+};
+
+static inline struct txx9aclc_plat_drvdata *txx9aclc_get_plat_drvdata(
+       struct txx9aclc_soc_device *sdev)
+{
+       return platform_get_drvdata(sdev->aclc_pdev);
+}
+
+extern struct snd_soc_platform txx9aclc_soc_platform;
+extern struct snd_soc_dai txx9aclc_ac97_dai;
+
+#endif /* __TXX9ACLC_H */
index e99fd76caa1724a23cfa34185dd6e0890c589fbc..11eb06ac2eca09ce6d80bcc0fec4ba8cfdbb7563 100644 (file)
@@ -5,16 +5,8 @@
 
 snd-util-mem-objs := util_mem.o
 
-#
-# this function returns:
-#   "m" - CONFIG_SND_SEQUENCER is m
-#   <empty string> - CONFIG_SND_SEQUENCER is undefined
-#   otherwise parameter #1 value
-#
-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
-
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_EMU10K1) += snd-util-mem.o
 obj-$(CONFIG_SND_TRIDENT) += snd-util-mem.o
-obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-util-mem.o
-obj-$(call sequencer,$(CONFIG_SND)) += emux/
+obj-$(CONFIG_SND_SBAWE_SEQ) += snd-util-mem.o
+obj-$(CONFIG_SND_SEQUENCER) += emux/
index b69035240cf68399cb757dc52af65d34007d1801..328594e6152dc4670dcf897829c95ccd1b31986d 100644 (file)
@@ -7,14 +7,6 @@ snd-emux-synth-objs := emux.o emux_synth.o emux_seq.o emux_nrpn.o \
                       emux_effect.o emux_proc.o emux_hwdep.o soundfont.o \
                       $(if $(CONFIG_SND_SEQUENCER_OSS),emux_oss.o)
 
-#
-# this function returns:
-#   "m" - CONFIG_SND_SEQUENCER is m
-#   <empty string> - CONFIG_SND_SEQUENCER is undefined
-#   otherwise parameter #1 value
-#
-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
-
 # Toplevel Module Dependencies
-obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-emux-synth.o
-obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-emux-synth.o
+obj-$(CONFIG_SND_SBAWE_SEQ) += snd-emux-synth.o
+obj-$(CONFIG_SND_EMU10K1_SEQ) += snd-emux-synth.o
index b13ce767ac7235314bd8dad23373d640302167ca..b14451342166a7630b820731d131b0536e71b9bd 100644 (file)
        (stream << 1) | (~(i / (dev->n_streams * BYTES_PER_SAMPLE_USB)) & 1)
 
 static struct snd_pcm_hardware snd_usb_caiaq_pcm_hardware = {
-       .info           = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 
+       .info           = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
                           SNDRV_PCM_INFO_BLOCK_TRANSFER),
        .formats        = SNDRV_PCM_FMTBIT_S24_3BE,
-       .rates          = (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 
+       .rates          = (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
                           SNDRV_PCM_RATE_96000),
        .rate_min       = 44100,
        .rate_max       = 0, /* will overwrite later */
@@ -68,7 +68,7 @@ activate_substream(struct snd_usb_caiaqdev *dev,
                dev->sub_capture[sub->number] = sub;
 }
 
-static void 
+static void
 deactivate_substream(struct snd_usb_caiaqdev *dev,
                     struct snd_pcm_substream *sub)
 {
@@ -118,7 +118,7 @@ static int stream_start(struct snd_usb_caiaqdev *dev)
                        return -EPIPE;
                }
        }
-       
+
        return 0;
 }
 
@@ -129,7 +129,7 @@ static void stream_stop(struct snd_usb_caiaqdev *dev)
        debug("%s(%p)\n", __func__, dev);
        if (!dev->streaming)
                return;
-       
+
        dev->streaming = 0;
 
        for (i = 0; i < N_URBS; i++) {
@@ -154,7 +154,7 @@ static int snd_usb_caiaq_substream_close(struct snd_pcm_substream *substream)
        debug("%s(%p)\n", __func__, substream);
        if (all_substreams_zero(dev->sub_playback) &&
            all_substreams_zero(dev->sub_capture)) {
-               /* when the last client has stopped streaming, 
+               /* when the last client has stopped streaming,
                 * all sample rates are allowed again */
                stream_stop(dev);
                dev->pcm_info.rates = dev->samplerates;
@@ -194,7 +194,7 @@ static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream)
        struct snd_pcm_runtime *runtime = substream->runtime;
 
        debug("%s(%p)\n", __func__, substream);
-       
+
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                dev->period_out_count[index] = BYTES_PER_SAMPLE + 1;
                dev->audio_out_buf_pos[index] = BYTES_PER_SAMPLE + 1;
@@ -205,19 +205,19 @@ static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream)
 
        if (dev->streaming)
                return 0;
-       
+
        /* the first client that opens a stream defines the sample rate
         * setting for all subsequent calls, until the last client closed. */
        for (i=0; i < ARRAY_SIZE(rates); i++)
                if (runtime->rate == rates[i])
                        dev->pcm_info.rates = 1 << i;
-       
+
        snd_pcm_limit_hw_rates(runtime);
 
        bytes_per_sample = BYTES_PER_SAMPLE;
        if (dev->spec.data_alignment == 2)
                bytes_per_sample++;
-       
+
        bpp = ((runtime->rate / 8000) + CLOCK_DRIFT_TOLERANCE)
                * bytes_per_sample * CHANNELS_PER_STREAM * dev->n_streams;
 
@@ -232,7 +232,7 @@ static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream)
        ret = stream_start(dev);
        if (ret)
                return ret;
-       
+
        dev->output_running = 0;
        wait_event_timeout(dev->prepare_wait_queue, dev->output_running, HZ);
        if (!dev->output_running) {
@@ -273,7 +273,7 @@ snd_usb_caiaq_pcm_pointer(struct snd_pcm_substream *sub)
                return SNDRV_PCM_POS_XRUN;
 
        if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               return bytes_to_frames(sub->runtime, 
+               return bytes_to_frames(sub->runtime,
                                        dev->audio_out_buf_pos[index]);
        else
                return bytes_to_frames(sub->runtime,
@@ -291,7 +291,7 @@ static struct snd_pcm_ops snd_usb_caiaq_ops = {
        .trigger =      snd_usb_caiaq_pcm_trigger,
        .pointer =      snd_usb_caiaq_pcm_pointer
 };
-       
+
 static void check_for_elapsed_periods(struct snd_usb_caiaqdev *dev,
                                      struct snd_pcm_substream **subs)
 {
@@ -333,7 +333,7 @@ static void read_in_urb_mode0(struct snd_usb_caiaqdev *dev,
                                struct snd_pcm_runtime *rt = sub->runtime;
                                char *audio_buf = rt->dma_area;
                                int sz = frames_to_bytes(rt, rt->buffer_size);
-                               audio_buf[dev->audio_in_buf_pos[stream]++] 
+                               audio_buf[dev->audio_in_buf_pos[stream]++]
                                        = usb_buf[i];
                                dev->period_in_count[stream]++;
                                if (dev->audio_in_buf_pos[stream] == sz)
@@ -354,14 +354,14 @@ static void read_in_urb_mode2(struct snd_usb_caiaqdev *dev,
 
        for (i = 0; i < iso->actual_length;) {
                if (i % (dev->n_streams * BYTES_PER_SAMPLE_USB) == 0) {
-                       for (stream = 0; 
-                            stream < dev->n_streams; 
+                       for (stream = 0;
+                            stream < dev->n_streams;
                             stream++, i++) {
                                if (dev->first_packet)
                                        continue;
 
                                check_byte = MAKE_CHECKBYTE(dev, stream, i);
-                               
+
                                if ((usb_buf[i] & 0x3f) != check_byte)
                                        dev->input_panic = 1;
 
@@ -410,21 +410,21 @@ static void read_in_urb(struct snd_usb_caiaqdev *dev,
        }
 
        if ((dev->input_panic || dev->output_panic) && !dev->warned) {
-               debug("streaming error detected %s %s\n", 
+               debug("streaming error detected %s %s\n",
                                dev->input_panic ? "(input)" : "",
                                dev->output_panic ? "(output)" : "");
                dev->warned = 1;
        }
 }
 
-static void fill_out_urb(struct snd_usb_caiaqdev *dev, 
-                        struct urb *urb, 
+static void fill_out_urb(struct snd_usb_caiaqdev *dev,
+                        struct urb *urb,
                         const struct usb_iso_packet_descriptor *iso)
 {
        unsigned char *usb_buf = urb->transfer_buffer + iso->offset;
        struct snd_pcm_substream *sub;
        int stream, i;
-       
+
        for (i = 0; i < iso->length;) {
                for (stream = 0; stream < dev->n_streams; stream++, i++) {
                        sub = dev->sub_playback[stream];
@@ -444,7 +444,7 @@ static void fill_out_urb(struct snd_usb_caiaqdev *dev,
 
                /* fill in the check bytes */
                if (dev->spec.data_alignment == 2 &&
-                   i % (dev->n_streams * BYTES_PER_SAMPLE_USB) == 
+                   i % (dev->n_streams * BYTES_PER_SAMPLE_USB) ==
                        (dev->n_streams * CHANNELS_PER_STREAM))
                    for (stream = 0; stream < dev->n_streams; stream++, i++)
                        usb_buf[i] = MAKE_CHECKBYTE(dev, stream, i);
@@ -453,7 +453,7 @@ static void fill_out_urb(struct snd_usb_caiaqdev *dev,
 
 static void read_completed(struct urb *urb)
 {
-       struct snd_usb_caiaq_cb_info *info = urb->context; 
+       struct snd_usb_caiaq_cb_info *info = urb->context;
        struct snd_usb_caiaqdev *dev;
        struct urb *out;
        int frame, len, send_it = 0, outframe = 0;
@@ -478,7 +478,7 @@ static void read_completed(struct urb *urb)
                out->iso_frame_desc[outframe].length = len;
                out->iso_frame_desc[outframe].actual_length = 0;
                out->iso_frame_desc[outframe].offset = BYTES_PER_FRAME * frame;
-               
+
                if (len > 0) {
                        spin_lock(&dev->spinlock);
                        fill_out_urb(dev, out, &out->iso_frame_desc[outframe]);
@@ -497,14 +497,14 @@ static void read_completed(struct urb *urb)
                out->transfer_flags = URB_ISO_ASAP;
                usb_submit_urb(out, GFP_ATOMIC);
        }
-       
+
        /* re-submit inbound urb */
        for (frame = 0; frame < FRAMES_PER_URB; frame++) {
                urb->iso_frame_desc[frame].offset = BYTES_PER_FRAME * frame;
                urb->iso_frame_desc[frame].length = BYTES_PER_FRAME;
                urb->iso_frame_desc[frame].actual_length = 0;
        }
-       
+
        urb->number_of_packets = FRAMES_PER_URB;
        urb->transfer_flags = URB_ISO_ASAP;
        usb_submit_urb(urb, GFP_ATOMIC);
@@ -528,7 +528,7 @@ static struct urb **alloc_urbs(struct snd_usb_caiaqdev *dev, int dir, int *ret)
        struct usb_device *usb_dev = dev->chip.dev;
        unsigned int pipe;
 
-       pipe = (dir == SNDRV_PCM_STREAM_PLAYBACK) ? 
+       pipe = (dir == SNDRV_PCM_STREAM_PLAYBACK) ?
                usb_sndisocpipe(usb_dev, ENDPOINT_PLAYBACK) :
                usb_rcvisocpipe(usb_dev, ENDPOINT_CAPTURE);
 
@@ -547,25 +547,25 @@ static struct urb **alloc_urbs(struct snd_usb_caiaqdev *dev, int dir, int *ret)
                        return urbs;
                }
 
-               urbs[i]->transfer_buffer = 
+               urbs[i]->transfer_buffer =
                        kmalloc(FRAMES_PER_URB * BYTES_PER_FRAME, GFP_KERNEL);
                if (!urbs[i]->transfer_buffer) {
                        log("unable to kmalloc() transfer buffer, OOM!?\n");
                        *ret = -ENOMEM;
                        return urbs;
                }
-               
+
                for (frame = 0; frame < FRAMES_PER_URB; frame++) {
-                       struct usb_iso_packet_descriptor *iso = 
+                       struct usb_iso_packet_descriptor *iso =
                                &urbs[i]->iso_frame_desc[frame];
-                       
+
                        iso->offset = BYTES_PER_FRAME * frame;
                        iso->length = BYTES_PER_FRAME;
                }
-               
+
                urbs[i]->dev = usb_dev;
                urbs[i]->pipe = pipe;
-               urbs[i]->transfer_buffer_length = FRAMES_PER_URB 
+               urbs[i]->transfer_buffer_length = FRAMES_PER_URB
                                                * BYTES_PER_FRAME;
                urbs[i]->context = &dev->data_cb_info[i];
                urbs[i]->interval = 1;
@@ -589,7 +589,7 @@ static void free_urbs(struct urb **urbs)
        for (i = 0; i < N_URBS; i++) {
                if (!urbs[i])
                        continue;
-               
+
                usb_kill_urb(urbs[i]);
                kfree(urbs[i]->transfer_buffer);
                usb_free_urb(urbs[i]);
@@ -602,11 +602,11 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev)
 {
        int i, ret;
 
-       dev->n_audio_in  = max(dev->spec.num_analog_audio_in, 
-                              dev->spec.num_digital_audio_in) / 
+       dev->n_audio_in  = max(dev->spec.num_analog_audio_in,
+                              dev->spec.num_digital_audio_in) /
                                CHANNELS_PER_STREAM;
        dev->n_audio_out = max(dev->spec.num_analog_audio_out,
-                              dev->spec.num_digital_audio_out) / 
+                              dev->spec.num_digital_audio_out) /
                                CHANNELS_PER_STREAM;
        dev->n_streams = max(dev->n_audio_in, dev->n_audio_out);
 
@@ -619,7 +619,7 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev)
                return -EINVAL;
        }
 
-       ret = snd_pcm_new(dev->chip.card, dev->product_name, 0, 
+       ret = snd_pcm_new(dev->chip.card, dev->product_name, 0,
                        dev->n_audio_out, dev->n_audio_in, &dev->pcm);
 
        if (ret < 0) {
@@ -632,7 +632,7 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev)
 
        memset(dev->sub_playback, 0, sizeof(dev->sub_playback));
        memset(dev->sub_capture, 0, sizeof(dev->sub_capture));
-       
+
        memcpy(&dev->pcm_info, &snd_usb_caiaq_pcm_hardware,
                        sizeof(snd_usb_caiaq_pcm_hardware));
 
@@ -651,9 +651,9 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev)
                break;
        }
 
-       snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, 
+       snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK,
                                &snd_usb_caiaq_ops);
-       snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, 
+       snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE,
                                &snd_usb_caiaq_ops);
 
        snd_pcm_lib_preallocate_pages_for_all(dev->pcm,
@@ -662,7 +662,7 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev)
                                        MAX_BUFFER_SIZE, MAX_BUFFER_SIZE);
 
        dev->data_cb_info =
-               kmalloc(sizeof(struct snd_usb_caiaq_cb_info) * N_URBS, 
+               kmalloc(sizeof(struct snd_usb_caiaq_cb_info) * N_URBS,
                                        GFP_KERNEL);
 
        if (!dev->data_cb_info)
@@ -672,14 +672,14 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev)
                dev->data_cb_info[i].dev = dev;
                dev->data_cb_info[i].index = i;
        }
-       
+
        dev->data_urbs_in = alloc_urbs(dev, SNDRV_PCM_STREAM_CAPTURE, &ret);
        if (ret < 0) {
                kfree(dev->data_cb_info);
                free_urbs(dev->data_urbs_in);
                return ret;
        }
-       
+
        dev->data_urbs_out = alloc_urbs(dev, SNDRV_PCM_STREAM_PLAYBACK, &ret);
        if (ret < 0) {
                kfree(dev->data_cb_info);
index 515de1cd2a3ecbaf30649cb48deab0a5732be1b2..22406245a98bb9a4a105161e2eceb44d13e44418 100644 (file)
@@ -35,7 +35,7 @@
 #include "input.h"
 
 MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
-MODULE_DESCRIPTION("caiaq USB audio, version 1.3.14");
+MODULE_DESCRIPTION("caiaq USB audio, version 1.3.16");
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
                         "{Native Instruments, RigKontrol3},"
@@ -79,7 +79,7 @@ static struct usb_device_id snd_usb_id_table[] = {
        {
                .match_flags =  USB_DEVICE_ID_MATCH_DEVICE,
                .idVendor =     USB_VID_NATIVEINSTRUMENTS,
-               .idProduct =    USB_PID_RIGKONTROL2 
+               .idProduct =    USB_PID_RIGKONTROL2
        },
        {
                .match_flags =  USB_DEVICE_ID_MATCH_DEVICE,
@@ -197,7 +197,7 @@ int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *dev,
 
        if (buffer && len > 0)
                memcpy(dev->ep1_out_buf+1, buffer, len);
-       
+
        dev->ep1_out_buf[0] = command;
        return usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, 1),
                           dev->ep1_out_buf, len+1, &actual_len, 200);
@@ -208,7 +208,7 @@ int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *dev,
 {
        int ret;
        char tmp[5];
-       
+
        switch (rate) {
        case 44100:     tmp[0] = SAMPLERATE_44100;   break;
        case 48000:     tmp[0] = SAMPLERATE_48000;   break;
@@ -237,12 +237,12 @@ int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *dev,
 
        if (ret)
                return ret;
-       
-       if (!wait_event_timeout(dev->ep1_wait_queue, 
+
+       if (!wait_event_timeout(dev->ep1_wait_queue,
            dev->audio_parm_answer >= 0, HZ))
                return -EPIPE;
-               
-       if (dev->audio_parm_answer != 1) 
+
+       if (dev->audio_parm_answer != 1)
                debug("unable to set the device's audio params\n");
        else
                dev->bpp = bpp;
@@ -250,8 +250,8 @@ int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *dev,
        return dev->audio_parm_answer == 1 ? 0 : -EINVAL;
 }
 
-int snd_usb_caiaq_set_auto_msg (struct snd_usb_caiaqdev *dev, 
-                               int digital, int analog, int erp)
+int snd_usb_caiaq_set_auto_msg(struct snd_usb_caiaqdev *dev,
+                              int digital, int analog, int erp)
 {
        char tmp[3] = { digital, analog, erp };
        return snd_usb_caiaq_send_command(dev, EP1_CMD_AUTO_MSG,
@@ -262,7 +262,7 @@ static void __devinit setup_card(struct snd_usb_caiaqdev *dev)
 {
        int ret;
        char val[4];
-       
+
        /* device-specific startup specials */
        switch (dev->chip.usb_id) {
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
@@ -314,7 +314,7 @@ static void __devinit setup_card(struct snd_usb_caiaqdev *dev)
                        dev->control_state, 1);
                break;
        }
-       
+
        if (dev->spec.num_analog_audio_out +
            dev->spec.num_analog_audio_in +
            dev->spec.num_digital_audio_out +
@@ -323,7 +323,7 @@ static void __devinit setup_card(struct snd_usb_caiaqdev *dev)
                if (ret < 0)
                        log("Unable to set up audio system (ret=%d)\n", ret);
        }
-       
+
        if (dev->spec.num_midi_in +
            dev->spec.num_midi_out > 0) {
                ret = snd_usb_caiaq_midi_init(dev);
@@ -363,7 +363,7 @@ static int create_card(struct usb_device* usb_dev, struct snd_card **cardp)
        if (devnum >= SNDRV_CARDS)
                return -ENODEV;
 
-       err = snd_card_create(index[devnum], id[devnum], THIS_MODULE, 
+       err = snd_card_create(index[devnum], id[devnum], THIS_MODULE,
                              sizeof(struct snd_usb_caiaqdev), &card);
        if (err < 0)
                return err;
@@ -382,11 +382,11 @@ static int create_card(struct usb_device* usb_dev, struct snd_card **cardp)
 
 static int __devinit init_card(struct snd_usb_caiaqdev *dev)
 {
-       char *c;
+       char *c, usbpath[32];
        struct usb_device *usb_dev = dev->chip.dev;
        struct snd_card *card = dev->chip.card;
        int err, len;
-       
+
        if (usb_set_interface(usb_dev, 0, 1) != 0) {
                log("can't set alt interface.\n");
                return -EIO;
@@ -395,19 +395,19 @@ static int __devinit init_card(struct snd_usb_caiaqdev *dev)
        usb_init_urb(&dev->ep1_in_urb);
        usb_init_urb(&dev->midi_out_urb);
 
-       usb_fill_bulk_urb(&dev->ep1_in_urb, usb_dev, 
+       usb_fill_bulk_urb(&dev->ep1_in_urb, usb_dev,
                          usb_rcvbulkpipe(usb_dev, 0x1),
-                         dev->ep1_in_buf, EP1_BUFSIZE, 
+                         dev->ep1_in_buf, EP1_BUFSIZE,
                          usb_ep1_command_reply_dispatch, dev);
 
-       usb_fill_bulk_urb(&dev->midi_out_urb, usb_dev, 
+       usb_fill_bulk_urb(&dev->midi_out_urb, usb_dev,
                          usb_sndbulkpipe(usb_dev, 0x1),
-                         dev->midi_out_buf, EP1_BUFSIZE, 
+                         dev->midi_out_buf, EP1_BUFSIZE,
                          snd_usb_caiaq_midi_output_done, dev);
-       
+
        init_waitqueue_head(&dev->ep1_wait_queue);
        init_waitqueue_head(&dev->prepare_wait_queue);
-       
+
        if (usb_submit_urb(&dev->ep1_in_urb, GFP_KERNEL) != 0)
                return -EIO;
 
@@ -420,47 +420,52 @@ static int __devinit init_card(struct snd_usb_caiaqdev *dev)
 
        usb_string(usb_dev, usb_dev->descriptor.iManufacturer,
                   dev->vendor_name, CAIAQ_USB_STR_LEN);
-       
+
        usb_string(usb_dev, usb_dev->descriptor.iProduct,
                   dev->product_name, CAIAQ_USB_STR_LEN);
-       
-       usb_string(usb_dev, usb_dev->descriptor.iSerialNumber,
-                  dev->serial, CAIAQ_USB_STR_LEN);
-
-       /* terminate serial string at first white space occurence */
-       c = strchr(dev->serial, ' ');
-       if (c)
-               *c = '\0';
-       
-       strcpy(card->driver, MODNAME);
-       strcpy(card->shortname, dev->product_name);
-
-       len = snprintf(card->longname, sizeof(card->longname),
-                      "%s %s (serial %s, ",
-                      dev->vendor_name, dev->product_name, dev->serial);
-
-       if (len < sizeof(card->longname) - 2)
-               len += usb_make_path(usb_dev, card->longname + len,
-                                    sizeof(card->longname) - len);
-
-       card->longname[len++] = ')';
-       card->longname[len] = '\0';
+
+       strlcpy(card->driver, MODNAME, sizeof(card->driver));
+       strlcpy(card->shortname, dev->product_name, sizeof(card->shortname));
+       strlcpy(card->mixername, dev->product_name, sizeof(card->mixername));
+
+       /* if the id was not passed as module option, fill it with a shortened
+        * version of the product string which does not contain any
+        * whitespaces */
+
+       if (*card->id == '\0') {
+               char id[sizeof(card->id)];
+
+               memset(id, 0, sizeof(id));
+
+               for (c = card->shortname, len = 0;
+                       *c && len < sizeof(card->id); c++)
+                       if (*c != ' ')
+                               id[len++] = *c;
+
+               snd_card_set_id(card, id);
+       }
+
+       usb_make_path(usb_dev, usbpath, sizeof(usbpath));
+       snprintf(card->longname, sizeof(card->longname),
+                      "%s %s (%s)",
+                      dev->vendor_name, dev->product_name, usbpath);
+
        setup_card(dev);
        return 0;
 }
 
-static int __devinit snd_probe(struct usb_interface *intf, 
+static int __devinit snd_probe(struct usb_interface *intf,
                     const struct usb_device_id *id)
 {
        int ret;
        struct snd_card *card;
        struct usb_device *device = interface_to_usbdev(intf);
-       
+
        ret = create_card(device, &card);
-       
+
        if (ret < 0)
                return ret;
-                       
+
        usb_set_intfdata(intf, card);
        ret = init_card(caiaqdev(card));
        if (ret < 0) {
@@ -468,7 +473,7 @@ static int __devinit snd_probe(struct usb_interface *intf,
                snd_card_free(card);
                return ret;
        }
-       
+
        return 0;
 }
 
@@ -489,10 +494,10 @@ static void snd_disconnect(struct usb_interface *intf)
        snd_usb_caiaq_input_free(dev);
 #endif
        snd_usb_caiaq_audio_free(dev);
-       
+
        usb_kill_urb(&dev->ep1_in_urb);
        usb_kill_urb(&dev->midi_out_urb);
-       
+
        snd_card_free(card);
        usb_reset_device(interface_to_usbdev(intf));
 }
index 4cce1ad7493db4ec138216bb3a56b6ae20fcc90f..ece73514854e2f302ab88da1d1a857d3f43ee3ec 100644 (file)
@@ -81,7 +81,6 @@ struct snd_usb_caiaqdev {
 
        char vendor_name[CAIAQ_USB_STR_LEN];
        char product_name[CAIAQ_USB_STR_LEN];
-       char serial[CAIAQ_USB_STR_LEN];
 
        int n_streams, n_audio_in, n_audio_out;
        int streaming, first_packet, output_running;
index 8fa8cd88d7634689592b7b512d85640a7128de5e..538e8c00d31aadac2c8555df958c1312051265b5 100644 (file)
@@ -40,7 +40,7 @@ static void snd_usb_caiaq_midi_input_trigger(struct snd_rawmidi_substream *subst
 
        if (!dev)
                return;
-       
+
        dev->midi_receive_substream = up ? substream : NULL;
 }
 
@@ -64,18 +64,18 @@ static void snd_usb_caiaq_midi_send(struct snd_usb_caiaqdev *dev,
                                    struct snd_rawmidi_substream *substream)
 {
        int len, ret;
-       
+
        dev->midi_out_buf[0] = EP1_CMD_MIDI_WRITE;
        dev->midi_out_buf[1] = 0; /* port */
        len = snd_rawmidi_transmit(substream, dev->midi_out_buf + 3,
                                   EP1_BUFSIZE - 3);
-       
+
        if (len <= 0)
                return;
-       
+
        dev->midi_out_buf[2] = len;
        dev->midi_out_urb.transfer_buffer_length = len+3;
-       
+
        ret = usb_submit_urb(&dev->midi_out_urb, GFP_ATOMIC);
        if (ret < 0)
                log("snd_usb_caiaq_midi_send(%p): usb_submit_urb() failed,"
@@ -88,7 +88,7 @@ static void snd_usb_caiaq_midi_send(struct snd_usb_caiaqdev *dev,
 static void snd_usb_caiaq_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
 {
        struct snd_usb_caiaqdev *dev = substream->rmidi->private_data;
-       
+
        if (up) {
                dev->midi_out_substream = substream;
                if (!dev->midi_out_active)
@@ -113,12 +113,12 @@ static struct snd_rawmidi_ops snd_usb_caiaq_midi_input =
        .trigger =      snd_usb_caiaq_midi_input_trigger,
 };
 
-void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *dev, 
+void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *dev,
                                     int port, const char *buf, int len)
 {
        if (!dev->midi_receive_substream)
                return;
-       
+
        snd_rawmidi_receive(dev->midi_receive_substream, buf, len);
 }
 
@@ -142,16 +142,16 @@ int snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *device)
 
        if (device->spec.num_midi_out > 0) {
                rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
-               snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 
+               snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
                                    &snd_usb_caiaq_midi_output);
        }
 
        if (device->spec.num_midi_in > 0) {
                rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
-               snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, 
+               snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
                                    &snd_usb_caiaq_midi_input);
        }
-       
+
        device->rmidi = rmidi;
 
        return 0;
@@ -160,7 +160,7 @@ int snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *device)
 void snd_usb_caiaq_midi_output_done(struct urb* urb)
 {
        struct snd_usb_caiaqdev *dev = urb->context;
-       
+
        dev->midi_out_active = 0;
        if (urb->status != 0)
                return;
index a6b88482637be9fbdc9b3c0fa56e2293a73ce0ab..c7b902358b7b268a3dd25178639f41009dedf125 100644 (file)
@@ -627,6 +627,7 @@ static int prepare_playback_urb(struct snd_usb_substream *subs,
        subs->hwptr_done += offs;
        if (subs->hwptr_done >= runtime->buffer_size)
                subs->hwptr_done -= runtime->buffer_size;
+       runtime->delay += offs;
        spin_unlock_irqrestore(&subs->lock, flags);
        urb->transfer_buffer_length = offs * stride;
        if (period_elapsed)
@@ -636,12 +637,22 @@ static int prepare_playback_urb(struct snd_usb_substream *subs,
 
 /*
  * process after playback data complete
- * - nothing to do
+ * - decrease the delay count again
  */
 static int retire_playback_urb(struct snd_usb_substream *subs,
                               struct snd_pcm_runtime *runtime,
                               struct urb *urb)
 {
+       unsigned long flags;
+       int stride = runtime->frame_bits >> 3;
+       int processed = urb->transfer_buffer_length / stride;
+
+       spin_lock_irqsave(&subs->lock, flags);
+       if (processed > runtime->delay)
+               runtime->delay = 0;
+       else
+               runtime->delay -= processed;
+       spin_unlock_irqrestore(&subs->lock, flags);
        return 0;
 }
 
@@ -1520,6 +1531,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
        subs->hwptr_done = 0;
        subs->transfer_done = 0;
        subs->phase = 0;
+       runtime->delay = 0;
 
        /* clear urbs (to be sure) */
        deactivate_urbs(subs, 0, 1);
@@ -3279,6 +3291,25 @@ static int snd_usb_cm106_boot_quirk(struct usb_device *dev)
        return snd_usb_cm106_write_int_reg(dev, 2, 0x8004);
 }
 
+/*
+ * C-Media CM6206 is based on CM106 with two additional
+ * registers that are not documented in the data sheet.
+ * Values here are chosen based on sniffing USB traffic
+ * under Windows.
+ */
+static int snd_usb_cm6206_boot_quirk(struct usb_device *dev)
+{
+       int err, reg;
+       int val[] = {0x200c, 0x3000, 0xf800, 0x143f, 0x0000, 0x3000};
+
+       for (reg = 0; reg < ARRAY_SIZE(val); reg++) {
+               err = snd_usb_cm106_write_int_reg(dev, reg, val[reg]);
+               if (err < 0)
+                       return err;
+       }
+
+       return err;
+}
 
 /*
  * Setup quirks
@@ -3565,6 +3596,12 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
                        goto __err_val;
        }
 
+       /* C-Media CM6206 / CM106-Like Sound Device */
+       if (id == USB_ID(0x0d8c, 0x0102)) {
+               if (snd_usb_cm6206_boot_quirk(dev) < 0)
+                       goto __err_val;
+       }
+
        /*
         * found a config.  now register to ALSA
         */
index 5d955aaad85f9a5d2ac96a990e97972a00e48905..f0f7624f91781efd574a883b78da048b5f9dbafb 100644 (file)
@@ -1469,6 +1469,41 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                }
        }
 },
+{
+       /* Edirol M-16DX */
+       /* FIXME: This quirk gives a good-working capture stream but the
+        *        playback seems problematic because of lacking of sync
+        *        with capture stream.  It needs to sync with the capture
+        *        clock.  As now, you'll get frequent sound distortions
+        *        via the playback.
+        */
+       USB_DEVICE(0x0582, 0x00c4),
+       .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_COMPOSITE,
+               .data = (const struct snd_usb_audio_quirk[]) {
+                       {
+                               .ifnum = 0,
+                               .type = QUIRK_AUDIO_STANDARD_INTERFACE
+                       },
+                       {
+                               .ifnum = 1,
+                               .type = QUIRK_AUDIO_STANDARD_INTERFACE
+                       },
+                       {
+                               .ifnum = 2,
+                               .type = QUIRK_MIDI_FIXED_ENDPOINT,
+                               .data = & (const struct snd_usb_midi_endpoint_info) {
+                                       .out_cables = 0x0001,
+                                       .in_cables  = 0x0001
+                               }
+                       },
+                       {
+                               .ifnum = -1
+                       }
+               }
+       }
+},
 {
        /* BOSS GT-10 */
        USB_DEVICE(0x0582, 0x00da),
@@ -1950,6 +1985,14 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                .type = QUIRK_MIDI_STANDARD_INTERFACE
        }
 },
+{
+       USB_DEVICE(0x0ccd, 0x0028),
+       .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+               .vendor_name = "TerraTec",
+               .product_name = "Aureon 5.1 MkII",
+               .ifnum = QUIRK_NO_INTERFACE
+       }
+},
 {
        USB_DEVICE(0x0ccd, 0x0035),
        .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
index 29259e74dcfa7b4b9d89202ed0987d2312b0eec5..0f5771f615dae24cd9a4073eb9b5ee95419f1124 100644 (file)
@@ -568,8 +568,11 @@ int cmd_record(int argc, const char **argv, const char *prefix)
        if (!argc && target_pid == -1 && !system_wide)
                usage_with_options(record_usage, options);
 
-       if (!nr_counters)
-               nr_counters = 1;
+       if (!nr_counters) {
+               nr_counters     = 1;
+               attrs[0].type   = PERF_TYPE_HARDWARE;
+               attrs[0].config = PERF_COUNT_HW_CPU_CYCLES;
+       }
 
        for (counter = 0; counter < nr_counters; counter++) {
                if (attrs[counter].sample_period)
index 860e116d979c834e0bdd68825a4cb83b8adf25da..f71e0d245cbada875af754e3cbae74a418fabbce 100644 (file)
@@ -440,3 +440,18 @@ by this process or by another, and doesn't affect any counters that
 this process has created on other processes.  It only enables or
 disables the group leaders, not any other members in the groups.
 
+
+Arch requirements
+-----------------
+
+If your architecture does not have hardware performance metrics, you can
+still use the generic software counters based on hrtimers for sampling.
+
+So to start with, in order to add HAVE_PERF_COUNTERS to your Kconfig, you
+will need at least this:
+       - asm/perf_counter.h - a basic stub will suffice at first
+       - support for atomic64 types (and associated helper functions)
+       - set_perf_counter_pending() implemented
+
+If your architecture does have hardware capabilities, you can override the
+weak stub hw_perf_counter_init() to register hardware counters.
index af0a5046d743a22b718b0360b392cc4107fdc6ef..87a1aca4a4246e0dad3ea82276ccd5487cddacf0 100644 (file)
@@ -53,11 +53,12 @@ static inline unsigned long long rdclock(void)
        _min1 < _min2 ? _min1 : _min2; })
 
 static inline int
-sys_perf_counter_open(struct perf_counter_attr *attr_uptr,
+sys_perf_counter_open(struct perf_counter_attr *attr,
                      pid_t pid, int cpu, int group_fd,
                      unsigned long flags)
 {
-       return syscall(__NR_perf_counter_open, attr_uptr, pid, cpu,
+       attr->size = sizeof(*attr);
+       return syscall(__NR_perf_counter_open, attr, pid, cpu,
                       group_fd, flags);
 }
 
index 9d5f1ca50e6fe4fc8466f3d97f8051a1e39253f2..5a72586e1df04dfcc7295ba1eb122a5e0d58f8de 100644 (file)
@@ -75,7 +75,7 @@ static char *sw_event_names[] = {
 #define MAX_ALIASES 8
 
 static char *hw_cache [][MAX_ALIASES] = {
-       { "L1-data"             , "l1-d", "l1d", "l1"                           },
+       { "L1-data"             , "l1-d", "l1d"                                 },
        { "L1-instruction"      , "l1-i", "l1i"                                 },
        { "L2"                  , "l2"                                          },
        { "Data-TLB"            , "dtlb", "d-tlb"                               },